diff options
| -rw-r--r-- | profman/profile_assistant_test.cc | 34 | ||||
| -rw-r--r-- | profman/profman.cc | 68 | ||||
| -rw-r--r-- | runtime/jit/offline_profiling_info.cc | 46 | ||||
| -rw-r--r-- | runtime/jit/offline_profiling_info.h | 5 |
4 files changed, 147 insertions, 6 deletions
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 462c39735e..cd0aa6fd7d 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -61,17 +61,21 @@ class ProfileAssistantTest : public CommonRuntimeTest { ASSERT_TRUE(file_info.Equals(info)); } - // Runs test with given arguments. - int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) { + std::string GetProfmanCmd() { std::string file_path = GetTestAndroidRoot(); file_path += "/bin/profman"; if (kIsDebugBuild) { file_path += "d"; } - - EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path"; + EXPECT_TRUE(OS::FileExists(file_path.c_str())) + << file_path << " should be a valid file path"; + return file_path; + } + // Runs test with given arguments. + int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) { + std::string profman_cmd = GetProfmanCmd(); std::vector<std::string> argv_str; - argv_str.push_back(file_path); + argv_str.push_back(profman_cmd); for (size_t k = 0; k < profiles_fd.size(); k++) { argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k])); } @@ -80,6 +84,15 @@ class ProfileAssistantTest : public CommonRuntimeTest { std::string error; return ExecAndReturnCode(argv_str, &error); } + + bool GenerateTestProfile(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); + std::string error; + return ExecAndReturnCode(argv_str, &error); + } }; TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { @@ -282,4 +295,15 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { CheckProfileInfo(profile1, info1); } +TEST_F(ProfileAssistantTest, TestProfileGeneration) { + ScratchFile profile; + // Generate a test profile. + GenerateTestProfile(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))); +} + } // namespace art diff --git a/profman/profman.cc b/profman/profman.cc index d2c9cb2e3f..a5fefa71d4 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -100,6 +100,14 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); UsageError(" accepts a file descriptor. Cannot be used together with"); UsageError(" --reference-profile-file."); + UsageError(" --generate-test-profile=<filename>: generates a random profile file for testing."); + UsageError(" --generate-test-profile-num-dex=<number>: number of dex files that should be"); + UsageError(" included in the generated profile. Defaults to 20."); + UsageError(" --generate-test-profile-method-ratio=<number>: the percentage from the maximum"); + 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(""); UsageError(""); UsageError(" --dex-location=<string>: location string to use with corresponding"); UsageError(" apk-fd to find dex files"); @@ -111,12 +119,20 @@ NO_RETURN static void Usage(const char *fmt, ...) { exit(EXIT_FAILURE); } +// Note: make sure you update the Usage if you change these values. +static constexpr uint16_t kDefaultTestProfileNumDex = 20; +static constexpr uint16_t kDefaultTestProfileMethodRatio = 5; +static constexpr uint16_t kDefaultTestProfileClassRatio = 5; + class ProfMan FINAL { public: ProfMan() : reference_profile_file_fd_(kInvalidFd), dump_only_(false), dump_output_to_fd_(kInvalidFd), + test_profile_num_dex_(kDefaultTestProfileNumDex), + test_profile_method_ratio_(kDefaultTestProfileMethodRatio), + test_profile_class_ratio_(kDefaultTestProfileClassRatio), start_ns_(NanoTime()) {} ~ProfMan() { @@ -159,6 +175,23 @@ class ProfMan FINAL { dex_locations_.push_back(option.substr(strlen("--dex-location=")).ToString()); } else if (option.starts_with("--apk-fd=")) { ParseFdForCollection(option, "--apk-fd", &apks_fd_); + } else if (option.starts_with("--generate-test-profile=")) { + test_profile_ = option.substr(strlen("--generate-test-profile=")).ToString(); + } else if (option.starts_with("--generate-test-profile-num-dex=")) { + ParseUintOption(option, + "--generate-test-profile-num-dex", + &test_profile_num_dex_, + Usage); + } else if (option.starts_with("--generate-test-profile-method-ratio")) { + ParseUintOption(option, + "--generate-test-profile-method-ratio", + &test_profile_method_ratio_, + Usage); + } else if (option.starts_with("--generate-test-profile-class-ratio")) { + ParseUintOption(option, + "--generate-test-profile-class-ratio", + &test_profile_class_ratio_, + Usage); } else { Usage("Unknown argument '%s'", option.data()); } @@ -168,6 +201,15 @@ class ProfMan FINAL { bool has_reference_profile = !reference_profile_file_.empty() || FdIsValid(reference_profile_file_fd_); + if (!test_profile_.empty()) { + if (test_profile_method_ratio_ > 100) { + Usage("Invalid ratio for --generate-test-profile-method-ratio"); + } + if (test_profile_class_ratio_ > 100) { + Usage("Invalid ratio for --generate-test-profile-class-ratio"); + } + return; + } // --dump-only may be specified with only --reference-profiles present. if (!dump_only_ && !has_profiles) { Usage("No profile files specified."); @@ -317,6 +359,25 @@ class ProfMan FINAL { return dump_only_; } + int GenerateTestProfile() { + int profile_test_fd = open(test_profile_.c_str(), O_CREAT | O_TRUNC | O_WRONLY); + if (profile_test_fd < 0) { + std::cerr << "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_); + close(profile_test_fd); // ignore close result. + return result ? 0 : -1; + } + + bool ShouldGenerateTestProfile() { + return !test_profile_.empty(); + } + private: static void ParseFdForCollection(const StringPiece& option, const char* arg_name, @@ -350,6 +411,10 @@ class ProfMan FINAL { int reference_profile_file_fd_; bool dump_only_; int dump_output_to_fd_; + std::string test_profile_; + uint16_t test_profile_num_dex_; + uint16_t test_profile_method_ratio_; + uint16_t test_profile_class_ratio_; uint64_t start_ns_; }; @@ -360,6 +425,9 @@ static int profman(int argc, char** argv) { // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. profman.ParseArgs(argc, argv); + if (profman.ShouldGenerateTestProfile()) { + return profman.GenerateTestProfile(); + } if (profman.ShouldOnlyDumpProfile()) { return profman.DumpProfileInfo(); } diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index 7a6d1a891f..5039d2de07 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -19,6 +19,7 @@ #include "errno.h" #include <limits.h> #include <vector> +#include <stdlib.h> #include <sys/file.h> #include <sys/stat.h> #include <sys/uio.h> @@ -41,7 +42,7 @@ const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '1', '\0' static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; // Debug flag to ignore checksums when testing if a method or a class is present in the profile. -// Use to make facilitate testing profile guided compilation across a large number of apps +// Used to facilitate testing profile guided compilation across a large number of apps // using the same test profile. static constexpr bool kDebugIgnoreChecksum = false; @@ -668,4 +669,47 @@ void ProfileCompilationInfo::ClearResolvedClasses() { } } +// Naive implementation to generate a random profile file suitable for testing. +bool ProfileCompilationInfo::GenerateTestProfile(int fd, + uint16_t number_of_dex_files, + uint16_t method_ratio, + uint16_t class_ratio) { + const std::string base_dex_location = "base.apk"; + ProfileCompilationInfo info; + // The limits are defined by the dex specification. + uint16_t max_method = std::numeric_limits<uint16_t>::max(); + uint16_t max_classes = std::numeric_limits<uint16_t>::max(); + uint16_t number_of_methods = max_method * method_ratio / 100; + uint16_t number_of_classes = max_classes * class_ratio / 100; + + srand(MicroTime()); + + // 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. + const uint16_t kFavorFirstN = 10000; + const uint16_t kFavorSplit = 2; + + for (uint16_t i = 0; i < number_of_dex_files; i++) { + std::string dex_location = DexFile::GetMultiDexLocation(i, base_dex_location.c_str()); + std::string profile_key = GetProfileDexFileKey(dex_location); + + for (uint16_t m = 0; m < number_of_methods; m++) { + uint16_t method_idx = rand() % max_method; + if (m < (number_of_methods / kFavorSplit)) { + method_idx %= kFavorFirstN; + } + info.AddMethodIndex(profile_key, 0, method_idx); + } + + for (uint16_t c = 0; c < number_of_classes; c++) { + uint16_t class_idx = rand() % max_classes; + if (c < (number_of_classes / kFavorSplit)) { + class_idx %= kFavorFirstN; + } + info.AddClassIndex(profile_key, 0, class_idx); + } + } + return info.Save(fd); +} + } // namespace art diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index 5a07da79a1..0b26f9bd0c 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -87,6 +87,11 @@ class ProfileCompilationInfo { // Clears the resolved classes from the current object. void ClearResolvedClasses(); + static bool GenerateTestProfile(int fd, + uint16_t number_of_dex_files, + uint16_t method_ratio, + uint16_t class_ratio); + private: enum ProfileLoadSatus { kProfileLoadIOError, |