diff options
Diffstat (limited to 'runtime/jit')
| -rw-r--r-- | runtime/jit/debugger_interface.cc | 2 | ||||
| -rw-r--r-- | runtime/jit/jit.cc | 2 | ||||
| -rw-r--r-- | runtime/jit/jit.h | 1 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.cc | 25 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.h | 1 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info.cc | 201 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info.h | 119 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info_test.cc | 78 | ||||
| -rw-r--r-- | runtime/jit/profile_saver.cc | 56 |
9 files changed, 385 insertions, 100 deletions
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc index ae0004426d..135d9b1f51 100644 --- a/runtime/jit/debugger_interface.cc +++ b/runtime/jit/debugger_interface.cc @@ -18,7 +18,7 @@ #include "base/logging.h" #include "base/mutex.h" -#include "thread-inl.h" +#include "thread-current-inl.h" #include "thread.h" #include <unordered_map> diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index ae474da7c0..969a5708c4 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -32,7 +32,9 @@ #include "profile_saver.h" #include "runtime.h" #include "runtime_options.h" +#include "stack.h" #include "stack_map.h" +#include "thread-inl.h" #include "thread_list.h" #include "utils.h" diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 4f5bebfbf9..75f9b0ac76 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -17,7 +17,6 @@ #ifndef ART_RUNTIME_JIT_JIT_H_ #define ART_RUNTIME_JIT_JIT_H_ -#include "base/arena_allocator.h" #include "base/histogram-inl.h" #include "base/macros.h" #include "base/mutex.h" diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 42d7653b9b..388a51751e 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -34,7 +34,9 @@ #include "linear_alloc.h" #include "mem_map.h" #include "oat_file-inl.h" +#include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" +#include "stack.h" #include "thread_list.h" namespace art { @@ -526,6 +528,15 @@ void JitCodeCache::CopyInlineCacheInto(const InlineCache& ic, } } +static void ClearMethodCounter(ArtMethod* method, bool was_warm) { + if (was_warm) { + method->AddAccessFlags(kAccPreviouslyWarm); + } + // We reset the counter to 1 so that the profile knows that the method was executed at least once. + // This is required for layout purposes. + method->SetCounter(1); +} + uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, ArtMethod* method, uint8_t* stack_map, @@ -600,7 +611,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // Simply discard the compiled code. Clear the counter so that it may be recompiled later. // Hopefully the class hierarchy will be more stable when compilation is retried. single_impl_still_valid = false; - method->SetCounter(1); + ClearMethodCounter(method, /*was_warm*/ false); break; } } @@ -1068,9 +1079,8 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { if (info->GetSavedEntryPoint() != nullptr) { info->SetSavedEntryPoint(nullptr); // We are going to move this method back to interpreter. Clear the counter now to - // give it a chance to be hot again, but set it to 1 so that this method can still be - // considered a startup method in case it's not executed again. - info->GetMethod()->SetCounter(1); + // give it a chance to be hot again. + ClearMethodCounter(info->GetMethod(), /*was_warm*/ true); } } } else if (kIsDebugBuild) { @@ -1379,7 +1389,7 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; // Because the counter is not atomic, there are some rare cases where we may not hit the // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. - method->SetCounter(1); + ClearMethodCounter(method, /*was_warm*/ false); return false; } @@ -1432,11 +1442,10 @@ void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, if (method->GetEntryPointFromQuickCompiledCode() == header->GetEntryPoint()) { // The entrypoint is the one to invalidate, so we just update it to the interpreter entry point - // and clear the counter to get the method Jitted again. We reset the counter to 1 to preserve - // it as a potential startup method. + // and clear the counter to get the method Jitted again. Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( method, GetQuickToInterpreterBridge()); - method->SetCounter(1); + ClearMethodCounter(method, /*was_warm*/ profiling_info != nullptr); } else { MutexLock mu(Thread::Current(), lock_); auto it = osr_code_map_.find(method); diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 612d06ba1c..eea2771500 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -39,6 +39,7 @@ namespace art { class ArtMethod; class LinearAlloc; class InlineCache; +class OatQuickMethodHeader; class ProfilingInfo; namespace jit { diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 1e720c0cf4..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); @@ -1349,16 +1458,27 @@ bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { } std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses( - const std::unordered_set<std::string>& dex_files_locations) const { - std::unordered_map<std::string, std::string> key_to_location_map; - for (const std::string& location : dex_files_locations) { - key_to_location_map.emplace(GetProfileDexFileKey(location), location); + const std::vector<const DexFile*>& dex_files) const { + std::unordered_map<std::string, const DexFile* > key_to_dex_file; + for (const DexFile* dex_file : dex_files) { + key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file); } std::set<DexCacheResolvedClasses> ret; for (const DexFileData* dex_data : info_) { - const auto it = key_to_location_map.find(dex_data->profile_key); - if (it != key_to_location_map.end()) { - DexCacheResolvedClasses classes(it->second, it->second, dex_data->checksum); + const auto it = key_to_dex_file.find(dex_data->profile_key); + if (it != key_to_dex_file.end()) { + const DexFile* dex_file = it->second; + const std::string& dex_location = dex_file->GetLocation(); + if (dex_data->checksum != it->second->GetLocationChecksum()) { + LOG(ERROR) << "Dex checksum mismatch when getting resolved classes from profile for " + << "location " << dex_location << " (checksum=" << dex_file->GetLocationChecksum() + << ", profile checksum=" << dex_data->checksum; + return std::set<DexCacheResolvedClasses>(); + } + 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); } @@ -1375,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; @@ -1396,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++) { @@ -1404,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); @@ -1423,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 a9a5b13b75..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,14 +274,16 @@ 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); // Return the class descriptors for all of the classes in the profiles' class sets. std::set<DexCacheResolvedClasses> GetResolvedClasses( - const std::unordered_set<std::string>& dex_files_locations) const; + const std::vector<const DexFile*>& dex_files_) const; // Return the profile key associated with the given dex location. static std::string GetProfileDexFileKey(const std::string& dex_location); @@ -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 166b6f4ba1..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,21 +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) { - // Have samples, add to profile. - const DexFile* dex_file = - method.GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetDexFile(); - methods_->push_back(MethodReference(dex_file, method.GetDexMethodIndex())); + 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) { + 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_; }; @@ -217,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); @@ -230,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_); @@ -244,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 " @@ -264,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( @@ -316,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)); |