summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Mathieu Chartier <mathieuc@google.com> 2017-05-24 12:04:13 -0700
committer Mathieu Chartier <mathieuc@google.com> 2017-06-02 18:25:09 +0000
commitea650f3bc4f54eb2c647cf0f7134398ef5038792 (patch)
treecdddde74424cf9fbeb2ef62e6bebca619c5ed989
parent6559ba1cfb9d24fdca4b1b1ec9e0b8dc31679982 (diff)
Move profiles to use bitmaps for startup methods
This CL enables changes like compiling only hot methods while still doing layout for hot and startup methods. The bitmaps are also a bit smaller for post-launch use cases. No change in compilation strategy yet. Fixed some bugs in dexlayout test like using a profile with the wrong dex location. This meant the second invocation of dexlayout didn't have any profile data. Added profman support for dump-classes-and-methods, create-profile-from, and related test. Profile sizes (bytes) post launch: Gmail: 7290 -> 6136 Maps: 22896 -> 18984 Music: 8582 -> 7050 YouTube: 16733 -> 14592 Test: test-art-host Bug: 62040831 Change-Id: I9915b81a2ff2c47464acbbdeb55ce30a33d5483f
-rw-r--r--compiler/driver/compiler_driver.cc3
-rw-r--r--compiler/driver/compiler_driver_test.cc10
-rw-r--r--dex2oat/dex2oat_test.cc4
-rw-r--r--dexlayout/dexlayout.cc4
-rw-r--r--dexlayout/dexlayout_test.cc20
-rw-r--r--profman/profile_assistant_test.cc36
-rw-r--r--profman/profman.cc97
-rw-r--r--runtime/class_linker.cc3
-rw-r--r--runtime/dex_cache_resolved_classes.h11
-rw-r--r--runtime/jit/profile_compilation_info.cc181
-rw-r--r--runtime/jit/profile_compilation_info.h117
-rw-r--r--runtime/jit/profile_compilation_info_test.cc78
-rw-r--r--runtime/jit/profile_saver.cc55
-rw-r--r--runtime/safe_map.h6
14 files changed, 504 insertions, 121 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index fc5f847354..0097f55e53 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1000,7 +1000,8 @@ bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_r
if (profile_compilation_info_ == nullptr) {
return false;
}
- bool result = profile_compilation_info_->ContainsMethod(method_ref);
+ // TODO: Revisit compiling all startup methods. b/36457259
+ bool result = profile_compilation_info_->IsStartupOrHotMethod(method_ref);
if (kDebugProfileGuidedCompilation) {
LOG(INFO) << "[ProfileGuidedCompilation] "
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 26ea39f205..4b979d8125 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -239,8 +239,14 @@ class CompilerDriverProfileTest : public CompilerDriverTest {
ProfileCompilationInfo info;
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
- profile_info_.AddMethodIndex(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 1);
- profile_info_.AddMethodIndex(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 2);
+ profile_info_.AddMethodIndex(dex_file->GetLocation(),
+ dex_file->GetLocationChecksum(),
+ 1,
+ dex_file->NumMethodIds());
+ profile_info_.AddMethodIndex(dex_file->GetLocation(),
+ dex_file->GetLocationChecksum(),
+ 2,
+ dex_file->NumMethodIds());
}
return &profile_info_;
}
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 40ca875fb6..b604e8b5f1 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -39,6 +39,8 @@
namespace art {
+static constexpr size_t kMaxMethodIds = 65535;
+
using android::base::StringPrintf;
class Dex2oatTest : public Dex2oatEnvironmentTest {
@@ -613,7 +615,7 @@ class Dex2oatLayoutTest : public Dex2oatTest {
ProfileCompilationInfo info;
std::string profile_key = ProfileCompilationInfo::GetProfileDexFileKey(dex_location);
for (size_t i = 0; i < num_classes; ++i) {
- info.AddClassIndex(profile_key, checksum, dex::TypeIndex(1 + i));
+ info.AddClassIndex(profile_key, checksum, dex::TypeIndex(1 + i), kMaxMethodIds);
}
bool result = info.Save(profile_test_fd);
close(profile_test_fd);
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 205c0d1384..db227676c2 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1557,7 +1557,7 @@ void DexLayout::LayoutStringData(const DexFile* dex_file) {
(method->GetAccessFlags() & kAccConstructor) != 0 &&
(method->GetAccessFlags() & kAccStatic) != 0;
const bool method_executed = is_clinit ||
- info_->ContainsMethod(MethodReference(dex_file, method_id->GetIndex()));
+ info_->IsStartupOrHotMethod(MethodReference(dex_file, method_id->GetIndex()));
if (!method_executed) {
continue;
}
@@ -1699,7 +1699,7 @@ int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file,
(method->GetAccessFlags() & kAccConstructor) != 0 &&
(method->GetAccessFlags() & kAccStatic) != 0;
const bool is_method_executed = is_clinit ||
- info_->ContainsMethod(MethodReference(dex_file, method_id->GetIndex()));
+ info_->IsStartupOrHotMethod(MethodReference(dex_file, method_id->GetIndex()));
code_items[is_method_executed
? CodeItemKind::kMethodExecuted
: CodeItemKind::kMethodNotExecuted]
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 1d09a7f72a..6fe8eeb66e 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -341,18 +341,30 @@ class DexLayoutTest : public CommonRuntimeTest {
if ((i & 3) != 0) {
pfi.AddMethodIndex(dex_location,
dex_file->GetLocationChecksum(),
- i);
+ i,
+ dex_file->NumMethodIds());
+ ++profile_methods;
+ } else if ((i & 2) != 0) {
+ pfi.AddSampledMethod(/*startup*/true,
+ dex_location,
+ dex_file->GetLocationChecksum(),
+ i,
+ dex_file->NumMethodIds());
++profile_methods;
}
}
DexCacheResolvedClasses cur_classes(dex_location,
dex_location,
- dex_file->GetLocationChecksum());
+ dex_file->GetLocationChecksum(),
+ dex_file->NumMethodIds());
// Add every even class too.
for (uint32_t i = 0; i < dex_file->NumClassDefs(); i += 1) {
- cur_classes.AddClass(dex_file->GetClassDef(i).class_idx_);
- ++profile_classes;
+ if ((i & 2) == 0) {
+ cur_classes.AddClass(dex_file->GetClassDef(i).class_idx_);
+ ++profile_classes;
+ }
}
+ classes.insert(cur_classes);
}
pfi.AddMethodsAndClasses(pmis, classes);
// Write to provided file.
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 1c328987cb..e87852baf0 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -30,6 +30,8 @@
namespace art {
+static constexpr size_t kMaxMethodIds = 65535;
+
class ProfileAssistantTest : public CommonRuntimeTest {
public:
void PostRuntimeCreate() OVERRIDE {
@@ -56,15 +58,18 @@ class ProfileAssistantTest : public CommonRuntimeTest {
GetOfflineProfileMethodInfo(dex_location1, dex_location_checksum1,
dex_location2, dex_location_checksum2);
if (reverse_dex_write_order) {
- ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, pmi));
- ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, pmi));
+ ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, kMaxMethodIds, pmi));
+ ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, kMaxMethodIds, pmi));
} else {
- ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, pmi));
- ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, pmi));
+ ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, kMaxMethodIds, pmi));
+ ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, kMaxMethodIds, pmi));
}
}
for (uint16_t i = 0; i < number_of_classes; i++) {
- ASSERT_TRUE(info->AddClassIndex(dex_location1, dex_location_checksum1, dex::TypeIndex(i)));
+ ASSERT_TRUE(info->AddClassIndex(dex_location1,
+ dex_location_checksum1,
+ dex::TypeIndex(i),
+ kMaxMethodIds));
}
ASSERT_TRUE(info->Save(GetFd(profile)));
@@ -84,8 +89,8 @@ class ProfileAssistantTest : public CommonRuntimeTest {
const std::string& dex_location2, uint32_t dex_checksum2) {
ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
- pmi.dex_references.emplace_back(dex_location1, dex_checksum1);
- pmi.dex_references.emplace_back(dex_location2, dex_checksum2);
+ pmi.dex_references.emplace_back(dex_location1, dex_checksum1, kMaxMethodIds);
+ pmi.dex_references.emplace_back(dex_location2, dex_checksum2, kMaxMethodIds);
// Monomorphic
for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
@@ -520,10 +525,11 @@ TEST_F(ProfileAssistantTest, TestProfileGenerationWithIndexDex) {
TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) {
// Class names put here need to be in sorted order.
std::vector<std::string> class_names = {
+ "HLjava/lang/Object;-><init>()V",
"Ljava/lang/Comparable;",
"Ljava/lang/Math;",
"Ljava/lang/Object;",
- "Ljava/lang/Object;-><init>()V"
+ "SPLjava/lang/Comparable;->compareTo(Ljava/lang/Object;)I",
};
std::string file_contents;
for (std::string& class_name : class_names) {
@@ -807,15 +813,21 @@ TEST_F(ProfileAssistantTest, TestProfileCreateWithInvalidData) {
// Verify that the start-up classes contain the invalid class.
std::set<dex::TypeIndex> classes;
- std::set<uint16_t> methods;
- ASSERT_TRUE(info.GetClassesAndMethods(*dex_file, &classes, &methods));
+ std::set<uint16_t> hot_methods;
+ std::set<uint16_t> startup_methods;
+ std::set<uint16_t> post_start_methods;
+ ASSERT_TRUE(info.GetClassesAndMethods(*dex_file,
+ &classes,
+ &hot_methods,
+ &startup_methods,
+ &post_start_methods));
ASSERT_EQ(1u, classes.size());
ASSERT_TRUE(classes.find(invalid_class_index) != classes.end());
// Verify that the invalid method is in the profile.
- ASSERT_EQ(2u, methods.size());
+ ASSERT_EQ(2u, hot_methods.size());
uint16_t invalid_method_index = std::numeric_limits<uint16_t>::max() - 1;
- ASSERT_TRUE(methods.find(invalid_method_index) != methods.end());
+ ASSERT_TRUE(hot_methods.find(invalid_method_index) != hot_methods.end());
}
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
index afc21057b1..adef0d0332 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -43,6 +43,7 @@
#include "runtime.h"
#include "type_reference.h"
#include "utils.h"
+#include "type_reference.h"
#include "zip_archive.h"
namespace art {
@@ -150,6 +151,9 @@ static const std::string kClassAllMethods = "*";
static constexpr char kProfileParsingInlineChacheSep = '+';
static constexpr char kProfileParsingTypeSep = ',';
static constexpr char kProfileParsingFirstCharInSignature = '(';
+static constexpr char kMethodFlagStringHot = 'H';
+static constexpr char kMethodFlagStringStartup = 'S';
+static constexpr char kMethodFlagStringPostStartup = 'P';
// TODO(calin): This class has grown too much from its initial design. Split the functionality
// into smaller, more contained pieces.
@@ -426,18 +430,42 @@ class ProfMan FINAL {
}
for (const std::unique_ptr<const DexFile>& dex_file : *dex_files) {
std::set<dex::TypeIndex> class_types;
- std::set<uint16_t> methods;
- if (profile_info.GetClassesAndMethods(*dex_file.get(), &class_types, &methods)) {
+ std::set<uint16_t> hot_methods;
+ std::set<uint16_t> startup_methods;
+ std::set<uint16_t> post_startup_methods;
+ std::set<uint16_t> combined_methods;
+ if (profile_info.GetClassesAndMethods(*dex_file.get(),
+ &class_types,
+ &hot_methods,
+ &startup_methods,
+ &post_startup_methods)) {
for (const dex::TypeIndex& type_index : class_types) {
const DexFile::TypeId& type_id = dex_file->GetTypeId(type_index);
out_lines->insert(std::string(dex_file->GetTypeDescriptor(type_id)));
}
- for (uint16_t dex_method_idx : methods) {
+ combined_methods = hot_methods;
+ combined_methods.insert(startup_methods.begin(), startup_methods.end());
+ combined_methods.insert(post_startup_methods.begin(), post_startup_methods.end());
+ for (uint16_t dex_method_idx : combined_methods) {
const DexFile::MethodId& id = dex_file->GetMethodId(dex_method_idx);
std::string signature_string(dex_file->GetMethodSignature(id).ToString());
std::string type_string(dex_file->GetTypeDescriptor(dex_file->GetTypeId(id.class_idx_)));
std::string method_name(dex_file->GetMethodName(id));
- out_lines->insert(type_string + kMethodSep + method_name + signature_string);
+ std::string flags_string;
+ if (hot_methods.find(dex_method_idx) != hot_methods.end()) {
+ flags_string += kMethodFlagStringHot;
+ }
+ if (startup_methods.find(dex_method_idx) != startup_methods.end()) {
+ flags_string += kMethodFlagStringStartup;
+ }
+ if (post_startup_methods.find(dex_method_idx) != post_startup_methods.end()) {
+ flags_string += kMethodFlagStringPostStartup;
+ }
+ out_lines->insert(flags_string +
+ type_string +
+ kMethodSep +
+ method_name +
+ signature_string);
}
}
}
@@ -461,7 +489,7 @@ class ProfMan FINAL {
return true;
}
- int DumpClasses() {
+ int DumpClassesAndMethods() {
// Validate that at least one profile file or reference was specified.
if (profile_files_.empty() && profile_files_fd_.empty() &&
reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
@@ -694,11 +722,30 @@ class ProfMan FINAL {
/*out*/ProfileCompilationInfo* profile) {
std::string klass;
std::string method_str;
- size_t method_sep_index = line.find(kMethodSep);
+ bool is_hot = false;
+ bool is_startup = false;
+ bool is_post_startup = false;
+ const size_t method_sep_index = line.find(kMethodSep, 0);
if (method_sep_index == std::string::npos) {
- klass = line;
+ klass = line.substr(0);
} else {
- klass = line.substr(0, method_sep_index);
+ // The method prefix flags are only valid for method strings.
+ size_t start_index = 0;
+ while (start_index < line.size() && line[start_index] != 'L') {
+ const char c = line[start_index];
+ if (c == kMethodFlagStringHot) {
+ is_hot = true;
+ } else if (c == kMethodFlagStringStartup) {
+ is_startup = true;
+ } else if (c == kMethodFlagStringPostStartup) {
+ is_post_startup = true;
+ } else {
+ LOG(WARNING) << "Invalid flag " << c;
+ return false;
+ }
+ ++start_index;
+ }
+ klass = line.substr(start_index, method_sep_index - start_index);
method_str = line.substr(method_sep_index + kMethodSep.size());
}
@@ -715,7 +762,8 @@ class ProfMan FINAL {
const auto& dex_resolved_classes = resolved_class_set.emplace(
dex_file->GetLocation(),
dex_file->GetBaseLocation(),
- dex_file->GetLocationChecksum());
+ dex_file->GetLocationChecksum(),
+ dex_file->NumMethodIds());
dex_resolved_classes.first->AddClass(class_ref.type_index);
std::vector<ProfileMethodInfo> methods;
if (method_str == kClassAllMethods) {
@@ -745,6 +793,9 @@ class ProfMan FINAL {
std::string method_spec;
std::vector<std::string> inline_cache_elems;
+ // If none of the flags are set, default to hot.
+ is_hot = is_hot || (!is_hot && !is_startup && !is_post_startup);
+
std::vector<std::string> method_elems;
bool is_missing_types = false;
Split(method_str, kProfileParsingInlineChacheSep, &method_elems);
@@ -766,7 +817,6 @@ class ProfMan FINAL {
return false;
}
- std::vector<ProfileMethodInfo> pmi;
std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches;
if (is_missing_types || !inline_cache_elems.empty()) {
uint32_t dex_pc;
@@ -783,8 +833,29 @@ class ProfMan FINAL {
}
inline_caches.emplace_back(dex_pc, is_missing_types, classes);
}
- pmi.emplace_back(class_ref.dex_file, method_index, inline_caches);
- profile->AddMethodsAndClasses(pmi, std::set<DexCacheResolvedClasses>());
+ ProfileMethodInfo pmi(class_ref.dex_file, method_index, inline_caches);
+ if (is_hot) {
+ profile->AddMethod(pmi);
+ }
+ if (is_startup) {
+ if (!profile->AddSampledMethod(/*is_startup*/ true,
+ pmi.dex_file->GetLocation(),
+ pmi.dex_file->GetLocationChecksum(),
+ method_index,
+ pmi.dex_file->NumMethodIds())) {
+ return false;
+ }
+ DCHECK(profile->IsStartupOrHotMethod(MethodReference(pmi.dex_file, method_index)));
+ }
+ if (is_post_startup) {
+ if (!profile->AddSampledMethod(/*is_startup*/ false,
+ pmi.dex_file->GetLocation(),
+ pmi.dex_file->GetLocationChecksum(),
+ method_index,
+ pmi.dex_file->NumMethodIds())) {
+ return false;
+ }
+ }
return true;
}
@@ -959,7 +1030,7 @@ static int profman(int argc, char** argv) {
return profman.DumpProfileInfo();
}
if (profman.ShouldOnlyDumpClassesAndMethods()) {
- return profman.DumpClasses();
+ return profman.DumpClassesAndMethods();
}
if (profman.ShouldCreateProfile()) {
return profman.CreateProfile();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c169ac0733..c3a8fc5383 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8949,7 +8949,8 @@ class GetResolvedClassesVisitor : public ClassVisitor {
last_dex_file_ = &dex_file;
DexCacheResolvedClasses resolved_classes(dex_file.GetLocation(),
dex_file.GetBaseLocation(),
- dex_file.GetLocationChecksum());
+ dex_file.GetLocationChecksum(),
+ dex_file.NumMethodIds());
last_resolved_classes_ = result_->find(resolved_classes);
if (last_resolved_classes_ == result_->end()) {
last_resolved_classes_ = result_->insert(resolved_classes).first;
diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h
index bebdf0dbfe..2278b052ed 100644
--- a/runtime/dex_cache_resolved_classes.h
+++ b/runtime/dex_cache_resolved_classes.h
@@ -30,10 +30,12 @@ class DexCacheResolvedClasses {
public:
DexCacheResolvedClasses(const std::string& dex_location,
const std::string& base_location,
- uint32_t location_checksum)
+ uint32_t location_checksum,
+ uint32_t num_method_ids)
: dex_location_(dex_location),
base_location_(base_location),
- location_checksum_(location_checksum) {}
+ location_checksum_(location_checksum),
+ num_method_ids_(num_method_ids) {}
// Only compare the key elements, ignore the resolved classes.
int Compare(const DexCacheResolvedClasses& other) const {
@@ -69,10 +71,15 @@ class DexCacheResolvedClasses {
return classes_;
}
+ size_t NumMethodIds() const {
+ return num_method_ids_;
+ }
+
private:
const std::string dex_location_;
const std::string base_location_;
const uint32_t location_checksum_;
+ const uint32_t num_method_ids_;
// Array of resolved class def indexes.
mutable std::unordered_set<dex::TypeIndex> classes_;
};
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 86c15e6227..9c039e2270 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -49,7 +49,7 @@ namespace art {
const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
// Last profile version: Instead of method index, put the difference with the last
// method's index.
-const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '7', '\0' };
+const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '8', '\0' };
static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
@@ -132,6 +132,33 @@ std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_
}
}
+bool ProfileCompilationInfo::AddSampledMethod(bool startup,
+ const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t method_idx,
+ uint32_t num_method_ids) {
+ DexFileData* data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
+ checksum,
+ num_method_ids);
+ if (data == nullptr) {
+ return false;
+ }
+ data->AddSampledMethod(startup, method_idx);
+ return true;
+}
+
+bool ProfileCompilationInfo::AddSampledMethods(bool startup,
+ std::vector<MethodReference>& methods) {
+ for (const MethodReference& ref : methods) {
+ DexFileData* data = GetOrAddDexFileData(ref.dex_file);
+ if (data == nullptr) {
+ return false;
+ }
+ data->AddSampledMethod(startup, ref.dex_method_index);
+ }
+ return true;
+}
+
bool ProfileCompilationInfo::AddMethodsAndClasses(
const std::vector<ProfileMethodInfo>& methods,
const std::set<DexCacheResolvedClasses>& resolved_classes) {
@@ -252,7 +279,7 @@ static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
static constexpr size_t kLineHeaderSize =
2 * sizeof(uint16_t) + // class_set.size + dex_location.size
- 2 * sizeof(uint32_t); // method_map.size + checksum
+ 3 * sizeof(uint32_t); // method_map.size + checksum + num_method_ids
/**
* Serialization format:
@@ -297,7 +324,8 @@ bool ProfileCompilationInfo::Save(int fd) {
required_capacity += kLineHeaderSize +
dex_data.profile_key.size() +
sizeof(uint16_t) * dex_data.class_set.size() +
- methods_region_size;
+ methods_region_size +
+ dex_data.bitmap_storage.size();
}
if (required_capacity > kProfileSizeErrorThresholdInBytes) {
LOG(ERROR) << "Profile data size exceeds "
@@ -335,10 +363,12 @@ bool ProfileCompilationInfo::Save(int fd) {
DCHECK_LE(dex_data.profile_key.size(), std::numeric_limits<uint16_t>::max());
DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
+ // Write profile line header.
AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.profile_key.size()));
AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
AddUintToBuffer(&buffer, methods_region_size); // uint32_t
AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t
+ AddUintToBuffer(&buffer, dex_data.num_method_ids); // uint32_t
AddStringToBuffer(&buffer, dex_data.profile_key);
@@ -362,6 +392,10 @@ bool ProfileCompilationInfo::Save(int fd) {
last_class_index = class_id.index_;
AddUintToBuffer(&buffer, diff_with_last_class_index);
}
+
+ buffer.insert(buffer.end(),
+ dex_data.bitmap_storage.begin(),
+ dex_data.bitmap_storage.end());
}
uint32_t output_size = 0;
@@ -476,7 +510,8 @@ void ProfileCompilationInfo::GroupClassesByDex(
ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
const std::string& profile_key,
- uint32_t checksum) {
+ uint32_t checksum,
+ uint32_t num_method_ids) {
const auto profile_index_it = profile_key_map_.FindOrAdd(profile_key, profile_key_map_.size());
if (profile_key_map_.size() > std::numeric_limits<uint8_t>::max()) {
// Allow only 255 dex files to be profiled. This allows us to save bytes
@@ -492,7 +527,11 @@ ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData
if (info_.size() <= profile_index) {
// This is a new addition. Add it to the info_ array.
DexFileData* dex_file_data = new (&arena_) DexFileData(
- &arena_, profile_key, checksum, profile_index);
+ &arena_,
+ profile_key,
+ checksum,
+ profile_index,
+ num_method_ids);
info_.push_back(dex_file_data);
}
DexFileData* result = info_[profile_index];
@@ -500,6 +539,7 @@ ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData
// This should always be the case since since the cache map is managed by ProfileCompilationInfo.
DCHECK_EQ(profile_key, result->profile_key);
DCHECK_EQ(profile_index, result->profile_index);
+ DCHECK_EQ(num_method_ids, result->num_method_ids);
// Check that the checksum matches.
// This may different if for example the dex file was updated and
@@ -528,7 +568,7 @@ const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) {
const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation());
const uint32_t checksum = classes.GetLocationChecksum();
- DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
+ DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, classes.NumMethodIds());
if (data == nullptr) {
return false;
}
@@ -538,15 +578,23 @@ bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& c
bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location,
uint32_t dex_checksum,
- uint16_t method_index) {
- return AddMethod(dex_location, dex_checksum, method_index, OfflineProfileMethodInfo(nullptr));
+ uint16_t method_index,
+ uint32_t num_method_ids) {
+ return AddMethod(dex_location,
+ dex_checksum,
+ method_index,
+ num_method_ids,
+ OfflineProfileMethodInfo(nullptr));
}
bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
uint32_t dex_checksum,
uint16_t method_index,
+ uint32_t num_method_ids,
const OfflineProfileMethodInfo& pmi) {
- DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location), dex_checksum);
+ DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
+ dex_checksum,
+ num_method_ids);
if (data == nullptr) { // checksum mismatch
return false;
}
@@ -579,7 +627,8 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index];
DexFileData* class_dex_data = GetOrAddDexFileData(
GetProfileDexFileKey(dex_ref.dex_location),
- dex_ref.dex_checksum);
+ dex_ref.dex_checksum,
+ dex_ref.num_method_ids);
if (class_dex_data == nullptr) { // checksum mismatch
return false;
}
@@ -590,9 +639,7 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
}
bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
- DexFileData* const data = GetOrAddDexFileData(
- GetProfileDexFileKey(pmi.dex_file->GetLocation()),
- pmi.dex_file->GetLocationChecksum());
+ DexFileData* const data = GetOrAddDexFileData(pmi.dex_file);
if (data == nullptr) { // checksum mismatch
return false;
}
@@ -604,9 +651,7 @@ bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
continue;
}
for (const TypeReference& class_ref : cache.classes) {
- DexFileData* class_dex_data = GetOrAddDexFileData(
- GetProfileDexFileKey(class_ref.dex_file->GetLocation()),
- class_ref.dex_file->GetLocationChecksum());
+ DexFileData* class_dex_data = GetOrAddDexFileData(class_ref.dex_file);
if (class_dex_data == nullptr) { // checksum mismatch
return false;
}
@@ -623,8 +668,9 @@ bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
uint32_t checksum,
- dex::TypeIndex type_idx) {
- DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
+ dex::TypeIndex type_idx,
+ uint32_t num_method_ids) {
+ DexFileData* const data = GetOrAddDexFileData(dex_location, checksum, num_method_ids);
if (data == nullptr) {
return false;
}
@@ -694,7 +740,9 @@ bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer,
- line_header.method_region_size_bytes;
uint16_t last_method_index = 0;
while (buffer.CountUnreadBytes() > expected_unread_bytes_after_operation) {
- DexFileData* const data = GetOrAddDexFileData(line_header.dex_location, line_header.checksum);
+ DexFileData* const data = GetOrAddDexFileData(line_header.dex_location,
+ line_header.checksum,
+ line_header.num_method_ids);
uint16_t diff_with_last_method_index;
READ_UINT(uint16_t, buffer, diff_with_last_method_index, error);
uint16_t method_index = last_method_index + diff_with_last_method_index;
@@ -729,7 +777,8 @@ bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
last_class_index = type_index;
if (!AddClassIndex(line_header.dex_location,
line_header.checksum,
- dex::TypeIndex(type_index))) {
+ dex::TypeIndex(type_index),
+ line_header.num_method_ids)) {
return false;
}
}
@@ -863,6 +912,7 @@ bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer,
READ_UINT(uint16_t, buffer, line_header->class_set_size, error);
READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error);
READ_UINT(uint32_t, buffer, line_header->checksum, error);
+ READ_UINT(uint32_t, buffer, line_header->num_method_ids, error);
return true;
}
@@ -902,7 +952,10 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine
uint8_t number_of_dex_files,
const ProfileLineHeader& line_header,
/*out*/std::string* error) {
- if (GetOrAddDexFileData(line_header.dex_location, line_header.checksum) == nullptr) {
+ DexFileData* data = GetOrAddDexFileData(line_header.dex_location,
+ line_header.checksum,
+ line_header.num_method_ids);
+ if (data == nullptr) {
*error = "Error when reading profile file line header: checksum mismatch for "
+ line_header.dex_location;
return kProfileLoadBadData;
@@ -915,6 +968,16 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine
if (!ReadClasses(buffer, line_header, error)) {
return kProfileLoadBadData;
}
+
+ const size_t bytes = data->bitmap_storage.size();
+ if (buffer.CountUnreadBytes() < bytes) {
+ *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
+ return kProfileLoadBadData;
+ }
+ const uint8_t* base_ptr = buffer.GetCurrentPtr();
+ std::copy_n(base_ptr, bytes, &data->bitmap_storage[0]);
+ buffer.Advance(bytes);
+ // Read method bitmap.
return kProfileLoadSuccess;
}
@@ -932,6 +995,15 @@ bool ProfileCompilationInfo::Load(int fd) {
}
}
+void ProfileCompilationInfo::DexFileData::CreateBitmap() {
+ const size_t num_bits = num_method_ids * kMethodBitCount;
+ bitmap_storage.resize(RoundUp(num_bits, kBitsPerByte) / kBitsPerByte);
+ if (!bitmap_storage.empty()) {
+ method_bitmap =
+ BitMemoryRegion(MemoryRegion(&bitmap_storage[0], bitmap_storage.size()), 0, num_bits);
+ }
+}
+
// TODO(calin): fail fast if the dex checksums don't match.
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
int fd, std::string* error) {
@@ -1110,7 +1182,8 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
for (const DexFileData* other_dex_data : other.info_) {
const DexFileData* dex_data = GetOrAddDexFileData(other_dex_data->profile_key,
- other_dex_data->checksum);
+ other_dex_data->checksum,
+ other_dex_data->num_method_ids);
if (dex_data == nullptr) {
return false; // Could happen if we exceed the number of allowed dex files.
}
@@ -1147,6 +1220,9 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
}
}
}
+
+ // Merge the bitmaps.
+ dex_data->MergeBitmap(*other_dex_data);
}
return true;
}
@@ -1159,6 +1235,27 @@ static bool ChecksumMatch(const DexFile& dex_file, uint32_t checksum) {
return ChecksumMatch(dex_file.GetLocationChecksum(), checksum);
}
+bool ProfileCompilationInfo::IsStartupOrHotMethod(const MethodReference& method_ref) const {
+ return IsStartupOrHotMethod(method_ref.dex_file->GetLocation(),
+ method_ref.dex_file->GetLocationChecksum(),
+ method_ref.dex_method_index);
+}
+
+bool ProfileCompilationInfo::IsStartupOrHotMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t dex_method_index) const {
+ const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_location));
+ if (dex_data == nullptr || !ChecksumMatch(dex_checksum, dex_data->checksum)) {
+ return false;
+ }
+ if (dex_data->HasSampledMethod(/*startup*/ true, dex_method_index)) {
+ return true;
+ }
+ const MethodMap& methods = dex_data->method_map;
+ const auto method_it = methods.find(dex_method_index);
+ return method_it != methods.end();
+}
+
bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
return FindMethod(method_ref.dex_file->GetLocation(),
method_ref.dex_file->GetLocationChecksum(),
@@ -1196,6 +1293,7 @@ std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> ProfileCompila
for (const DexFileData* dex_data : info_) {
pmi->dex_references[dex_data->profile_index].dex_location = dex_data->profile_key;
pmi->dex_references[dex_data->profile_index].dex_checksum = dex_data->checksum;
+ pmi->dex_references[dex_data->profile_index].num_method_ids = dex_data->num_method_ids;
}
return pmi;
@@ -1314,9 +1412,12 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>*
return os.str();
}
-bool ProfileCompilationInfo::GetClassesAndMethods(const DexFile& dex_file,
- std::set<dex::TypeIndex>* class_set,
- std::set<uint16_t>* method_set) const {
+bool ProfileCompilationInfo::GetClassesAndMethods(
+ const DexFile& dex_file,
+ /*out*/std::set<dex::TypeIndex>* class_set,
+ /*out*/std::set<uint16_t>* hot_method_set,
+ /*out*/std::set<uint16_t>* startup_method_set,
+ /*out*/std::set<uint16_t>* post_startup_method_method_set) const {
std::set<std::string> ret;
std::string profile_key = GetProfileDexFileKey(dex_file.GetLocation());
const DexFileData* dex_data = FindDexData(profile_key);
@@ -1324,7 +1425,15 @@ bool ProfileCompilationInfo::GetClassesAndMethods(const DexFile& dex_file,
return false;
}
for (const auto& it : dex_data->method_map) {
- method_set->insert(it.first);
+ hot_method_set->insert(it.first);
+ }
+ for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
+ if (dex_data->HasSampledMethod(/*startup*/ true, method_idx)) {
+ startup_method_set->insert(method_idx);
+ }
+ if (dex_data->HasSampledMethod(/*startup*/ false, method_idx)) {
+ post_startup_method_method_set->insert(method_idx);
+ }
}
for (const dex::TypeIndex& type_index : dex_data->class_set) {
class_set->insert(type_index);
@@ -1366,7 +1475,10 @@ std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses(
<< ", profile checksum=" << dex_data->checksum;
return std::set<DexCacheResolvedClasses>();
}
- DexCacheResolvedClasses classes(dex_location, dex_location, dex_data->checksum);
+ DexCacheResolvedClasses classes(dex_location,
+ dex_location,
+ dex_data->checksum,
+ dex_data->num_method_ids);
classes.AddClasses(dex_data->class_set.begin(), dex_data->class_set.end());
ret.insert(classes);
}
@@ -1383,8 +1495,8 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd,
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();
+ const uint16_t max_method = std::numeric_limits<uint16_t>::max();
+ const 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;
@@ -1404,7 +1516,7 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd,
if (m < (number_of_methods / kFavorSplit)) {
method_idx %= kFavorFirstN;
}
- info.AddMethodIndex(profile_key, 0, method_idx);
+ info.AddMethodIndex(profile_key, 0, method_idx, max_method);
}
for (uint16_t c = 0; c < number_of_classes; c++) {
@@ -1412,7 +1524,7 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd,
if (c < (number_of_classes / kFavorSplit)) {
type_idx %= kFavorFirstN;
}
- info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx));
+ info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx), max_method);
}
}
return info.Save(fd);
@@ -1431,13 +1543,16 @@ bool ProfileCompilationInfo::GenerateTestProfile(
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_));
+ info.AddClassIndex(location,
+ checksum,
+ dex::TypeIndex(dex_file->GetClassDef(i).class_idx_),
+ dex_file->NumMethodIds());
}
}
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);
+ info.AddMethodIndex(location, checksum, i, dex_file->NumMethodIds());
}
}
}
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index ca5b28a809..2b89a41dd8 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -23,6 +23,7 @@
#include "atomic.h"
#include "base/arena_object.h"
#include "base/arena_containers.h"
+#include "bit_memory_region.h"
#include "dex_cache_resolved_classes.h"
#include "dex_file.h"
#include "dex_file_types.h"
@@ -54,7 +55,9 @@ struct ProfileMethodInfo {
ProfileMethodInfo(const DexFile* dex,
uint32_t method_index,
const std::vector<ProfileInlineCache>& caches)
- : dex_file(dex), dex_method_index(method_index), inline_caches(caches) {}
+ : dex_file(dex),
+ dex_method_index(method_index),
+ inline_caches(caches) {}
const DexFile* dex_file;
const uint32_t dex_method_index;
@@ -79,13 +82,15 @@ class ProfileCompilationInfo {
// A dex location together with its checksum.
struct DexReference {
- DexReference() : dex_checksum(0) {}
+ DexReference() : dex_checksum(0), num_method_ids(0) {}
- DexReference(const std::string& location, uint32_t checksum)
- : dex_location(location), dex_checksum(checksum) {}
+ DexReference(const std::string& location, uint32_t checksum, uint32_t num_methods)
+ : dex_location(location), dex_checksum(checksum), num_method_ids(num_methods) {}
bool operator==(const DexReference& other) const {
- return dex_checksum == other.dex_checksum && dex_location == other.dex_location;
+ return dex_checksum == other.dex_checksum &&
+ dex_location == other.dex_location &&
+ num_method_ids == other.num_method_ids;
}
bool MatchesDex(const DexFile* dex_file) const {
@@ -95,6 +100,7 @@ class ProfileCompilationInfo {
std::string dex_location;
uint32_t dex_checksum;
+ uint32_t num_method_ids;
};
// Encodes a class reference in the profile.
@@ -191,6 +197,24 @@ class ProfileCompilationInfo {
bool AddMethodsAndClasses(const std::vector<ProfileMethodInfo>& methods,
const std::set<DexCacheResolvedClasses>& resolved_classes);
+ // Add a method index to the profile (without inline caches).
+ bool AddMethodIndex(const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t method_idx,
+ uint32_t num_method_ids);
+
+ // Add a method to the profile using its online representation (containing runtime structures).
+ bool AddMethod(const ProfileMethodInfo& pmi);
+
+ // Add methods that have samples but are are not necessarily hot. These are partitioned into two
+ // possibly interesecting sets startup and post startup.
+ bool AddSampledMethods(bool startup, std::vector<MethodReference>& methods);
+ bool AddSampledMethod(bool startup,
+ const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t method_idx,
+ uint32_t num_method_ids);
+
// Load profile information from the given file descriptor.
// If the current profile is non-empty the load will fail.
bool Load(int fd);
@@ -216,6 +240,12 @@ class ProfileCompilationInfo {
// Return the number of resolved classes that were profiled.
uint32_t GetNumberOfResolvedClasses() const;
+ // Return true if the method reference is a hot or startup method in the profiling info.
+ bool IsStartupOrHotMethod(const MethodReference& method_ref) const;
+ bool IsStartupOrHotMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t dex_method_index) const;
+
// Return true if the method reference is present in the profiling info.
bool ContainsMethod(const MethodReference& method_ref) const;
@@ -244,7 +274,9 @@ class ProfileCompilationInfo {
// file is register and has a matching checksum, false otherwise.
bool GetClassesAndMethods(const DexFile& dex_file,
/*out*/std::set<dex::TypeIndex>* class_set,
- /*out*/std::set<uint16_t>* method_set) const;
+ /*out*/std::set<uint16_t>* hot_method_set,
+ /*out*/std::set<uint16_t>* startup_method_set,
+ /*out*/std::set<uint16_t>* post_startup_method_method_set) const;
// Perform an equality test with the `other` profile information.
bool Equals(const ProfileCompilationInfo& other);
@@ -301,13 +333,31 @@ class ProfileCompilationInfo {
DexFileData(ArenaAllocator* arena,
const std::string& key,
uint32_t location_checksum,
- uint16_t index)
+ uint16_t index,
+ uint32_t num_methods)
: arena_(arena),
profile_key(key),
profile_index(index),
checksum(location_checksum),
method_map(std::less<uint16_t>(), arena->Adapter(kArenaAllocProfile)),
- class_set(std::less<dex::TypeIndex>(), arena->Adapter(kArenaAllocProfile)) {}
+ class_set(std::less<dex::TypeIndex>(), arena->Adapter(kArenaAllocProfile)),
+ num_method_ids(num_methods),
+ bitmap_storage(arena->Adapter(kArenaAllocProfile)) {
+ CreateBitmap();
+ }
+
+ bool operator==(const DexFileData& other) const {
+ return checksum == other.checksum && method_map == other.method_map;
+ }
+
+ // Mark a method as executed at least once.
+ void AddSampledMethod(bool startup, size_t index) {
+ method_bitmap.StoreBit(MethodBitIndex(startup, index), true);
+ }
+
+ bool HasSampledMethod(bool startup, size_t index) const {
+ return method_bitmap.LoadBit(MethodBitIndex(startup, index));
+ }
// The arena used to allocate new inline cache maps.
ArenaAllocator* arena_;
@@ -322,32 +372,64 @@ class ProfileCompilationInfo {
// The classes which have been profiled. Note that these don't necessarily include
// all the classes that can be found in the inline caches reference.
ArenaSet<dex::TypeIndex> class_set;
-
- bool operator==(const DexFileData& other) const {
- return checksum == other.checksum && method_map == other.method_map;
- }
-
// Find the inline caches of the the given method index. Add an empty entry if
// no previous data is found.
InlineCacheMap* FindOrAddMethod(uint16_t method_index);
+ // Num method ids.
+ uint32_t num_method_ids;
+ ArenaVector<uint8_t> bitmap_storage;
+ BitMemoryRegion method_bitmap;
+
+ void CreateBitmap();
+
+ void MergeBitmap(const DexFileData& other) {
+ DCHECK_EQ(bitmap_storage.size(), other.bitmap_storage.size());
+ for (size_t i = 0; i < bitmap_storage.size(); ++i) {
+ bitmap_storage[i] |= other.bitmap_storage[i];
+ }
+ }
+
+ private:
+ enum Bits {
+ kMethodBitStartup,
+ kMethodBitAfterStartup,
+ kMethodBitCount,
+ };
+
+ size_t MethodBitIndex(bool startup, size_t index) const {
+ DCHECK_LT(index, num_method_ids);
+ if (!startup) {
+ index += num_method_ids;
+ }
+ return index;
+ }
};
// Return the profile data for the given profile key or null if the dex location
// already exists but has a different checksum
- DexFileData* GetOrAddDexFileData(const std::string& profile_key, uint32_t checksum);
+ DexFileData* GetOrAddDexFileData(const std::string& profile_key,
+ uint32_t checksum,
+ uint32_t num_method_ids);
- // Add a method to the profile using its online representation (containing runtime structures).
- bool AddMethod(const ProfileMethodInfo& pmi);
+ DexFileData* GetOrAddDexFileData(const DexFile* dex_file) {
+ return GetOrAddDexFileData(GetProfileDexFileKey(dex_file->GetLocation()),
+ dex_file->GetLocationChecksum(),
+ dex_file->NumMethodIds());
+ }
// Add a method to the profile using its offline representation.
// This is mostly used to facilitate testing.
bool AddMethod(const std::string& dex_location,
uint32_t dex_checksum,
uint16_t method_index,
+ uint32_t num_method_ids,
const OfflineProfileMethodInfo& pmi);
// Add a class index to the profile.
- bool AddClassIndex(const std::string& dex_location, uint32_t checksum, dex::TypeIndex type_idx);
+ bool AddClassIndex(const std::string& dex_location,
+ uint32_t checksum,
+ dex::TypeIndex type_idx,
+ uint32_t num_method_ids);
// Add all classes from the given dex cache to the the profile.
bool AddResolvedClasses(const DexCacheResolvedClasses& classes);
@@ -392,6 +474,7 @@ class ProfileCompilationInfo {
uint16_t class_set_size;
uint32_t method_region_size_bytes;
uint32_t checksum;
+ uint32_t num_method_ids;
};
// A helper structure to make sure we don't read past our buffers in the loops.
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 1cfa3552b9..615149feb3 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -32,6 +32,8 @@
namespace art {
+static constexpr size_t kMaxMethodIds = 65535;
+
class ProfileCompilationInfoTest : public CommonRuntimeTest {
public:
void PostRuntimeCreate() OVERRIDE {
@@ -61,7 +63,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
uint32_t checksum,
uint16_t method_index,
ProfileCompilationInfo* info) {
- return info->AddMethodIndex(dex_location, checksum, method_index);
+ return info->AddMethodIndex(dex_location, checksum, method_index, kMaxMethodIds);
}
bool AddMethod(const std::string& dex_location,
@@ -69,14 +71,14 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
uint16_t method_index,
const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi,
ProfileCompilationInfo* info) {
- return info->AddMethod(dex_location, checksum, method_index, pmi);
+ return info->AddMethod(dex_location, checksum, method_index, kMaxMethodIds, pmi);
}
bool AddClass(const std::string& dex_location,
uint32_t checksum,
uint16_t class_index,
ProfileCompilationInfo* info) {
- return info->AddMethodIndex(dex_location, checksum, class_index);
+ return info->AddMethodIndex(dex_location, checksum, class_index, kMaxMethodIds);
}
uint32_t GetFd(const ScratchFile& file) {
@@ -149,7 +151,9 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
std::vector<TypeReference> classes;
caches.emplace_back(dex_pc, /*is_missing_types*/true, classes);
}
- ProfileMethodInfo pmi(method->GetDexFile(), method->GetDexMethodIndex(), caches);
+ ProfileMethodInfo pmi(method->GetDexFile(),
+ method->GetDexMethodIndex(),
+ caches);
profile_methods.push_back(pmi);
profile_methods_map->Put(method, pmi);
}
@@ -191,7 +195,8 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey(
class_ref.dex_file->GetLocation());
offline_pmi.dex_references.emplace_back(dex_key,
- class_ref.dex_file->GetLocationChecksum());
+ class_ref.dex_file->GetLocationChecksum(),
+ class_ref.dex_file->NumMethodIds());
}
}
}
@@ -201,6 +206,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
// Creates an offline profile used for testing inline caches.
ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() {
ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
+
// Monomorphic
for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
@@ -231,9 +237,9 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
- pmi.dex_references.emplace_back("dex_location1", /* checksum */1);
- pmi.dex_references.emplace_back("dex_location2", /* checksum */2);
- pmi.dex_references.emplace_back("dex_location3", /* checksum */3);
+ pmi.dex_references.emplace_back("dex_location1", /* checksum */1, kMaxMethodIds);
+ pmi.dex_references.emplace_back("dex_location2", /* checksum */2, kMaxMethodIds);
+ pmi.dex_references.emplace_back("dex_location3", /* checksum */3, kMaxMethodIds);
return pmi;
}
@@ -694,8 +700,8 @@ TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) {
ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
- pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
- pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2);
+ pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
+ pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2, kMaxMethodIds);
for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
dex_pc_data.AddClass(0, dex::TypeIndex(0));
@@ -705,8 +711,8 @@ TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) {
ProfileCompilationInfo::InlineCacheMap* ic_map_reindexed = CreateInlineCacheMap();
ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed(ic_map_reindexed);
- pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2);
- pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1);
+ pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2, kMaxMethodIds);
+ pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
dex_pc_data.AddClass(1, dex::TypeIndex(0));
@@ -761,7 +767,7 @@ TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) {
// Create a megamorphic inline cache.
ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
- pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
+ pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
dex_pc_data.SetIsMegamorphic();
ic_map->Put(/*dex_pc*/ 0, dex_pc_data);
@@ -791,7 +797,7 @@ TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) {
// Create an inline cache with missing types
ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
- pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
+ pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
dex_pc_data.SetIsMissingTypes();
ic_map->Put(/*dex_pc*/ 0, dex_pc_data);
@@ -839,4 +845,48 @@ TEST_F(ProfileCompilationInfoTest, LoadShouldClearExistingDataFromProfiles) {
// This should fail since the test_info already contains data and the load would overwrite it.
ASSERT_FALSE(test_info.Load(GetFd(profile)));
}
+
+TEST_F(ProfileCompilationInfoTest, SampledMethodsTest) {
+ ProfileCompilationInfo test_info;
+ static constexpr size_t kNumMethods = 1000;
+ static constexpr size_t kChecksum1 = 1234;
+ static constexpr size_t kChecksum2 = 4321;
+ static const std::string kDex1 = "dex1";
+ static const std::string kDex2 = "dex2";
+ test_info.AddSampledMethod(true, kDex1, kChecksum1, 1, kNumMethods);
+ test_info.AddSampledMethod(true, kDex1, kChecksum1, 5, kNumMethods);
+ test_info.AddSampledMethod(false, kDex2, kChecksum2, 1, kNumMethods);
+ test_info.AddSampledMethod(false, kDex2, kChecksum2, 5, kNumMethods);
+ auto run_test = [](const ProfileCompilationInfo& info) {
+ EXPECT_FALSE(info.IsStartupOrHotMethod(kDex1, kChecksum1, 0));
+ EXPECT_TRUE(info.IsStartupOrHotMethod(kDex1, kChecksum1, 1));
+ EXPECT_FALSE(info.IsStartupOrHotMethod(kDex1, kChecksum1, 3));
+ EXPECT_TRUE(info.IsStartupOrHotMethod(kDex1, kChecksum1, 5));
+ EXPECT_FALSE(info.IsStartupOrHotMethod(kDex1, kChecksum1, 6));
+ EXPECT_FALSE(info.IsStartupOrHotMethod(kDex2, kChecksum2, 5));
+ EXPECT_FALSE(info.IsStartupOrHotMethod(kDex2, kChecksum2, 5));
+ };
+ run_test(test_info);
+
+ // Save the profile.
+ ScratchFile profile;
+ ASSERT_TRUE(test_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+
+ // Load the profile and make sure we can read the data and it matches what we expect.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+ run_test(loaded_info);
+
+ // Test that the bitmap gets merged properly.
+ EXPECT_FALSE(test_info.IsStartupOrHotMethod(kDex1, kChecksum1, 11));
+ {
+ ProfileCompilationInfo merge_info;
+ merge_info.AddSampledMethod(true, kDex1, kChecksum1, 11, kNumMethods);
+ test_info.MergeWith(merge_info);
+ }
+ EXPECT_TRUE(test_info.IsStartupOrHotMethod(kDex1, kChecksum1, 11));
+}
+
} // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index bc829cf9a7..c96ca88874 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -183,8 +183,11 @@ void ProfileSaver::NotifyJitActivityInternal() {
// Excludes native methods and classes in the boot image.
class GetMethodsVisitor : public ClassVisitor {
public:
- GetMethodsVisitor(std::vector<MethodReference>* methods, uint32_t startup_method_samples)
- : methods_(methods),
+ GetMethodsVisitor(std::vector<MethodReference>* hot_methods,
+ std::vector<MethodReference>* startup_methods,
+ uint32_t startup_method_samples)
+ : hot_methods_(hot_methods),
+ startup_methods_(startup_methods),
startup_method_samples_(startup_method_samples) {}
virtual bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -192,22 +195,26 @@ class GetMethodsVisitor : public ClassVisitor {
return true;
}
for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
- if (!method.IsNative()) {
- if (method.GetCounter() >= startup_method_samples_ ||
- method.GetProfilingInfo(kRuntimePointerSize) != nullptr ||
+ if (!method.IsNative() && !method.IsProxyMethod()) {
+ const uint16_t counter = method.GetCounter();
+ MethodReference ref(method.GetDexFile(), method.GetDexMethodIndex());
+ if (method.GetProfilingInfo(kRuntimePointerSize) != nullptr ||
(method.GetAccessFlags() & kAccPreviouslyWarm) != 0) {
- // Have samples, add to profile.
- const DexFile* dex_file =
- method.GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetDexFile();
- methods_->push_back(MethodReference(dex_file, method.GetDexMethodIndex()));
+ hot_methods_->push_back(ref);
+ startup_methods_->push_back(ref);
+ } else if (counter >= startup_method_samples_) {
+ startup_methods_->push_back(ref);
}
+ } else {
+ CHECK_EQ(method.GetCounter(), 0u);
}
}
return true;
}
private:
- std::vector<MethodReference>* const methods_;
+ std::vector<MethodReference>* const hot_methods_;
+ std::vector<MethodReference>* const startup_methods_;
uint32_t startup_method_samples_;
};
@@ -218,7 +225,8 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
ResolveTrackedLocations();
Thread* const self = Thread::Current();
- std::vector<MethodReference> methods;
+ std::vector<MethodReference> hot_methods;
+ std::vector<MethodReference> startup_methods;
std::set<DexCacheResolvedClasses> resolved_classes;
{
ScopedObjectAccess soa(self);
@@ -231,10 +239,13 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
{
ScopedTrace trace2("Get hot methods");
- GetMethodsVisitor visitor(&methods, options_.GetStartupMethodSamples());
+ GetMethodsVisitor visitor(&hot_methods,
+ &startup_methods,
+ options_.GetStartupMethodSamples());
class_linker->VisitClasses(&visitor);
- VLOG(profiler) << "Methods with samples greater than "
- << options_.GetStartupMethodSamples() << " = " << methods.size();
+ VLOG(profiler) << "Profile saver recorded " << hot_methods.size() << " hot methods and "
+ << startup_methods.size() << " startup methods with threshold "
+ << options_.GetStartupMethodSamples();
}
}
MutexLock mu(self, *Locks::profiler_lock_);
@@ -245,11 +256,18 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
const std::string& filename = it.first;
const std::set<std::string>& locations = it.second;
std::vector<ProfileMethodInfo> profile_methods_for_location;
- for (const MethodReference& ref : methods) {
+ for (const MethodReference& ref : hot_methods) {
if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) {
profile_methods_for_location.emplace_back(ref.dex_file, ref.dex_method_index);
}
}
+ std::vector<MethodReference> startup_methods_for_locations;
+ for (const MethodReference& ref : startup_methods) {
+ if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) {
+ startup_methods_for_locations.push_back(ref);
+ }
+ }
+
for (const DexCacheResolvedClasses& classes : resolved_classes) {
if (locations.find(classes.GetBaseLocation()) != locations.end()) {
VLOG(profiler) << "Added " << classes.GetClasses().size() << " classes for location "
@@ -265,8 +283,8 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
new ProfileCompilationInfo(Runtime::Current()->GetArenaPool()));
ProfileCompilationInfo* cached_info = info_it->second;
- cached_info->AddMethodsAndClasses(profile_methods_for_location,
- resolved_classes_for_location);
+ cached_info->AddMethodsAndClasses(profile_methods_for_location, resolved_classes_for_location);
+ cached_info->AddSampledMethods(/*startup*/ true, startup_methods_for_locations);
total_number_of_profile_entries_cached += resolved_classes_for_location.size();
}
max_number_of_profile_entries_cached_ = std::max(
@@ -317,8 +335,7 @@ bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number
uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
- info.AddMethodsAndClasses(profile_methods,
- std::set<DexCacheResolvedClasses>());
+ info.AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>());
auto profile_cache_it = profile_cache_.find(filename);
if (profile_cache_it != profile_cache_.end()) {
info.MergeWith(*(profile_cache_it->second));
diff --git a/runtime/safe_map.h b/runtime/safe_map.h
index e638fdb504..b54f587715 100644
--- a/runtime/safe_map.h
+++ b/runtime/safe_map.h
@@ -46,6 +46,7 @@ class SafeMap {
SafeMap() = default;
SafeMap(const SafeMap&) = default;
+ SafeMap(SafeMap&&) = default;
explicit SafeMap(const key_compare& cmp, const allocator_type& allocator = allocator_type())
: map_(cmp, allocator) {
}
@@ -151,6 +152,11 @@ class SafeMap {
return map_ == rhs.map_;
}
+ template <class... Args>
+ std::pair<iterator, bool> emplace(Args&&... args) {
+ return map_.emplace(std::forward<Args>(args)...);
+ }
+
private:
::std::map<K, V, Comparator, Allocator> map_;
};