Merge "Add ability to generate a random test profile"
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 462c397..cd0aa6f 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -61,17 +61,21 @@
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 @@
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 @@
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 d2c9cb2..a5fefa7 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -100,6 +100,14 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 7a6d1a8..5039d2d 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 @@
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 @@
}
}
+// 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 5a07da7..0b26f9b 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -87,6 +87,11 @@
// 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,