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/Android.bp b/profman/Android.bp
index a327ef2..2a45c46 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -19,6 +19,7 @@
host_supported: true,
defaults: ["art_defaults"],
srcs: [
+ "boot_image_profile.cc",
"profman.cc",
"profile_assistant.cc",
],
diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc
new file mode 100644
index 0000000..21de083
--- /dev/null
+++ b/profman/boot_image_profile.cc
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <set>
+
+#include "boot_image_profile.h"
+#include "dex_file-inl.h"
+#include "method_reference.h"
+#include "type_reference.h"
+
+namespace art {
+
+using Hotness = ProfileCompilationInfo::MethodHotness;
+
+void GenerateBootImageProfile(
+ const std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ const std::vector<std::unique_ptr<const ProfileCompilationInfo>>& profiles,
+ const BootImageOptions& options,
+ bool verbose,
+ ProfileCompilationInfo* out_profile) {
+ for (const std::unique_ptr<const ProfileCompilationInfo>& profile : profiles) {
+ // Avoid merging classes since we may want to only add classes that fit a certain criteria.
+ // If we merged the classes, every single class in each profile would be in the out_profile,
+ // but we want to only included classes that are in at least a few profiles.
+ out_profile->MergeWith(*profile, /*merge_classes*/ false);
+ }
+
+ // Image classes that were added because they are commonly used.
+ size_t class_count = 0;
+ // Image classes that were only added because they were clean.
+ size_t clean_class_count = 0;
+ // Total clean classes.
+ size_t clean_count = 0;
+ // Total dirty classes.
+ size_t dirty_count = 0;
+
+ for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ // Inferred classes are classes inferred from method samples.
+ std::set<std::pair<const ProfileCompilationInfo*, dex::TypeIndex>> inferred_classes;
+ for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
+ MethodReference ref(dex_file.get(), i);
+ // This counter is how many profiles contain the method as sampled or hot.
+ size_t counter = 0;
+ for (const std::unique_ptr<const ProfileCompilationInfo>& profile : profiles) {
+ Hotness hotness = profile->GetMethodHotness(ref);
+ if (hotness.IsInProfile()) {
+ ++counter;
+ out_profile->AddMethodHotness(ref, hotness);
+ inferred_classes.emplace(profile.get(),
+ dex_file->GetMethodId(ref.dex_method_index).class_idx_);
+ }
+ }
+ // If the counter is greater or equal to the compile threshold, mark the method as hot.
+ // Note that all hot methods are also marked as hot in the out profile during the merging
+ // process.
+ if (counter >= options.compiled_method_threshold) {
+ Hotness hotness;
+ hotness.AddFlag(Hotness::kFlagHot);
+ out_profile->AddMethodHotness(ref, hotness);
+ }
+ }
+ // Walk all of the classes and add them to the profile if they meet the requirements.
+ for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+ TypeReference ref(dex_file.get(), class_def.class_idx_);
+ bool is_clean = true;
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+ if (class_data != nullptr) {
+ ClassDataItemIterator it(*dex_file, class_data);
+ while (it.HasNextStaticField()) {
+ const uint32_t flags = it.GetFieldAccessFlags();
+ if ((flags & kAccFinal) == 0) {
+ // Not final static field will probably dirty the class.
+ is_clean = false;
+ break;
+ }
+ it.Next();
+ }
+ it.SkipInstanceFields();
+ while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) {
+ const uint32_t flags = it.GetMethodAccessFlags();
+ if ((flags & kAccNative) != 0 || (flags & kAccFastNative) != 0) {
+ // Native method will get dirtied.
+ is_clean = false;
+ break;
+ }
+ if ((flags & kAccConstructor) != 0 && (flags & kAccStatic) != 0) {
+ // Class initializer, may get dirtied (not sure).
+ is_clean = false;
+ break;
+ }
+ it.Next();
+ }
+ }
+ ++(is_clean ? clean_count : dirty_count);
+ // This counter is how many profiles contain the class.
+ size_t counter = 0;
+ for (const std::unique_ptr<const ProfileCompilationInfo>& profile : profiles) {
+ auto it = inferred_classes.find(std::make_pair(profile.get(), ref.type_index));
+ if (it != inferred_classes.end() ||
+ profile->ContainsClass(*ref.dex_file, ref.type_index)) {
+ ++counter;
+ }
+ }
+ if (counter == 0) {
+ continue;
+ }
+ if (counter >= options.image_class_theshold) {
+ ++class_count;
+ out_profile->AddClassesForDex(ref.dex_file, &ref.type_index, &ref.type_index + 1);
+ } else if (is_clean && counter >= options.image_class_clean_theshold) {
+ ++clean_class_count;
+ out_profile->AddClassesForDex(ref.dex_file, &ref.type_index, &ref.type_index + 1);
+ }
+ }
+ }
+ if (verbose) {
+ LOG(INFO) << "Image classes " << class_count + clean_class_count
+ << " added because clean " << clean_class_count
+ << " total clean " << clean_count << " total dirty " << dirty_count;
+ }
+}
+
+} // namespace art
diff --git a/profman/boot_image_profile.h b/profman/boot_image_profile.h
new file mode 100644
index 0000000..d02e408
--- /dev/null
+++ b/profman/boot_image_profile.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_PROFMAN_BOOT_IMAGE_PROFILE_H_
+#define ART_PROFMAN_BOOT_IMAGE_PROFILE_H_
+
+#include <limits>
+#include <memory>
+#include <vector>
+
+#include "dex_file.h"
+#include "jit/profile_compilation_info.h"
+
+namespace art {
+
+struct BootImageOptions {
+ public:
+ // Threshold for classes that may be dirty or clean. The threshold specifies how
+ // many different profiles need to have the class before it gets added to the boot profile.
+ uint32_t image_class_theshold = 10;
+
+ // Threshold for classes that are likely to remain clean. The threshold specifies how
+ // many different profiles need to have the class before it gets added to the boot profile.
+ uint32_t image_class_clean_theshold = 3;
+
+ // Threshold for non-hot methods to be compiled. The threshold specifies how
+ // many different profiles need to have the method before it gets added to the boot profile.
+ uint32_t compiled_method_threshold = std::numeric_limits<uint32_t>::max();
+};
+
+// Merge a bunch of profiles together to generate a boot profile. Classes and methods are added
+// to the out_profile if they meet the options.
+void GenerateBootImageProfile(
+ const std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ const std::vector<std::unique_ptr<const ProfileCompilationInfo>>& profiles,
+ const BootImageOptions& options,
+ bool verbose,
+ ProfileCompilationInfo* out_profile);
+
+} // namespace art
+
+#endif // ART_PROFMAN_BOOT_IMAGE_PROFILE_H_
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index c6b06af..75f8ec9 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -621,6 +621,100 @@
EXPECT_GT(method_count, 0u);
}
+TEST_F(ProfileAssistantTest, TestBootImageProfile) {
+ const std::string core_dex = GetLibCoreDexFileNames()[0];
+
+ std::vector<ScratchFile> profiles;
+
+ // In image with enough clean occurrences.
+ const std::string kCleanClass = "Ljava/lang/CharSequence;";
+ // In image with enough dirty occurrences.
+ const std::string kDirtyClass = "Ljava/lang/Object;";
+ // Not in image becauseof not enough occurrences.
+ const std::string kUncommonCleanClass = "Ljava/lang/Process;";
+ const std::string kUncommonDirtyClass = "Ljava/lang/Package;";
+ // Method that is hot.
+ // Also adds the class through inference since it is in each dex.
+ const std::string kHotMethod = "Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I";
+ // Method that doesn't add the class since its only in one profile. Should still show up in the
+ // boot profile.
+ const std::string kOtherMethod = "Ljava/util/HashMap;-><init>()V";
+
+ // Thresholds for this test.
+ static const size_t kDirtyThreshold = 3;
+ static const size_t kCleanThreshold = 2;
+
+ // Create a bunch of boot profiles.
+ std::string dex1 =
+ kCleanClass + "\n" +
+ kDirtyClass + "\n" +
+ kUncommonCleanClass + "\n" +
+ "H" + kHotMethod + "\n" +
+ kUncommonDirtyClass;
+ profiles.emplace_back(ScratchFile());
+ EXPECT_TRUE(CreateProfile(dex1, profiles.back().GetFilename(), core_dex));
+
+ // Create a bunch of boot profiles.
+ std::string dex2 =
+ kCleanClass + "\n" +
+ kDirtyClass + "\n" +
+ "P" + kHotMethod + "\n" +
+ kUncommonDirtyClass;
+ profiles.emplace_back(ScratchFile());
+ EXPECT_TRUE(CreateProfile(dex2, profiles.back().GetFilename(), core_dex));
+
+ // Create a bunch of boot profiles.
+ std::string dex3 =
+ "S" + kHotMethod + "\n" +
+ "P" + kOtherMethod + "\n" +
+ kDirtyClass + "\n";
+ profiles.emplace_back(ScratchFile());
+ EXPECT_TRUE(CreateProfile(dex3, profiles.back().GetFilename(), core_dex));
+
+ // Generate the boot profile.
+ ScratchFile out_profile;
+ std::vector<std::string> args;
+ args.push_back(GetProfmanCmd());
+ args.push_back("--generate-boot-image-profile");
+ args.push_back("--boot-image-class-threshold=" + std::to_string(kDirtyThreshold));
+ args.push_back("--boot-image-clean-class-threshold=" + std::to_string(kCleanThreshold));
+ args.push_back("--reference-profile-file=" + out_profile.GetFilename());
+ args.push_back("--apk=" + core_dex);
+ args.push_back("--dex-location=" + core_dex);
+ for (const ScratchFile& profile : profiles) {
+ args.push_back("--profile-file=" + profile.GetFilename());
+ }
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(args, &error), 0) << error;
+ ASSERT_EQ(0, out_profile.GetFile()->Flush());
+ ASSERT_TRUE(out_profile.GetFile()->ResetOffset());
+
+ // Verify the boot profile contents.
+ std::string output_file_contents;
+ EXPECT_TRUE(DumpClassesAndMethods(out_profile.GetFilename(), &output_file_contents));
+ // Common classes, should be in the classes of the profile.
+ EXPECT_NE(output_file_contents.find(kCleanClass + "\n"), std::string::npos)
+ << output_file_contents;
+ EXPECT_NE(output_file_contents.find(kDirtyClass + "\n"), std::string::npos)
+ << output_file_contents;
+ // Uncommon classes, should not fit preloaded class criteria and should not be in the profile.
+ EXPECT_EQ(output_file_contents.find(kUncommonCleanClass + "\n"), std::string::npos)
+ << output_file_contents;
+ EXPECT_EQ(output_file_contents.find(kUncommonDirtyClass + "\n"), std::string::npos)
+ << output_file_contents;
+ // Inferred class from a method common to all three profiles.
+ EXPECT_NE(output_file_contents.find("Ljava/lang/Comparable;\n"), std::string::npos)
+ << output_file_contents;
+ // Aggregated methods hotness information.
+ EXPECT_NE(output_file_contents.find("HSP" + kHotMethod), std::string::npos)
+ << output_file_contents;
+ EXPECT_NE(output_file_contents.find(kOtherMethod), std::string::npos)
+ << output_file_contents;
+ // Not inferred class, method is only in one profile.
+ EXPECT_EQ(output_file_contents.find("Ljava/util/HashMap;\n"), std::string::npos)
+ << output_file_contents;
+}
+
TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) {
// 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 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();
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 960030d..147173c 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -1148,7 +1148,8 @@
return ret;
}
-bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
+bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other,
+ bool merge_classes) {
// First verify that all checksums match. This will avoid adding garbage to
// the current profile info.
// Note that the number of elements should be very small, so this should not
@@ -1194,8 +1195,10 @@
DCHECK(dex_data != nullptr);
// Merge the classes.
- dex_data->class_set.insert(other_dex_data->class_set.begin(),
- other_dex_data->class_set.end());
+ if (merge_classes) {
+ dex_data->class_set.insert(other_dex_data->class_set.begin(),
+ other_dex_data->class_set.end());
+ }
// Merge the methods and the inline caches.
for (const auto& other_method_it : other_dex_data->method_map) {
@@ -1239,6 +1242,18 @@
: MethodHotness();
}
+bool ProfileCompilationInfo::AddMethodHotness(const MethodReference& method_ref,
+ const MethodHotness& hotness) {
+ DexFileData* dex_data = GetOrAddDexFileData(method_ref.dex_file);
+ if (dex_data != nullptr) {
+ // TODO: Add inline caches.
+ dex_data->AddMethod(static_cast<MethodHotness::Flag>(hotness.GetFlags()),
+ method_ref.dex_method_index);
+ return true;
+ }
+ return false;
+}
+
ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
const std::string& dex_location,
uint32_t dex_checksum,
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index f1f2428..079ce8d 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -285,6 +285,9 @@
return true;
}
+ // Add hotness flags for a simple method.
+ bool AddMethodHotness(const MethodReference& method_ref, const MethodHotness& hotness);
+
// Load profile information from the given file descriptor.
// If the current profile is non-empty the load will fail.
bool Load(int fd);
@@ -295,8 +298,10 @@
// the file and returns true.
bool Load(const std::string& filename, bool clear_if_invalid);
- // Merge the data from another ProfileCompilationInfo into the current object.
- bool MergeWith(const ProfileCompilationInfo& info);
+ // Merge the data from another ProfileCompilationInfo into the current object. Only merges
+ // classes if merge_classes is true. This is used for creating the boot profile since
+ // we don't want all of the classes to be image classes.
+ bool MergeWith(const ProfileCompilationInfo& info, bool merge_classes = true);
// Save the profile data to the given file descriptor.
bool Save(int fd);
diff --git a/runtime/type_reference.h b/runtime/type_reference.h
index b7e964b..c44019d 100644
--- a/runtime/type_reference.h
+++ b/runtime/type_reference.h
@@ -37,6 +37,15 @@
dex::TypeIndex type_index;
};
+struct TypeReferenceComparator {
+ bool operator()(TypeReference mr1, TypeReference mr2) const {
+ if (mr1.dex_file != mr2.dex_file) {
+ return mr1.dex_file < mr2.dex_file;
+ }
+ return mr1.type_index < mr2.type_index;
+ }
+};
+
// Compare the actual referenced type names. Used for type reference deduplication.
struct TypeReferenceValueComparator {
bool operator()(TypeReference tr1, TypeReference tr2) const {