Add support for generating boot image profile
Added three options:
--generate-boot-image-profile:
If this option is passed in, profman creates a boot image profile.
--boot-image-clean-class-threshold=<value>
Specifies how many occurrences of a likely clean class are required
before a class is added to the profile.
--boot-image-class-threshold=<value>
Specify how many occurrences of a possibly dirty class are required
before a class is added to the profile.
Added unit test.
Test: test-art-host
Bug: 37966211
Change-Id: I8e12b0ec34dfa1d1bed0b51f342fffde09815348
diff --git a/profman/profman.cc b/profman/profman.cc
index f763b8e..14b0262 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -36,6 +36,7 @@
#include "base/stringpiece.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
+#include "boot_image_profile.h"
#include "bytecode_utils.h"
#include "dex_file.h"
#include "jit/profile_compilation_info.h"
@@ -133,6 +134,15 @@
UsageError(" search for dex files");
UsageError(" --apk-=<filename>: an APK to search for dex files");
UsageError("");
+ UsageError(" --generate-boot-image-profile: Generate a boot image profile based on input");
+ UsageError(" profiles. Requires passing in dex files to inspect properties of classes.");
+ UsageError(" --boot-image-class-threshold=<value>: specify minimum number of class occurrences");
+ UsageError(" to include a class in the boot image profile. Default is 10.");
+ UsageError(" --boot-image-clean-class-threshold=<value>: specify minimum number of clean class");
+ UsageError(" occurrences to include a class in the boot image profile. A clean class is a");
+ UsageError(" class that doesn't have any static fields or native methods and is likely to");
+ UsageError(" remain clean in the image. Default is 3.");
+ UsageError("");
exit(EXIT_FAILURE);
}
@@ -163,6 +173,7 @@
reference_profile_file_fd_(kInvalidFd),
dump_only_(false),
dump_classes_and_methods_(false),
+ generate_boot_image_profile_(false),
dump_output_to_fd_(kInvalidFd),
test_profile_num_dex_(kDefaultTestProfileNumDex),
test_profile_method_ratio_(kDefaultTestProfileMethodRatio),
@@ -202,6 +213,18 @@
create_profile_from_file_ = option.substr(strlen("--create-profile-from=")).ToString();
} else if (option.starts_with("--dump-output-to-fd=")) {
ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage);
+ } else if (option == "--generate-boot-image-profile") {
+ generate_boot_image_profile_ = true;
+ } else if (option.starts_with("--boot-image-class-threshold=")) {
+ ParseUintOption(option,
+ "--boot-image-class-threshold",
+ &boot_image_options_.image_class_theshold,
+ Usage);
+ } else if (option.starts_with("--boot-image-clean-class-threshold=")) {
+ ParseUintOption(option,
+ "--boot-image-clean-class-threshold",
+ &boot_image_options_.image_class_clean_theshold,
+ Usage);
} else if (option.starts_with("--profile-file=")) {
profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
} else if (option.starts_with("--profile-file-fd=")) {
@@ -323,28 +346,33 @@
}
}
+ std::unique_ptr<const ProfileCompilationInfo> LoadProfile(const std::string& filename, int fd) {
+ if (!filename.empty()) {
+ fd = open(filename.c_str(), O_RDWR);
+ if (fd < 0) {
+ LOG(ERROR) << "Cannot open " << filename << strerror(errno);
+ return nullptr;
+ }
+ }
+ std::unique_ptr<ProfileCompilationInfo> info(new ProfileCompilationInfo);
+ if (!info->Load(fd)) {
+ LOG(ERROR) << "Cannot load profile info from fd=" << fd << "\n";
+ return nullptr;
+ }
+ return info;
+ }
+
int DumpOneProfile(const std::string& banner,
const std::string& filename,
int fd,
const std::vector<std::unique_ptr<const DexFile>>* dex_files,
std::string* dump) {
- if (!filename.empty()) {
- fd = open(filename.c_str(), O_RDWR);
- if (fd < 0) {
- LOG(ERROR) << "Cannot open " << filename << strerror(errno);
- return -1;
- }
- }
- ProfileCompilationInfo info;
- if (!info.Load(fd)) {
- LOG(ERROR) << "Cannot load profile info from fd=" << fd << "\n";
+ std::unique_ptr<const ProfileCompilationInfo> info(LoadProfile(filename, fd));
+ if (info == nullptr) {
+ LOG(ERROR) << "Cannot load profile info from filename=" << filename << " fd=" << fd;
return -1;
}
- std::string this_dump = banner + "\n" + info.DumpInfo(dex_files) + "\n";
- *dump += this_dump;
- if (close(fd) < 0) {
- PLOG(WARNING) << "Failed to close descriptor";
- }
+ *dump += banner + "\n" + info->DumpInfo(dex_files) + "\n";
return 0;
}
@@ -854,6 +882,19 @@
return true;
}
+ int OpenReferenceProfile() const {
+ int fd = reference_profile_file_fd_;
+ if (!FdIsValid(fd)) {
+ CHECK(!reference_profile_file_.empty());
+ fd = open(reference_profile_file_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
+ if (fd < 0) {
+ LOG(ERROR) << "Cannot open " << reference_profile_file_ << strerror(errno);
+ return kInvalidFd;
+ }
+ }
+ return fd;
+ }
+
// Creates a profile from a human friendly textual representation.
// The expected input format is:
// # Classes
@@ -881,14 +922,9 @@
// for ZipArchive::OpenFromFd
MemMap::Init();
// Open the profile output file if needed.
- int fd = reference_profile_file_fd_;
+ int fd = OpenReferenceProfile();
if (!FdIsValid(fd)) {
- CHECK(!reference_profile_file_.empty());
- fd = open(reference_profile_file_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
- if (fd < 0) {
- LOG(ERROR) << "Cannot open " << reference_profile_file_ << strerror(errno);
return -1;
- }
}
// Read the user-specified list of classes and methods.
std::unique_ptr<std::unordered_set<std::string>>
@@ -914,6 +950,57 @@
return 0;
}
+ bool ShouldCreateBootProfile() const {
+ return generate_boot_image_profile_;
+ }
+
+ int CreateBootProfile() {
+ // Initialize memmap since it's required to open dex files.
+ MemMap::Init();
+ // Open the profile output file.
+ const int reference_fd = OpenReferenceProfile();
+ if (!FdIsValid(reference_fd)) {
+ PLOG(ERROR) << "Error opening reference profile";
+ return -1;
+ }
+ // Open the dex files.
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ OpenApkFilesFromLocations(&dex_files);
+ if (dex_files.empty()) {
+ PLOG(ERROR) << "Expected dex files for creating boot profile";
+ return -2;
+ }
+ // Open the input profiles.
+ std::vector<std::unique_ptr<const ProfileCompilationInfo>> profiles;
+ if (!profile_files_fd_.empty()) {
+ for (int profile_file_fd : profile_files_fd_) {
+ std::unique_ptr<const ProfileCompilationInfo> profile(LoadProfile("", profile_file_fd));
+ if (profile == nullptr) {
+ return -3;
+ }
+ profiles.emplace_back(std::move(profile));
+ }
+ }
+ if (!profile_files_.empty()) {
+ for (const std::string& profile_file : profile_files_) {
+ std::unique_ptr<const ProfileCompilationInfo> profile(LoadProfile(profile_file, kInvalidFd));
+ if (profile == nullptr) {
+ return -4;
+ }
+ profiles.emplace_back(std::move(profile));
+ }
+ }
+ ProfileCompilationInfo out_profile;
+ GenerateBootImageProfile(dex_files,
+ profiles,
+ boot_image_options_,
+ VLOG_IS_ON(profiler),
+ &out_profile);
+ out_profile.Save(reference_fd);
+ close(reference_fd);
+ return 0;
+ }
+
bool ShouldCreateProfile() {
return !create_profile_from_file_.empty();
}
@@ -1001,7 +1088,9 @@
int reference_profile_file_fd_;
bool dump_only_;
bool dump_classes_and_methods_;
+ bool generate_boot_image_profile_;
int dump_output_to_fd_;
+ BootImageOptions boot_image_options_;
std::string test_profile_;
std::string create_profile_from_file_;
uint16_t test_profile_num_dex_;
@@ -1030,6 +1119,10 @@
if (profman.ShouldCreateProfile()) {
return profman.CreateProfile();
}
+
+ if (profman.ShouldCreateBootProfile()) {
+ return profman.CreateBootProfile();
+ }
// Process profile information and assess if we need to do a profile guided compilation.
// This operation involves I/O.
return profman.ProcessProfiles();