Add inline caches to offline profiles
Add support for inline caches in profiles:
- extract inline caches from the jit cache when the profile saver
queries the hot methods
- bump profile version to support the new data
- add new tests
- inline caches are only supported for same-apk calls (including
multidex)
Test: m art-test-host-gtest-profile_compilation_info_test
Bug: 32434870
Change-Id: I38b4ca0a54568d2224765ff76023baef1b8fd1a2
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 60ab275..c226a38 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1245,15 +1245,40 @@
}
void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_locations,
- std::vector<MethodReference>& methods) {
+ std::vector<ProfileMethodInfo>& methods) {
ScopedTrace trace(__FUNCTION__);
MutexLock mu(Thread::Current(), lock_);
for (const ProfilingInfo* info : profiling_infos_) {
ArtMethod* method = info->GetMethod();
const DexFile* dex_file = method->GetDexFile();
- if (ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) {
- methods.emplace_back(dex_file, method->GetDexMethodIndex());
+ if (!ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) {
+ // Skip dex files which are not profiled.
+ continue;
}
+ std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches;
+ for (size_t i = 0; i < info->number_of_inline_caches_; ++i) {
+ std::vector<ProfileMethodInfo::ProfileClassReference> profile_classes;
+ const InlineCache& cache = info->cache_[i];
+ for (size_t k = 0; k < InlineCache::kIndividualCacheSize; k++) {
+ mirror::Class* cls = cache.classes_[k].Read();
+ if (cls == nullptr) {
+ break;
+ }
+ const DexFile& class_dex_file = cls->GetDexFile();
+ dex::TypeIndex type_index = cls->GetDexTypeIndex();
+ if (ContainsElement(dex_base_locations, class_dex_file.GetBaseLocation())) {
+ // Only consider classes from the same apk (including multidex).
+ profile_classes.emplace_back(/*ProfileMethodInfo::ProfileClassReference*/
+ &class_dex_file, type_index);
+ }
+ }
+ if (!profile_classes.empty()) {
+ inline_caches.emplace_back(/*ProfileMethodInfo::ProfileInlineCache*/
+ cache.dex_pc_, profile_classes);
+ }
+ }
+ methods.emplace_back(/*ProfileMethodInfo*/
+ dex_file, method->GetDexMethodIndex(), inline_caches);
}
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index b5e3176..33a792f 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -30,6 +30,7 @@
#include "method_reference.h"
#include "oat_file.h"
#include "object_callbacks.h"
+#include "profile_compilation_info.h"
#include "safe_map.h"
#include "thread_pool.h"
@@ -192,7 +193,7 @@
// Adds to `methods` all profiled methods which are part of any of the given dex locations.
void GetProfiledMethods(const std::set<std::string>& dex_base_locations,
- std::vector<MethodReference>& methods)
+ std::vector<ProfileMethodInfo>& methods)
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 54fc038..5638ce1 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -37,7 +37,7 @@
namespace art {
const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
-const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '2', '\0' };
+const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '3', '\0' }; // inline caches
static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
@@ -46,6 +46,25 @@
// using the same test profile.
static constexpr bool kDebugIgnoreChecksum = false;
+static constexpr uint8_t kMegamorphicEncoding = 7;
+
+static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t),
+ "InlineCache::kIndividualCacheSize does not have the expect type size");
+static_assert(InlineCache::kIndividualCacheSize < kMegamorphicEncoding,
+ "InlineCache::kIndividualCacheSize is larger than expected");
+
+void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
+ const dex::TypeIndex& type_idx) {
+ if (is_megamorphic) {
+ return;
+ }
+ classes.emplace(dex_profile_idx, type_idx);
+ if (classes.size() >= InlineCache::kIndividualCacheSize) {
+ is_megamorphic = true;
+ classes.clear();
+ }
+}
+
// Transform the actual dex location into relative paths.
// Note: this is OK because we don't store profiles of different apps into the same file.
// Apps with split apks don't cause trouble because each split has a different name and will not
@@ -62,12 +81,10 @@
}
bool ProfileCompilationInfo::AddMethodsAndClasses(
- const std::vector<MethodReference>& methods,
+ const std::vector<ProfileMethodInfo>& methods,
const std::set<DexCacheResolvedClasses>& resolved_classes) {
- for (const MethodReference& method : methods) {
- if (!AddMethodIndex(GetProfileDexFileKey(method.dex_file->GetLocation()),
- method.dex_file->GetLocationChecksum(),
- method.dex_method_index)) {
+ for (const ProfileMethodInfo& method : methods) {
+ if (!AddMethod(method)) {
return false;
}
}
@@ -170,29 +187,40 @@
}
static constexpr size_t kLineHeaderSize =
- 3 * sizeof(uint16_t) + // method_set.size + class_set.size + dex_location.size
- sizeof(uint32_t); // checksum
+ 2 * sizeof(uint16_t) + // class_set.size + dex_location.size
+ 2 * sizeof(uint32_t); // method_map.size + checksum
/**
* Serialization format:
- * magic,version,number_of_lines
- * dex_location1,number_of_methods1,number_of_classes1,dex_location_checksum1, \
- * method_id11,method_id12...,class_id1,class_id2...
- * dex_location2,number_of_methods2,number_of_classes2,dex_location_checksum2, \
- * method_id21,method_id22...,,class_id1,class_id2...
+ * magic,version,number_of_dex_files
+ * dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1, \
+ * method_encoding_11,method_encoding_12...,class_id1,class_id2...
+ * dex_location2,number_of_classes2,methods_region_size,dex_location_checksum2, \
+ * method_encoding_21,method_encoding_22...,,class_id1,class_id2...
* .....
+ * The method_encoding is:
+ * method_id,number_of_inline_caches,inline_cache1,inline_cache2...
+ * The inline_cache is:
+ * dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,...
+ * dex_map_size is the number of dex_indeces that follows.
+ * Classes are grouped per their dex files and the line
+ * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the
+ * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...`
+ * M stands for megamorphic and it's encoded as the byte kMegamorphicEncoding.
+ * When present, there will be no class ids following.
**/
bool ProfileCompilationInfo::Save(int fd) {
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
- // Cache at most 5KB before writing.
- static constexpr size_t kMaxSizeToKeepBeforeWriting = 5 * KB;
+ // Cache at most 50KB before writing.
+ static constexpr size_t kMaxSizeToKeepBeforeWriting = 50 * KB;
// Use a vector wrapper to avoid keeping track of offsets when we add elements.
std::vector<uint8_t> buffer;
WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic));
WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion));
- AddUintToBuffer(&buffer, static_cast<uint16_t>(info_.size()));
+ DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max());
+ AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size()));
for (const auto& it : info_) {
if (buffer.size() > kMaxSizeToKeepBeforeWriting) {
@@ -203,9 +231,9 @@
}
const std::string& dex_location = it.first;
const DexFileData& dex_data = it.second;
- if (dex_data.method_set.empty() && dex_data.class_set.empty()) {
- continue;
- }
+
+ // Note that we allow dex files without any methods or classes, so that
+ // inline caches can refer valid dex files.
if (dex_location.size() >= kMaxDexFileKeyLength) {
LOG(WARNING) << "DexFileKey exceeds allocated limit";
@@ -214,42 +242,128 @@
// Make sure that the buffer has enough capacity to avoid repeated resizings
// while we add data.
+ uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
size_t required_capacity = buffer.size() +
kLineHeaderSize +
dex_location.size() +
- sizeof(uint16_t) * (dex_data.class_set.size() + dex_data.method_set.size());
+ sizeof(uint16_t) * dex_data.class_set.size() +
+ methods_region_size;
buffer.reserve(required_capacity);
-
DCHECK_LE(dex_location.size(), std::numeric_limits<uint16_t>::max());
- DCHECK_LE(dex_data.method_set.size(), std::numeric_limits<uint16_t>::max());
DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_location.size()));
- AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.method_set.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
AddStringToBuffer(&buffer, dex_location);
- for (auto method_it : dex_data.method_set) {
- AddUintToBuffer(&buffer, method_it);
+ for (const auto& method_it : dex_data.method_map) {
+ AddUintToBuffer(&buffer, method_it.first);
+ AddInlineCacheToBuffer(&buffer, method_it.second);
}
- for (auto class_id : dex_data.class_set) {
+ for (const auto& class_id : dex_data.class_set) {
AddUintToBuffer(&buffer, class_id.index_);
}
- DCHECK_EQ(required_capacity, buffer.size())
+
+ DCHECK_LE(required_capacity, buffer.size())
<< "Failed to add the expected number of bytes in the buffer";
}
return WriteBuffer(fd, buffer.data(), buffer.size());
}
+void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
+ const InlineCacheMap& inline_cache_map) {
+ // Add inline cache map size.
+ AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size()));
+ if (inline_cache_map.size() == 0) {
+ return;
+ }
+ for (const auto& inline_cache_it : inline_cache_map) {
+ uint16_t dex_pc = inline_cache_it.first;
+ const DexPcData dex_pc_data = inline_cache_it.second;
+ const ClassSet& classes = dex_pc_data.classes;
+
+ // Add the dex pc.
+ AddUintToBuffer(buffer, dex_pc);
+
+ if (dex_pc_data.is_megamorphic) {
+ // Add the megamorphic encoding if needed and continue.
+ // If megamorphic, we don't add the rest of the classes.
+ AddUintToBuffer(buffer, kMegamorphicEncoding);
+ continue;
+ }
+
+ DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize);
+ DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
+
+ SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
+ // Group the classes by dex. We expect that most of the classes will come from
+ // the same dex, so this will be more efficient than encoding the dex index
+ // for each class reference.
+ GroupClassesByDex(classes, &dex_to_classes_map);
+ // Add the dex map size.
+ AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size()));
+ for (const auto& dex_it : dex_to_classes_map) {
+ uint8_t dex_profile_index = dex_it.first;
+ const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
+ // Add the dex profile index.
+ AddUintToBuffer(buffer, dex_profile_index);
+ // Add the the number of classes for each dex profile index.
+ AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size()));
+ for (size_t i = 0; i < dex_classes.size(); i++) {
+ // Add the type index of the classes.
+ AddUintToBuffer(buffer, dex_classes[i].index_);
+ }
+ }
+ }
+}
+
+uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) {
+ // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods
+ uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size();
+ for (const auto& method_it : dex_data.method_map) {
+ const InlineCacheMap& inline_cache = method_it.second;
+ size += sizeof(uint16_t) * inline_cache.size(); // dex_pc
+ for (const auto& inline_cache_it : inline_cache) {
+ const ClassSet& classes = inline_cache_it.second.classes;
+ SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
+ GroupClassesByDex(classes, &dex_to_classes_map);
+ size += sizeof(uint8_t); // dex_to_classes_map size
+ for (const auto& dex_it : dex_to_classes_map) {
+ size += sizeof(uint8_t); // dex profile index
+ size += sizeof(uint8_t); // number of classes
+ const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
+ size += sizeof(uint16_t) * dex_classes.size(); // the actual classes
+ }
+ }
+ }
+ return size;
+}
+
+void ProfileCompilationInfo::GroupClassesByDex(
+ const ClassSet& classes,
+ /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map) {
+ for (const auto& classes_it : classes) {
+ auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index);
+ dex_it->second.push_back(classes_it.type_index);
+ }
+}
+
ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
const std::string& dex_location,
uint32_t checksum) {
- auto info_it = info_.find(dex_location);
- if (info_it == info_.end()) {
- info_it = info_.Put(dex_location, DexFileData(checksum));
+ auto info_it = info_.FindOrAdd(dex_location, DexFileData(checksum, info_.size()));
+ if (info_.size() > std::numeric_limits<uint8_t>::max()) {
+ // Allow only 255 dex files to be profiled. This allows us to save bytes
+ // when encoding. The number is well above what we expect for normal applications.
+ if (kIsDebugBuild) {
+ LOG(WARNING) << "Exceeded the maximum number of dex files (255). Something went wrong";
+ }
+ info_.erase(dex_location);
+ return nullptr;
}
if (info_it->second.checksum != checksum) {
LOG(WARNING) << "Checksum mismatch for dex " << dex_location;
@@ -270,13 +384,65 @@
}
bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location,
- uint32_t checksum,
- uint16_t method_idx) {
- DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
- if (data == nullptr) {
+ uint32_t dex_checksum,
+ uint16_t method_index) {
+ return AddMethod(dex_location, dex_checksum, method_index, OfflineProfileMethodInfo());
+}
+
+bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t method_index,
+ const OfflineProfileMethodInfo& pmi) {
+ DexFileData* const data = GetOrAddDexFileData(
+ GetProfileDexFileKey(dex_location),
+ dex_checksum);
+ if (data == nullptr) { // checksum mismatch
return false;
}
- data->method_set.insert(method_idx);
+ auto inline_cache_it = data->method_map.FindOrAdd(method_index);
+ for (const auto& pmi_inline_cache_it : pmi.inline_caches) {
+ uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first;
+ const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second;
+ auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(pmi_ic_dex_pc);
+ if (pmi_ic_dex_pc_data.is_megamorphic) {
+ dex_pc_data_it->second.SetMegamorphic();
+ continue;
+ }
+ for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) {
+ 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);
+ if (class_dex_data == nullptr) { // checksum mismatch
+ return false;
+ }
+ dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index);
+ }
+ }
+ return true;
+}
+
+bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
+ DexFileData* const data = GetOrAddDexFileData(
+ GetProfileDexFileKey(pmi.dex_file->GetLocation()),
+ pmi.dex_file->GetLocationChecksum());
+ if (data == nullptr) { // checksum mismatch
+ return false;
+ }
+ auto inline_cache_it = data->method_map.FindOrAdd(pmi.dex_method_index);
+
+ for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
+ for (const ProfileMethodInfo::ProfileClassReference& class_ref : cache.classes) {
+ DexFileData* class_dex_data = GetOrAddDexFileData(
+ GetProfileDexFileKey(class_ref.dex_file->GetLocation()),
+ class_ref.dex_file->GetLocationChecksum());
+ if (class_dex_data == nullptr) { // checksum mismatch
+ return false;
+ }
+ auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(cache.dex_pc);
+ dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index);
+ }
+ }
return true;
}
@@ -291,21 +457,79 @@
return true;
}
-bool ProfileCompilationInfo::ProcessLine(SafeBuffer& line_buffer,
- uint16_t method_set_size,
- uint16_t class_set_size,
- uint32_t checksum,
- const std::string& dex_location) {
- for (uint16_t i = 0; i < method_set_size; i++) {
- uint16_t method_idx = line_buffer.ReadUintAndAdvance<uint16_t>();
- if (!AddMethodIndex(dex_location, checksum, method_idx)) {
+#define READ_UINT(type, buffer, dest, error) \
+ do { \
+ if (!buffer.ReadUintAndAdvance<type>(&dest)) { \
+ *error = "Could not read "#dest; \
+ return false; \
+ } \
+ } \
+ while (false)
+
+bool ProfileCompilationInfo::ReadInlineCache(SafeBuffer& buffer,
+ uint8_t number_of_dex_files,
+ /*out*/ InlineCacheMap* inline_cache,
+ /*out*/ std::string* error) {
+ uint16_t inline_cache_size;
+ READ_UINT(uint16_t, buffer, inline_cache_size, error);
+ for (; inline_cache_size > 0; inline_cache_size--) {
+ uint16_t dex_pc;
+ uint8_t dex_to_classes_map_size;
+ READ_UINT(uint16_t, buffer, dex_pc, error);
+ READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
+ auto dex_pc_data_it = inline_cache->FindOrAdd(dex_pc);
+ if (dex_to_classes_map_size == kMegamorphicEncoding) {
+ dex_pc_data_it->second.SetMegamorphic();
+ continue;
+ }
+ for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
+ uint8_t dex_profile_index;
+ uint8_t dex_classes_size;
+ READ_UINT(uint8_t, buffer, dex_profile_index, error);
+ READ_UINT(uint8_t, buffer, dex_classes_size, error);
+ if (dex_profile_index >= number_of_dex_files) {
+ *error = "dex_profile_index out of bounds ";
+ *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files);
+ return false;
+ }
+ for (; dex_classes_size > 0; dex_classes_size--) {
+ uint16_t type_index;
+ READ_UINT(uint16_t, buffer, type_index, error);
+ dex_pc_data_it->second.AddClass(dex_profile_index, dex::TypeIndex(type_index));
+ }
+ }
+ }
+ return true;
+}
+
+bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer,
+ uint8_t number_of_dex_files,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error) {
+ while (buffer.HasMoreData()) {
+ DexFileData* const data = GetOrAddDexFileData(line_header.dex_location, line_header.checksum);
+ uint16_t method_index;
+ READ_UINT(uint16_t, buffer, method_index, error);
+
+ auto it = data->method_map.FindOrAdd(method_index);
+ if (!ReadInlineCache(buffer, number_of_dex_files, &(it->second), error)) {
return false;
}
}
- for (uint16_t i = 0; i < class_set_size; i++) {
- uint16_t type_idx = line_buffer.ReadUintAndAdvance<uint16_t>();
- if (!AddClassIndex(dex_location, checksum, dex::TypeIndex(type_idx))) {
+ return true;
+}
+
+bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
+ uint16_t classes_to_read,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error) {
+ for (uint16_t i = 0; i < classes_to_read; i++) {
+ uint16_t type_index;
+ READ_UINT(uint16_t, buffer, type_index, error);
+ if (!AddClassIndex(line_header.dex_location,
+ line_header.checksum,
+ dex::TypeIndex(type_index))) {
return false;
}
}
@@ -324,15 +548,17 @@
// Reads an uint value previously written with AddUintToBuffer.
template <typename T>
-T ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance() {
+bool ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance(/*out*/T* value) {
static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
- CHECK_LE(ptr_current_ + sizeof(T), ptr_end_);
- T value = 0;
+ if (ptr_current_ + sizeof(T) > ptr_end_) {
+ return false;
+ }
+ *value = 0;
for (size_t i = 0; i < sizeof(T); i++) {
- value += ptr_current_[i] << (i * kBitsPerByte);
+ *value += ptr_current_[i] << (i * kBitsPerByte);
}
ptr_current_ += sizeof(T);
- return value;
+ return true;
}
bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) {
@@ -346,6 +572,10 @@
return false;
}
+bool ProfileCompilationInfo::SafeBuffer::HasMoreData() {
+ return ptr_current_ < ptr_end_;
+}
+
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
int fd,
const std::string& source,
@@ -369,13 +599,13 @@
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
int fd,
- /*out*/uint16_t* number_of_lines,
+ /*out*/uint8_t* number_of_dex_files,
/*out*/std::string* error) {
// Read magic and version
const size_t kMagicVersionSize =
sizeof(kProfileMagic) +
sizeof(kProfileVersion) +
- sizeof(uint16_t); // number of lines
+ sizeof(uint8_t); // number of dex files
SafeBuffer safe_buffer(kMagicVersionSize);
@@ -392,24 +622,38 @@
*error = "Profile version mismatch";
return kProfileLoadVersionMismatch;
}
- *number_of_lines = safe_buffer.ReadUintAndAdvance<uint16_t>();
+ if (!safe_buffer.ReadUintAndAdvance<uint8_t>(number_of_dex_files)) {
+ *error = "Cannot read the number of dex files";
+ return kProfileLoadBadData;
+ }
return kProfileLoadSuccess;
}
+bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer,
+ /*out*/uint16_t* dex_location_size,
+ /*out*/ProfileLineHeader* line_header,
+ /*out*/std::string* error) {
+ READ_UINT(uint16_t, buffer, *dex_location_size, error);
+ 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);
+ return true;
+}
+
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
int fd,
/*out*/ProfileLineHeader* line_header,
/*out*/std::string* error) {
SafeBuffer header_buffer(kLineHeaderSize);
- ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileHeader", error);
+ ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileLineHeader", error);
if (status != kProfileLoadSuccess) {
return status;
}
- uint16_t dex_location_size = header_buffer.ReadUintAndAdvance<uint16_t>();
- line_header->method_set_size = header_buffer.ReadUintAndAdvance<uint16_t>();
- line_header->class_set_size = header_buffer.ReadUintAndAdvance<uint16_t>();
- line_header->checksum = header_buffer.ReadUintAndAdvance<uint32_t>();
+ uint16_t dex_location_size;
+ if (!ReadProfileLineHeaderElements(header_buffer, &dex_location_size, line_header, error)) {
+ return kProfileLoadBadData;
+ }
if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
*error = "DexFileKey has an invalid size: " +
@@ -429,37 +673,38 @@
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
int fd,
+ uint8_t number_of_dex_files,
const ProfileLineHeader& line_header,
/*out*/std::string* error) {
- // Make sure that we don't try to read everything in memory (in case the profile if full).
- // Split readings in chunks of at most 10kb.
- static constexpr uint16_t kMaxNumberOfEntriesToRead = 5120;
- uint16_t methods_left_to_read = line_header.method_set_size;
- uint16_t classes_left_to_read = line_header.class_set_size;
+ if (GetOrAddDexFileData(line_header.dex_location, line_header.checksum) == nullptr) {
+ *error = "Error when reading profile file line header: checksum mismatch for "
+ + line_header.dex_location;
+ return kProfileLoadBadData;
+ }
- while ((methods_left_to_read > 0) || (classes_left_to_read > 0)) {
- uint16_t methods_to_read = std::min(kMaxNumberOfEntriesToRead, methods_left_to_read);
- uint16_t max_classes_to_read = kMaxNumberOfEntriesToRead - methods_to_read;
- uint16_t classes_to_read = std::min(max_classes_to_read, classes_left_to_read);
-
- size_t line_size = sizeof(uint16_t) * (methods_to_read + classes_to_read);
- SafeBuffer line_buffer(line_size);
-
- ProfileLoadSatus status = line_buffer.FillFromFd(fd, "ReadProfileLine", error);
+ {
+ SafeBuffer buffer(line_header.method_region_size_bytes);
+ ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineMethods", error);
if (status != kProfileLoadSuccess) {
return status;
}
- if (!ProcessLine(line_buffer,
- methods_to_read,
- classes_to_read,
- line_header.checksum,
- line_header.dex_location)) {
- *error = "Error when reading profile file line";
+
+ if (!ReadMethods(buffer, number_of_dex_files, line_header, error)) {
return kProfileLoadBadData;
}
- methods_left_to_read -= methods_to_read;
- classes_left_to_read -= classes_to_read;
}
+
+ {
+ SafeBuffer buffer(sizeof(uint16_t) * line_header.class_set_size);
+ ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineClasses", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+ if (!ReadClasses(buffer, line_header.class_set_size, line_header, error)) {
+ return kProfileLoadBadData;
+ }
+ }
+
return kProfileLoadSuccess;
}
@@ -470,7 +715,7 @@
if (status == kProfileLoadSuccess) {
return true;
} else {
- PLOG(WARNING) << "Error when reading profile " << error;
+ LOG(WARNING) << "Error when reading profile: " << error;
return false;
}
}
@@ -490,15 +735,16 @@
if (stat_buffer.st_size == 0) {
return kProfileLoadSuccess;
}
- // Read profile header: magic + version + number_of_lines.
- uint16_t number_of_lines;
- ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_lines, error);
+ // Read profile header: magic + version + number_of_dex_files.
+ uint8_t number_of_dex_files;
+ ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_dex_files, error);
if (status != kProfileLoadSuccess) {
return status;
}
- while (number_of_lines > 0) {
+ for (uint8_t k = 0; k < number_of_dex_files; k++) {
ProfileLineHeader line_header;
+
// First, read the line header to get the amount of data we need to read.
status = ReadProfileLineHeader(fd, &line_header, error);
if (status != kProfileLoadSuccess) {
@@ -506,11 +752,10 @@
}
// Now read the actual profile line.
- status = ReadProfileLine(fd, line_header, error);
+ status = ReadProfileLine(fd, number_of_dex_files, line_header, error);
if (status != kProfileLoadSuccess) {
return status;
}
- number_of_lines--;
}
// Check that we read everything and that profiles don't contain junk data.
@@ -538,37 +783,112 @@
}
}
// All checksums match. Import the data.
+
+ // The other profile might have a different indexing of dex files.
+ // That is because each dex files gets a 'dex_profile_index' on a first come first served basis.
+ // That means that the order in with the methods are added to the profile matters for the
+ // actual indices.
+ // The reason we cannot rely on the actual multidex index is that a single profile may store
+ // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
+ // and one from split-B.
+
+ // First, build a mapping from other_dex_profile_index to this_dex_profile_index.
+ // This will make sure that the ClassReferences will point to the correct dex file.
+ SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
+ for (const auto& other_it : other.info_) {
+ const std::string& other_dex_location = other_it.first;
+ const DexFileData& other_dex_data = other_it.second;
+ auto info_it = info_.FindOrAdd(other_dex_location, DexFileData(other_dex_data.checksum, 0));
+ const DexFileData& dex_data = info_it->second;
+ dex_profile_index_remap.Put(other_dex_data.profile_index, dex_data.profile_index);
+ }
+
+ // Merge the actual profile data.
for (const auto& other_it : other.info_) {
const std::string& other_dex_location = other_it.first;
const DexFileData& other_dex_data = other_it.second;
auto info_it = info_.find(other_dex_location);
- if (info_it == info_.end()) {
- info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum));
- }
- info_it->second.method_set.insert(other_dex_data.method_set.begin(),
- other_dex_data.method_set.end());
+ DCHECK(info_it != info_.end());
+
+ // Merge the classes.
info_it->second.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) {
+ uint16_t other_method_index = other_method_it.first;
+ auto method_it = info_it->second.method_map.FindOrAdd(other_method_index);
+ const auto& other_inline_cache = other_method_it.second;
+ for (const auto& other_ic_it : other_inline_cache) {
+ uint16_t other_dex_pc = other_ic_it.first;
+ const ClassSet& other_class_set = other_ic_it.second.classes;
+ auto class_set = method_it->second.FindOrAdd(other_dex_pc);
+ for (const auto& class_it : other_class_set) {
+ class_set->second.AddClass(dex_profile_index_remap.Get(
+ class_it.dex_profile_index), class_it.type_index);
+ }
+ }
+ }
}
return true;
}
+static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) {
+ return kDebugIgnoreChecksum || dex_file_checksum == checksum;
+}
+
static bool ChecksumMatch(const DexFile& dex_file, uint32_t checksum) {
- return kDebugIgnoreChecksum || dex_file.GetLocationChecksum() == checksum;
+ return ChecksumMatch(dex_file.GetLocationChecksum(), checksum);
}
bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
- auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation()));
- if (info_it != info_.end()) {
- if (!ChecksumMatch(*method_ref.dex_file, info_it->second.checksum)) {
- return false;
- }
- const std::set<uint16_t>& methods = info_it->second.method_set;
- return methods.find(method_ref.dex_method_index) != methods.end();
- }
- return false;
+ return FindMethod(method_ref.dex_file->GetLocation(),
+ method_ref.dex_file->GetLocationChecksum(),
+ method_ref.dex_method_index) != nullptr;
}
+const ProfileCompilationInfo::InlineCacheMap*
+ProfileCompilationInfo::FindMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t dex_method_index) const {
+ auto info_it = info_.find(GetProfileDexFileKey(dex_location));
+ if (info_it != info_.end()) {
+ if (!ChecksumMatch(dex_checksum, info_it->second.checksum)) {
+ return nullptr;
+ }
+ const MethodMap& methods = info_it->second.method_map;
+ const auto method_it = methods.find(dex_method_index);
+ return method_it == methods.end() ? nullptr : &(method_it->second);
+ }
+ return nullptr;
+}
+
+void ProfileCompilationInfo::DexFileToProfileIndex(
+ /*out*/std::vector<DexReference>* dex_references) const {
+ dex_references->resize(info_.size());
+ for (const auto& info_it : info_) {
+ DexReference& dex_ref = (*dex_references)[info_it.second.profile_index];
+ dex_ref.dex_location = info_it.first;
+ dex_ref.dex_checksum = info_it.second.checksum;
+ }
+}
+
+bool ProfileCompilationInfo::GetMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t dex_method_index,
+ /*out*/OfflineProfileMethodInfo* pmi) const {
+ const InlineCacheMap* inline_caches = FindMethod(dex_location, dex_checksum, dex_method_index);
+ if (inline_caches == nullptr) {
+ return false;
+ }
+
+ DexFileToProfileIndex(&pmi->dex_references);
+ // TODO(calin): maybe expose a direct pointer to avoid copying
+ pmi->inline_caches = *inline_caches;
+ return true;
+}
+
+
bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const {
auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation()));
if (info_it != info_.end()) {
@@ -584,7 +904,7 @@
uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
uint32_t total = 0;
for (const auto& it : info_) {
- total += it.second.method_set.size();
+ total += it.second.method_map.size();
}
return total;
}
@@ -645,19 +965,34 @@
}
}
os << "\n\tmethods: ";
- for (const auto method_it : dex_data.method_set) {
+ for (const auto method_it : dex_data.method_map) {
if (dex_file != nullptr) {
- os << "\n\t\t" << dex_file->PrettyMethod(method_it, true);
+ os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
} else {
- os << method_it << ",";
+ os << method_it.first;
}
+
+ os << "[";
+ for (const auto& inline_cache_it : method_it.second) {
+ os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
+ if (inline_cache_it.second.is_megamorphic) {
+ os << "M";
+ } else {
+ for (const ClassReference& class_ref : inline_cache_it.second.classes) {
+ os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
+ << "," << class_ref.type_index.index_ << ")";
+ }
+ }
+ os << "}";
+ }
+ os << "], ";
}
os << "\n\tclasses: ";
for (const auto class_it : dex_data.class_set) {
if (dex_file != nullptr) {
os << "\n\t\t" << dex_file->PrettyType(class_it);
} else {
- os << class_it << ",";
+ os << class_it.index_ << ",";
}
}
}
@@ -762,4 +1097,44 @@
return info.Save(fd);
}
+bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
+ const OfflineProfileMethodInfo& other) const {
+ if (inline_caches.size() != other.inline_caches.size()) {
+ return false;
+ }
+
+ // We can't use a simple equality test because we need to match the dex files
+ // of the inline caches which might have different profile indices.
+ for (const auto& inline_cache_it : inline_caches) {
+ uint16_t dex_pc = inline_cache_it.first;
+ const DexPcData dex_pc_data = inline_cache_it.second;
+ const auto other_it = other.inline_caches.find(dex_pc);
+ if (other_it == other.inline_caches.end()) {
+ return false;
+ }
+ const DexPcData& other_dex_pc_data = other_it->second;
+ if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic) {
+ return false;
+ }
+ for (const ClassReference& class_ref : dex_pc_data.classes) {
+ bool found = false;
+ for (const ClassReference& other_class_ref : other_dex_pc_data.classes) {
+ CHECK_LE(class_ref.dex_profile_index, dex_references.size());
+ CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size());
+ const DexReference& dex_ref = dex_references[class_ref.dex_profile_index];
+ const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index];
+ if (class_ref.type_index == other_class_ref.type_index &&
+ dex_ref == other_dex_ref) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
} // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 758b46d..4bfbfcd 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -31,6 +31,40 @@
namespace art {
/**
+ * Convenient class to pass around profile information (including inline caches)
+ * without the need to hold GC-able objects.
+ */
+struct ProfileMethodInfo {
+ struct ProfileClassReference {
+ ProfileClassReference(const DexFile* dex, const dex::TypeIndex& index)
+ : dex_file(dex), type_index(index) {}
+
+ const DexFile* dex_file;
+ const dex::TypeIndex type_index;
+ };
+
+ struct ProfileInlineCache {
+ ProfileInlineCache(uint32_t pc, const std::vector<ProfileClassReference>& profile_classes)
+ : dex_pc(pc), classes(profile_classes) {}
+
+ const uint32_t dex_pc;
+ const std::vector<ProfileClassReference> classes;
+ };
+
+ ProfileMethodInfo(const DexFile* dex, uint32_t method_index)
+ : dex_file(dex), dex_method_index(method_index) {}
+
+ ProfileMethodInfo(const DexFile* dex,
+ uint32_t method_index,
+ const std::vector<ProfileInlineCache>& caches)
+ : dex_file(dex), dex_method_index(method_index), inline_caches(caches) {}
+
+ const DexFile* dex_file;
+ const uint32_t dex_method_index;
+ const std::vector<ProfileInlineCache> inline_caches;
+};
+
+/**
* Profile information in a format suitable to be queried by the compiler and
* performing profile guided compilation.
* It is a serialize-friendly format based on information collected by the
@@ -42,34 +76,130 @@
static const uint8_t kProfileMagic[];
static const uint8_t kProfileVersion[];
+ // Data structures for encoding the offline representation of inline caches.
+ // This is exposed as public in order to make it available to dex2oat compilations
+ // (see compiler/optimizing/inliner.cc).
+
+ // A dex location together with its checksum.
+ struct DexReference {
+ DexReference() {}
+
+ DexReference(const std::string& location, uint32_t checksum)
+ : dex_location(location), dex_checksum(checksum) {}
+
+ bool operator==(const DexReference& other) const {
+ return dex_checksum == other.dex_checksum && dex_location == other.dex_location;
+ }
+
+ std::string dex_location;
+ uint32_t dex_checksum;
+ };
+
+ // Encodes a class reference in the profile.
+ // The owning dex file is encoded as the index (dex_profile_index) it has in the
+ // profile rather than as a full DexRefence(location,checksum).
+ // This avoids excessive string copying when managing the profile data.
+ // The dex_profile_index is an index in either of:
+ // - OfflineProfileMethodInfo#dex_references vector (public use)
+ // - DexFileData#profile_index (internal use).
+ // Note that the dex_profile_index is not necessary the multidex index.
+ // We cannot rely on the actual multidex index because a single profile may store
+ // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
+ // and one from split-B.
+ struct ClassReference {
+ ClassReference(uint8_t dex_profile_idx, const dex::TypeIndex& type_idx) :
+ dex_profile_index(dex_profile_idx), type_index(type_idx) {}
+
+ bool operator==(const ClassReference& other) const {
+ return dex_profile_index == other.dex_profile_index && type_index == other.type_index;
+ }
+ bool operator<(const ClassReference& other) const {
+ return dex_profile_index == other.dex_profile_index
+ ? type_index < other.type_index
+ : dex_profile_index < other.dex_profile_index;
+ }
+
+ uint8_t dex_profile_index; // the index of the owning dex in the profile info
+ dex::TypeIndex type_index; // the type index of the class
+ };
+
+ // The set of classes that can be found at a given dex pc.
+ using ClassSet = std::set<ClassReference>;
+
+ // Encodes the actual inline cache for a given dex pc (whether or not the receiver is
+ // megamorphic and its possible types).
+ // If the receiver is megamorphic the set of classes will be empty.
+ struct DexPcData {
+ DexPcData() : is_megamorphic(false) {}
+ void AddClass(uint16_t dex_profile_idx, const dex::TypeIndex& type_idx);
+ void SetMegamorphic() {
+ is_megamorphic = true;
+ classes.clear();
+ }
+ bool operator==(const DexPcData& other) const {
+ return is_megamorphic == other.is_megamorphic && classes == other.classes;
+ }
+
+ bool is_megamorphic;
+ ClassSet classes;
+ };
+
+ // The inline cache map: DexPc -> DexPcData.
+ using InlineCacheMap = SafeMap<uint16_t, DexPcData>;
+
+ // Encodes the full set of inline caches for a given method.
+ // The dex_references vector is indexed according to the ClassReference::dex_profile_index.
+ // i.e. the dex file of any ClassReference present in the inline caches can be found at
+ // dex_references[ClassReference::dex_profile_index].
+ struct OfflineProfileMethodInfo {
+ bool operator==(const OfflineProfileMethodInfo& other) const;
+
+ std::vector<DexReference> dex_references;
+ InlineCacheMap inline_caches;
+ };
+
+ // Public methods to create, extend or query the profile.
+
// Add the given methods and classes to the current profile object.
- bool AddMethodsAndClasses(const std::vector<MethodReference>& methods,
+ bool AddMethodsAndClasses(const std::vector<ProfileMethodInfo>& methods,
const std::set<DexCacheResolvedClasses>& resolved_classes);
- // Loads profile information from the given file descriptor.
+
+ // Load profile information from the given file descriptor.
bool Load(int fd);
+
// Merge the data from another ProfileCompilationInfo into the current object.
bool MergeWith(const ProfileCompilationInfo& info);
- // Saves the profile data to the given file descriptor.
+
+ // Save the profile data to the given file descriptor.
bool Save(int fd);
- // Loads and merges profile information from the given file into the current
+
+ // Load and merge profile information from the given file into the current
// object and tries to save it back to disk.
// If `force` is true then the save will go through even if the given file
// has bad data or its version does not match. In this cases the profile content
// is ignored.
bool MergeAndSave(const std::string& filename, uint64_t* bytes_written, bool force);
- // Returns the number of methods that were profiled.
+ // Return the number of methods that were profiled.
uint32_t GetNumberOfMethods() const;
- // Returns the number of resolved classes that were profiled.
+
+ // Return the number of resolved classes that were profiled.
uint32_t GetNumberOfResolvedClasses() const;
- // Returns true if the method reference is present in the profiling info.
+ // Return true if the method reference is present in the profiling info.
bool ContainsMethod(const MethodReference& method_ref) const;
- // Returns true if the class's type is present in the profiling info.
+ // Return true if the class's type is present in the profiling info.
bool ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const;
- // Dumps all the loaded profile info into a string and returns it.
+ // Return true if the method is present in the profiling info.
+ // If the method is found, `pmi` is populated with its inline caches.
+ bool GetMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t dex_method_index,
+ /*out*/OfflineProfileMethodInfo* pmi) const;
+
+ // Dump all the loaded profile info into a string and returns it.
// If dex_files is not null then the method indices will be resolved to their
// names.
// This is intended for testing and debugging.
@@ -80,26 +210,35 @@
void GetClassNames(const std::vector<std::unique_ptr<const DexFile>>* dex_files,
std::set<std::string>* class_names) const;
+
void GetClassNames(const std::vector<const DexFile*>* dex_files,
std::set<std::string>* class_names) const;
+ // Perform an equality test with the `other` profile information.
bool Equals(const ProfileCompilationInfo& other);
- static std::string GetProfileDexFileKey(const std::string& dex_location);
-
- // Returns the class descriptors for all of the classes in the profiles' class sets.
+ // Return the class descriptors for all of the classes in the profiles' class sets.
// Note the dex location is actually the profile key, the caller needs to call back in to the
// profile info stuff to generate a map back to the dex location.
std::set<DexCacheResolvedClasses> GetResolvedClasses() const;
- // Clears the resolved classes from the current object.
+ // Clear the resolved classes from the current object.
void ClearResolvedClasses();
+ // Return the profile key associated with the given dex location.
+ static std::string GetProfileDexFileKey(const std::string& dex_location);
+
+ // Generate a test profile which will contain a percentage of the total maximum
+ // number of methods and classes (method_ratio and class_ratio).
static bool GenerateTestProfile(int fd,
uint16_t number_of_dex_files,
uint16_t method_ratio,
uint16_t class_ratio);
+ // Check that the given profile method info contain the same data.
+ static bool Equals(const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi1,
+ const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi2);
+
private:
enum ProfileLoadSatus {
kProfileLoadIOError,
@@ -108,30 +247,71 @@
kProfileLoadSuccess
};
+ // Maps a method dex index to its inline cache.
+ using MethodMap = SafeMap<uint16_t, InlineCacheMap>;
+
+ // Internal representation of the profile information belonging to a dex file.
struct DexFileData {
- explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
+ DexFileData(uint32_t location_checksum, uint16_t index)
+ : profile_index(index), checksum(location_checksum) {}
+ // The profile index of this dex file (matches ClassReference#dex_profile_index)
+ uint8_t profile_index;
+ // The dex checksum
uint32_t checksum;
- std::set<uint16_t> method_set;
+ // The methonds' profile information
+ MethodMap method_map;
+ // 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.
std::set<dex::TypeIndex> class_set;
bool operator==(const DexFileData& other) const {
- return checksum == other.checksum && method_set == other.method_set;
+ return checksum == other.checksum && method_map == other.method_map;
}
};
+ // Maps dex file to their profile information.
using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>;
+ // Return the profile data for the given dex location or null if the dex location
+ // already exists but has a different checksum
DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum);
+
+ // Add a method index to the profile (without inline caches).
bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
+
+ // Add a method to the profile using its online representation (containing runtime structures).
+ bool AddMethod(const ProfileMethodInfo& pmi);
+
+ // 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,
+ const OfflineProfileMethodInfo& pmi);
+
+ // Add a class index to the profile.
bool AddClassIndex(const std::string& dex_location, uint32_t checksum, dex::TypeIndex type_idx);
+
+ // Add all classes from the given dex cache to the the profile.
bool AddResolvedClasses(const DexCacheResolvedClasses& classes);
+ // Search for the given method in the profile.
+ // If found, its inline cache map is returned, otherwise the method returns null.
+ const InlineCacheMap* FindMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t dex_method_index) const;
+
+ // Encode the known dex_files into a vector. The index of a dex_reference will
+ // be the same as the profile index of the dex file (used to encode the ClassReferences).
+ void DexFileToProfileIndex(/*out*/std::vector<DexReference>* dex_references) const;
+
// Parsing functionality.
+ // The information present in the header of each profile line.
struct ProfileLineHeader {
std::string dex_location;
- uint16_t method_set_size;
uint16_t class_set_size;
+ uint32_t method_region_size_bytes;
uint32_t checksum;
};
@@ -150,12 +330,15 @@
// Reads an uint value (high bits to low bits) and advances the current pointer
// with the number of bits read.
- template <typename T> T ReadUintAndAdvance();
+ template <typename T> bool ReadUintAndAdvance(/*out*/ T* value);
// Compares the given data with the content current pointer. If the contents are
// equal it advances the current pointer by data_size.
bool CompareAndAdvance(const uint8_t* data, size_t data_size);
+ // Returns true if the buffer has more data to read.
+ bool HasMoreData();
+
// Get the underlying raw buffer.
uint8_t* Get() { return storage_.get(); }
@@ -165,24 +348,63 @@
uint8_t* ptr_end_;
};
+ // Entry point for profile loding functionality.
ProfileLoadSatus LoadInternal(int fd, std::string* error);
+ // Read the profile header from the given fd and store the number of profile
+ // lines into number_of_dex_files.
ProfileLoadSatus ReadProfileHeader(int fd,
- /*out*/uint16_t* number_of_lines,
+ /*out*/uint8_t* number_of_dex_files,
/*out*/std::string* error);
+ // Read the header of a profile line from the given fd.
ProfileLoadSatus ReadProfileLineHeader(int fd,
/*out*/ProfileLineHeader* line_header,
/*out*/std::string* error);
+
+ // Read individual elements from the profile line header.
+ bool ReadProfileLineHeaderElements(SafeBuffer& buffer,
+ /*out*/uint16_t* dex_location_size,
+ /*out*/ProfileLineHeader* line_header,
+ /*out*/std::string* error);
+
+ // Read a single profile line from the given fd.
ProfileLoadSatus ReadProfileLine(int fd,
+ uint8_t number_of_dex_files,
const ProfileLineHeader& line_header,
/*out*/std::string* error);
- bool ProcessLine(SafeBuffer& line_buffer,
- uint16_t method_set_size,
- uint16_t class_set_size,
- uint32_t checksum,
- const std::string& dex_location);
+ // Read all the classes from the buffer into the profile `info_` structure.
+ bool ReadClasses(SafeBuffer& buffer,
+ uint16_t classes_to_read,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error);
+
+ // Read all the methods from the buffer into the profile `info_` structure.
+ bool ReadMethods(SafeBuffer& buffer,
+ uint8_t number_of_dex_files,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error);
+
+ // Read the inline cache encoding from line_bufer into inline_cache.
+ bool ReadInlineCache(SafeBuffer& buffer,
+ uint8_t number_of_dex_files,
+ /*out*/InlineCacheMap* inline_cache,
+ /*out*/std::string* error);
+
+ // Encode the inline cache into the given buffer.
+ void AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
+ const InlineCacheMap& inline_cache);
+
+ // Return the number of bytes needed to encode the profile information
+ // for the methods in dex_data.
+ uint32_t GetMethodsRegionSize(const DexFileData& dex_data);
+
+ // Group `classes` by their owning dex profile index and put the result in
+ // `dex_to_classes_map`.
+ void GroupClassesByDex(
+ const ClassSet& classes,
+ /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map);
friend class ProfileCompilationInfoTest;
friend class CompilerDriverProfileTest;
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 835a5f3..93b47ac 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -57,6 +57,14 @@
return info->AddMethodIndex(dex_location, checksum, method_index);
}
+ bool AddMethod(const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t method_index,
+ const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi,
+ ProfileCompilationInfo* info) {
+ return info->AddMethod(dex_location, checksum, method_index, pmi);
+ }
+
bool AddClass(const std::string& dex_location,
uint32_t checksum,
uint16_t class_index,
@@ -73,17 +81,132 @@
const std::vector<ArtMethod*>& methods,
const std::set<DexCacheResolvedClasses>& resolved_classes) {
ProfileCompilationInfo info;
- std::vector<MethodReference> method_refs;
+ std::vector<ProfileMethodInfo> profile_methods;
ScopedObjectAccess soa(Thread::Current());
for (ArtMethod* method : methods) {
- method_refs.emplace_back(method->GetDexFile(), method->GetDexMethodIndex());
+ profile_methods.emplace_back(method->GetDexFile(), method->GetDexMethodIndex());
}
- if (!info.AddMethodsAndClasses(method_refs, resolved_classes)) {
+ if (!info.AddMethodsAndClasses(profile_methods, resolved_classes)) {
+ return false;
+ }
+ if (info.GetNumberOfMethods() != profile_methods.size()) {
return false;
}
return info.MergeAndSave(filename, nullptr, false);
}
+ // Saves the given art methods to a profile backed by 'filename' and adds
+ // some fake inline caches to it. The added inline caches are returned in
+ // the out map `profile_methods_map`.
+ bool SaveProfilingInfoWithFakeInlineCaches(
+ const std::string& filename,
+ const std::vector<ArtMethod*>& methods,
+ /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
+ ProfileCompilationInfo info;
+ std::vector<ProfileMethodInfo> profile_methods;
+ ScopedObjectAccess soa(Thread::Current());
+ for (ArtMethod* method : methods) {
+ std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
+ // Monomorphic
+ for (uint16_t dex_pc = 0; dex_pc < 1; dex_pc++) {
+ std::vector<ProfileMethodInfo::ProfileClassReference> classes;
+ classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
+ caches.emplace_back(dex_pc, classes);
+ }
+ // Polymorphic
+ for (uint16_t dex_pc = 1; dex_pc < 2; dex_pc++) {
+ std::vector<ProfileMethodInfo::ProfileClassReference> classes;
+ for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
+ classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
+ }
+ caches.emplace_back(dex_pc, classes);
+ }
+ // Megamorphic
+ for (uint16_t dex_pc = 2; dex_pc < 3; dex_pc++) {
+ std::vector<ProfileMethodInfo::ProfileClassReference> classes;
+ for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
+ classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
+ }
+ caches.emplace_back(dex_pc, classes);
+ }
+ ProfileMethodInfo pmi(method->GetDexFile(), method->GetDexMethodIndex(), caches);
+ profile_methods.push_back(pmi);
+ profile_methods_map->Put(method, pmi);
+ }
+
+ if (!info.AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>())) {
+ return false;
+ }
+ if (info.GetNumberOfMethods() != profile_methods.size()) {
+ return false;
+ }
+ return info.MergeAndSave(filename, nullptr, false);
+ }
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
+ const ProfileMethodInfo& pmi) {
+ ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi;
+ SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index
+ for (const auto& inline_cache : pmi.inline_caches) {
+ for (const auto& class_ref : inline_cache.classes) {
+ uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file),
+ static_cast<uint8_t>(dex_map.size()))->second;
+ offline_pmi.inline_caches
+ .FindOrAdd(inline_cache.dex_pc)->second
+ .AddClass(dex_profile_index, class_ref.type_index);
+ if (dex_profile_index >= offline_pmi.dex_references.size()) {
+ // This is a new dex.
+ 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());
+ }
+ }
+ }
+ return offline_pmi;
+ }
+
+ // Creates an offline profile used for testing inline caches.
+ ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() {
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+
+ 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);
+
+ // Monomorphic
+ for (uint16_t dex_pc = 0; dex_pc < 1; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.AddClass(0, dex::TypeIndex(0));
+ pmi.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+ // Polymorphic
+ for (uint16_t dex_pc = 1; dex_pc < 2; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.AddClass(0, dex::TypeIndex(0));
+ dex_pc_data.AddClass(1, dex::TypeIndex(1));
+ dex_pc_data.AddClass(2, dex::TypeIndex(2));
+
+ pmi.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+ // Megamorphic
+ for (uint16_t dex_pc = 2; dex_pc < 3; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.is_megamorphic = true;
+ pmi.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+
+ return pmi;
+ }
+
+ void MakeMegamorphic(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
+ for (auto it : pmi->inline_caches) {
+ for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) {
+ it.second.AddClass(0, dex::TypeIndex(k));
+ }
+ }
+ }
+
// Cannot sizeof the actual arrays so hardcode the values here.
// They should not change anyway.
static constexpr int kProfileMagicSize = 4;
@@ -235,12 +358,12 @@
TEST_F(ProfileCompilationInfoTest, LoadEmpty) {
ScratchFile profile;
- ProfileCompilationInfo empyt_info;
+ ProfileCompilationInfo empty_info;
ProfileCompilationInfo loaded_info;
ASSERT_TRUE(profile.GetFile()->ResetOffset());
ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
- ASSERT_TRUE(loaded_info.Equals(empyt_info));
+ ASSERT_TRUE(loaded_info.Equals(empty_info));
}
TEST_F(ProfileCompilationInfoTest, BadMagic) {
@@ -324,4 +447,214 @@
ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
}
+TEST_F(ProfileCompilationInfoTest, SaveInlineCaches) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
+
+ // Add methods with inline caches.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ // Add a method which is part of the same dex file as one of the
+ // class from the inline caches.
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
+ // Add a method which is outside the set of dex files.
+ ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
+ }
+
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+
+ ASSERT_TRUE(loaded_info.Equals(saved_info));
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
+ ASSERT_TRUE(loaded_info.GetMethod("dex_location1",
+ /* checksum */ 1,
+ /* method_idx */ 3,
+ &loaded_pmi1));
+ ASSERT_TRUE(loaded_pmi1 == pmi);
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi2;
+ ASSERT_TRUE(loaded_info.GetMethod("dex_location4",
+ /* checksum */ 4,
+ /* method_idx */ 3,
+ &loaded_pmi2));
+ ASSERT_TRUE(loaded_pmi2 == pmi);
+}
+
+TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
+
+ // Add methods with inline caches.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
+ }
+
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Make the inline caches megamorphic and add them to the profile again.
+ ProfileCompilationInfo saved_info_extra;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo();
+ MakeMegamorphic(&pmi_extra);
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
+ }
+
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(saved_info_extra.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Merge the profiles so that we have the same view as the file.
+ ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+
+ ASSERT_TRUE(loaded_info.Equals(saved_info));
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
+ ASSERT_TRUE(loaded_info.GetMethod("dex_location1",
+ /* checksum */ 1,
+ /* method_idx */ 3,
+ &loaded_pmi1));
+ ASSERT_TRUE(loaded_pmi1 == pmi_extra);
+}
+
+TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
+ ScratchFile profile;
+
+ Thread* self = Thread::Current();
+ jobject class_loader;
+ {
+ ScopedObjectAccess soa(self);
+ class_loader = LoadDex("ProfileTestMultiDex");
+ }
+ ASSERT_NE(class_loader, nullptr);
+
+ // Save virtual methods from Main.
+ std::set<DexCacheResolvedClasses> resolved_classes;
+ std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
+
+ SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
+ ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
+ profile.GetFilename(), main_methods, &profile_methods_map));
+
+ // Check that what we saved is in the profile.
+ ProfileCompilationInfo info;
+ ASSERT_TRUE(info.Load(GetFd(profile)));
+ ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
+ {
+ ScopedObjectAccess soa(self);
+ for (ArtMethod* m : main_methods) {
+ ASSERT_TRUE(info.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
+ const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
+ ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi;
+ ASSERT_TRUE(info.GetMethod(m->GetDexFile()->GetLocation(),
+ m->GetDexFile()->GetLocationChecksum(),
+ m->GetDexMethodIndex(),
+ &offline_pmi));
+ ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi =
+ ConvertProfileMethodInfo(pmi);
+ ASSERT_EQ(converted_pmi, offline_pmi);
+ }
+ }
+}
+
+TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCahce) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo info;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi1 = GetOfflineProfileMethodInfo();
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi2 = GetOfflineProfileMethodInfo();
+ // Modify the checksum to trigger a mismatch.
+ pmi2.dex_references[0].dex_checksum++;
+
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /*method_idx*/ 0, pmi1, &info));
+ ASSERT_FALSE(AddMethod("dex_location2", /* checksum */ 2, /*method_idx*/ 0, pmi2, &info));
+}
+
+// Verify that profiles behave correctly even if the methods are added in a different
+// order and with a different dex profile indices for the dex files.
+TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo info;
+ ProfileCompilationInfo info_reindexed;
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+ pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
+ pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2);
+ for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.AddClass(0, dex::TypeIndex(0));
+ dex_pc_data.AddClass(1, dex::TypeIndex(1));
+ pmi.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed;
+ pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2);
+ pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1);
+ for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.AddClass(1, dex::TypeIndex(0));
+ dex_pc_data.AddClass(0, dex::TypeIndex(1));
+ pmi_reindexed.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+
+ // Profile 1 and Profile 2 get the same methods but in different order.
+ // This will trigger a different dex numbers.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &info));
+ }
+
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod(
+ "dex_location2", /* checksum */ 2, method_idx, pmi_reindexed, &info_reindexed));
+ ASSERT_TRUE(AddMethod(
+ "dex_location1", /* checksum */ 1, method_idx, pmi_reindexed, &info_reindexed));
+ }
+
+ ProfileCompilationInfo info_backup = info;
+ ASSERT_TRUE(info.MergeWith(info_reindexed));
+ // Merging should have no effect as we're adding the exact same stuff.
+ ASSERT_TRUE(info.Equals(info_backup));
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
+ ASSERT_TRUE(info.GetMethod("dex_location1",
+ /* checksum */ 1,
+ /* method_idx */ method_idx,
+ &loaded_pmi1));
+ ASSERT_TRUE(loaded_pmi1 == pmi);
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi2;
+ ASSERT_TRUE(info.GetMethod("dex_location2",
+ /* checksum */ 2,
+ /* method_idx */ method_idx,
+ &loaded_pmi2));
+ ASSERT_TRUE(loaded_pmi2 == pmi);
+ }
+}
+
+TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimit) {
+ ProfileCompilationInfo info;
+ // Save a few methods.
+ for (uint16_t i = 0; i < std::numeric_limits<uint8_t>::max(); i++) {
+ std::string dex_location = std::to_string(i);
+ ASSERT_TRUE(AddMethod(dex_location, /* checksum */ 1, /* method_idx */ i, &info));
+ }
+ // We only support at most 255 dex files.
+ ASSERT_FALSE(AddMethod(
+ /*dex_location*/ "256", /* checksum */ 1, /* method_idx */ 0, &info));
+}
+
} // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 025d10c..61e6c41 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -236,10 +236,10 @@
std::set<DexCacheResolvedClasses> resolved_classes_for_location;
const std::string& filename = it.first;
const std::set<std::string>& locations = it.second;
- std::vector<MethodReference> methods_for_location;
+ std::vector<ProfileMethodInfo> profile_methods_for_location;
for (const MethodReference& ref : methods) {
if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) {
- methods_for_location.push_back(ref);
+ profile_methods_for_location.emplace_back(ref.dex_file, ref.dex_method_index);
}
}
for (const DexCacheResolvedClasses& classes : resolved_classes) {
@@ -253,7 +253,7 @@
}
}
ProfileCompilationInfo* info = GetCachedProfiledInfo(filename);
- info->AddMethodsAndClasses(methods_for_location, resolved_classes_for_location);
+ info->AddMethodsAndClasses(profile_methods_for_location, resolved_classes_for_location);
total_number_of_profile_entries_cached += resolved_classes_for_location.size();
}
max_number_of_profile_entries_cached_ = std::max(
@@ -280,15 +280,15 @@
}
const std::string& filename = it.first;
const std::set<std::string>& locations = it.second;
- std::vector<MethodReference> methods;
+ std::vector<ProfileMethodInfo> profile_methods;
{
ScopedObjectAccess soa(Thread::Current());
- jit_code_cache_->GetProfiledMethods(locations, methods);
+ jit_code_cache_->GetProfiledMethods(locations, profile_methods);
total_number_of_code_cache_queries_++;
}
ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename);
- cached_info->AddMethodsAndClasses(methods, std::set<DexCacheResolvedClasses>());
+ cached_info->AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>());
int64_t delta_number_of_methods =
cached_info->GetNumberOfMethods() -
static_cast<int64_t>(last_save_number_of_methods_);
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index 1c58a83..f42a8da 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -39,7 +39,7 @@
// Once the classes_ array is full, we consider the INVOKE to be megamorphic.
class InlineCache {
public:
- static constexpr uint16_t kIndividualCacheSize = 5;
+ static constexpr uint8_t kIndividualCacheSize = 5;
private:
uint32_t dex_pc_;