Add profman option to generate a random profile based on dex files.
Extends --generate-test-profile to make a randomized profile based on a
set of dex files. Will randomly incorporate classes and methods from all
the dex files given. Can be given a random seed for reproducibility.
Bug: 36107940
Test: mm test-art-host-gtest-profile_assistant_test
(cherry-picked from commit f0a31f865c3b3e5843ddd52f015c70293b54b4e4)
Change-Id: I850350f465363773e608ba1562db53c1f306a123
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 44cc33f..38254e2 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -147,6 +147,18 @@
return ExecAndReturnCode(argv_str, &error);
}
+ bool GenerateTestProfileWithInputDex(const std::string& filename) {
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--generate-test-profile=" + filename);
+ argv_str.push_back("--generate-test-profile-seed=0");
+ argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
+ argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
+ std::string error;
+ return ExecAndReturnCode(argv_str, &error);
+ }
+
bool CreateProfile(std::string profile_file_contents,
const std::string& filename,
const std::string& dex_location) {
@@ -473,6 +485,17 @@
ASSERT_TRUE(info.Load(GetFd(profile)));
}
+TEST_F(ProfileAssistantTest, TestProfileGenerationWithIndexDex) {
+ ScratchFile profile;
+ // Generate a test profile passing in a dex file as reference.
+ GenerateTestProfileWithInputDex(profile.GetFilename());
+
+ // Verify that the generated profile is valid and can be loaded.
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ProfileCompilationInfo info;
+ ASSERT_TRUE(info.Load(GetFd(profile)));
+}
+
TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) {
// Class names put here need to be in sorted order.
std::vector<std::string> class_names = {
diff --git a/profman/profman.cc b/profman/profman.cc
index c1bdb4f..cf1ffe1 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -118,6 +118,8 @@
UsageError(" number of methods that should be generated. Defaults to 5.");
UsageError(" --generate-test-profile-class-ratio=<number>: the percentage from the maximum");
UsageError(" number of classes that should be generated. Defaults to 5.");
+ UsageError(" --generate-test-profile-seed=<number>: seed for random number generator used when");
+ UsageError(" generating random test profiles. Defaults to using NanoTime.");
UsageError("");
UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes and");
UsageError(" methods.");
@@ -158,6 +160,7 @@
test_profile_num_dex_(kDefaultTestProfileNumDex),
test_profile_method_ratio_(kDefaultTestProfileMethodRatio),
test_profile_class_ratio_(kDefaultTestProfileClassRatio),
+ test_profile_seed_(NanoTime()),
start_ns_(NanoTime()) {}
~ProfMan() {
@@ -223,6 +226,8 @@
"--generate-test-profile-class-ratio",
&test_profile_class_ratio_,
Usage);
+ } else if (option.starts_with("--generate-test-profile-seed=")) {
+ ParseUintOption(option, "--generate-test-profile-seed", &test_profile_seed_, Usage);
} else {
Usage("Unknown argument '%s'", option.data());
}
@@ -827,17 +832,39 @@
if (test_profile_class_ratio_ > 100) {
Usage("Invalid ratio for --generate-test-profile-class-ratio");
}
+ // If given APK files or DEX locations, check that they're ok.
+ if (!apk_files_.empty() || !apks_fd_.empty() || !dex_locations_.empty()) {
+ if (apk_files_.empty() && apks_fd_.empty()) {
+ Usage("APK files must be specified when passing DEX locations to --generate-test-profile");
+ }
+ if (dex_locations_.empty()) {
+ Usage("DEX locations must be specified when passing APK files to --generate-test-profile");
+ }
+ }
// ShouldGenerateTestProfile confirms !test_profile_.empty().
int profile_test_fd = open(test_profile_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
if (profile_test_fd < 0) {
LOG(ERROR) << "Cannot open " << test_profile_ << strerror(errno);
return -1;
}
-
- bool result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd,
- test_profile_num_dex_,
- test_profile_method_ratio_,
- test_profile_class_ratio_);
+ bool result;
+ if (apk_files_.empty() && apks_fd_.empty() && dex_locations_.empty()) {
+ result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd,
+ test_profile_num_dex_,
+ test_profile_method_ratio_,
+ test_profile_class_ratio_,
+ test_profile_seed_);
+ } else {
+ // Initialize MemMap for ZipArchive::OpenFromFd.
+ MemMap::Init();
+ // Open the dex files to look up classes and methods.
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ OpenApkFilesFromLocations(&dex_files);
+ // Create a random profile file based on the set of dex files.
+ result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd,
+ dex_files,
+ test_profile_seed_);
+ }
close(profile_test_fd); // ignore close result.
return result ? 0 : -1;
}
@@ -886,6 +913,7 @@
uint16_t test_profile_num_dex_;
uint16_t test_profile_method_ratio_;
uint16_t test_profile_class_ratio_;
+ uint32_t test_profile_seed_;
uint64_t start_ns_;
};
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 6fcf971..2d1601e 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -1139,7 +1139,8 @@
bool ProfileCompilationInfo::GenerateTestProfile(int fd,
uint16_t number_of_dex_files,
uint16_t method_ratio,
- uint16_t class_ratio) {
+ uint16_t class_ratio,
+ uint32_t random_seed) {
const std::string base_dex_location = "base.apk";
ProfileCompilationInfo info;
// The limits are defined by the dex specification.
@@ -1148,7 +1149,7 @@
uint16_t number_of_methods = max_method * method_ratio / 100;
uint16_t number_of_classes = max_classes * class_ratio / 100;
- srand(MicroTime());
+ std::srand(random_seed);
// Make sure we generate more samples with a low index value.
// This makes it more likely to hit valid method/class indices in small apps.
@@ -1178,6 +1179,32 @@
return info.Save(fd);
}
+// Naive implementation to generate a random profile file suitable for testing.
+bool ProfileCompilationInfo::GenerateTestProfile(
+ int fd,
+ std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ uint32_t random_seed) {
+ std::srand(random_seed);
+ ProfileCompilationInfo info;
+ for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ const std::string& location = dex_file->GetLocation();
+ uint32_t checksum = dex_file->GetLocationChecksum();
+ for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+ // Randomly add a class from the dex file (with 50% chance).
+ if (std::rand() % 2 != 0) {
+ info.AddClassIndex(location, checksum, dex::TypeIndex(dex_file->GetClassDef(i).class_idx_));
+ }
+ }
+ for (uint32_t i = 0; i < dex_file->NumMethodIds(); ++i) {
+ // Randomly add a method from the dex file (with 50% chance).
+ if (std::rand() % 2 != 0) {
+ info.AddMethodIndex(location, checksum, i);
+ }
+ }
+ }
+ return info.Save(fd);
+}
+
bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
const OfflineProfileMethodInfo& other) const {
if (inline_caches.size() != other.inline_caches.size()) {
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index b8442ea..f68ed5d 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -262,7 +262,14 @@
static bool GenerateTestProfile(int fd,
uint16_t number_of_dex_files,
uint16_t method_ratio,
- uint16_t class_ratio);
+ uint16_t class_ratio,
+ uint32_t random_seed);
+
+ // Generate a test profile which will randomly contain classes and methods from
+ // the provided list of dex files.
+ static bool GenerateTestProfile(int fd,
+ std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ uint32_t random_seed);
// Check that the given profile method info contain the same data.
static bool Equals(const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi1,