diff options
author | 2015-12-09 16:38:30 -0800 | |
---|---|---|
committer | 2016-02-26 11:35:59 -0800 | |
commit | 8913fc1a27df8cf3b37fd99e94d87f290591328e (patch) | |
tree | 103dae9d86a153e8520dfa9f733c3208bcadd06d | |
parent | 167e638ffd46186ef4fa26d1c0b6c7e00ed51ccb (diff) |
Add and use loaded class profiling
Class profiling is a way to keep track of which classes are resolved.
From here the compiler can use this information to generate a smaller
app image.
TODO: Add tests for profile stuff.
Bug: 22858531
Change-Id: I91ccd686394cc2517512f66abb0e277f3d26d4da
-rw-r--r-- | compiler/driver/compiler_driver.cc | 13 | ||||
-rw-r--r-- | compiler/driver/compiler_driver_test.cc | 4 | ||||
-rw-r--r-- | dex2oat/dex2oat.cc | 24 | ||||
-rw-r--r-- | profman/profile_assistant_test.cc | 4 | ||||
-rw-r--r-- | runtime/class_linker.cc | 114 | ||||
-rw-r--r-- | runtime/class_linker.h | 10 | ||||
-rw-r--r-- | runtime/dex_cache_resolved_classes.h | 71 | ||||
-rw-r--r-- | runtime/gc/space/image_space.cc | 2 | ||||
-rw-r--r-- | runtime/jit/offline_profiling_info.cc | 111 | ||||
-rw-r--r-- | runtime/jit/offline_profiling_info.h | 21 | ||||
-rw-r--r-- | runtime/jit/profile_compilation_info_test.cc | 11 | ||||
-rw-r--r-- | runtime/jit/profile_saver.cc | 36 | ||||
-rw-r--r-- | runtime/jit/profile_saver.h | 1 |
13 files changed, 377 insertions, 45 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 8ef1f28130..d13800be14 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -382,7 +382,9 @@ CompilerDriver::CompilerDriver( compiler_->Init(); - CHECK_EQ(boot_image_, image_classes_.get() != nullptr); + if (boot_image_) { + CHECK(image_classes_.get() != nullptr) << "Expected image classes for boot image"; + } } CompilerDriver::~CompilerDriver() { @@ -866,12 +868,13 @@ void CompilerDriver::PreCompile(jobject class_loader, } bool CompilerDriver::IsImageClass(const char* descriptor) const { - if (!IsBootImage()) { - // NOTE: Currently only reachable from InitImageMethodVisitor for the app image case. - return true; - } else { + if (image_classes_ != nullptr) { + // If we have a set of image classes, use those. return image_classes_->find(descriptor) != image_classes_->end(); } + // No set of image classes, assume we include all the classes. + // NOTE: Currently only reachable from InitImageMethodVisitor for the app image case. + return !IsBootImage(); } bool CompilerDriver::IsClassToCompile(const char* descriptor) const { diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 478588561f..00375641f3 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -250,8 +250,8 @@ class CompilerDriverProfileTest : public CompilerDriverTest { ProfileCompilationInfo info; for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { std::string key = ProfileCompilationInfo::GetProfileDexFileKey(dex_file->GetLocation()); - profile_info_.AddData(key, dex_file->GetLocationChecksum(), 1); - profile_info_.AddData(key, dex_file->GetLocationChecksum(), 2); + profile_info_.AddMethodIndex(key, dex_file->GetLocationChecksum(), 1); + profile_info_.AddMethodIndex(key, dex_file->GetLocationChecksum(), 2); } return &profile_info_; } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index ba0357bcb7..98acf3244e 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1267,6 +1267,24 @@ class Dex2Oat FINAL { dex_caches_.clear(); } + void LoadClassProfileDescriptors() { + if (profile_compilation_info_ != nullptr && app_image_) { + Runtime* runtime = Runtime::Current(); + CHECK(runtime != nullptr); + std::set<DexCacheResolvedClasses> resolved_classes( + profile_compilation_info_->GetResolvedClasses()); + image_classes_.reset(new std::unordered_set<std::string>( + runtime->GetClassLinker()->GetClassDescriptorsForProfileKeys(resolved_classes))); + VLOG(compiler) << "Loaded " << image_classes_->size() + << " image class descriptors from profile"; + if (VLOG_IS_ON(compiler)) { + for (const std::string& s : *image_classes_) { + LOG(INFO) << "Image class " << s; + } + } + } + } + // Set up the environment for compilation. Includes starting the runtime and loading/opening the // boot class path. bool Setup() { @@ -1615,7 +1633,10 @@ class Dex2Oat FINAL { // The non moving space is right after the oat file. Put the preferred app image location // right after the non moving space so that we ideally get a continuous immune region for // the GC. - const size_t non_moving_space_capacity = heap->GetNonMovingSpace()->Capacity(); + // Use the default non moving space capacity since dex2oat does not have a separate non- + // moving space. This means the runtime's non moving space space size will be as large + // as the growth limit for dex2oat, but smaller in the zygote. + const size_t non_moving_space_capacity = gc::Heap::kDefaultNonMovingSpaceCapacity; image_base_ += non_moving_space_capacity; VLOG(compiler) << "App image base=" << reinterpret_cast<void*>(image_base_); } @@ -2468,6 +2489,7 @@ static void b13564922() { } static int CompileImage(Dex2Oat& dex2oat) { + dex2oat.LoadClassProfileDescriptors(); dex2oat.Compile(); if (!dex2oat.WriteOatFiles()) { diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 3faa8eb53f..b0d5df2b3b 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -37,8 +37,8 @@ class ProfileAssistantTest : public CommonRuntimeTest { std::string dex_location2 = "location2" + id; uint32_t dex_location_checksum2 = 10 * checksum; for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) { - ASSERT_TRUE(info->AddData(dex_location1, dex_location_checksum1, i)); - ASSERT_TRUE(info->AddData(dex_location2, dex_location_checksum2, i)); + ASSERT_TRUE(info->AddMethodIndex(dex_location1, dex_location_checksum1, i)); + ASSERT_TRUE(info->AddMethodIndex(dex_location2, dex_location_checksum2, i)); } ASSERT_TRUE(info->Save(GetFd(profile))); ASSERT_EQ(0, profile.GetFile()->Flush()); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 9ea082769a..98f9a0121b 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -58,6 +58,7 @@ #include "interpreter/interpreter.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jit/offline_profiling_info.h" #include "leb128.h" #include "linear_alloc.h" #include "mirror/class.h" @@ -1615,7 +1616,8 @@ bool ClassLinker::AddImageSpace( VLOG(image) << name->ToModifiedUtf8(); } *error_msg = "Rejecting application image due to class loader mismatch"; - return false; + // Ignore class loader mismatch for now since these would just use possibly incorrect + // oat code anyways. The structural class check should be done in the parent. } } } @@ -7635,6 +7637,116 @@ void ClassLinker::CleanupClassLoaders() { } } +std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_boot_classes) { + ScopedObjectAccess soa(Thread::Current()); + ScopedAssertNoThreadSuspension ants(soa.Self(), __FUNCTION__); + std::set<DexCacheResolvedClasses> ret; + VLOG(class_linker) << "Collecting resolved classes"; + const uint64_t start_time = NanoTime(); + ReaderMutexLock mu(soa.Self(), *DexLock()); + // Loop through all the dex caches and inspect resolved classes. + for (const ClassLinker::DexCacheData& data : GetDexCachesData()) { + if (soa.Self()->IsJWeakCleared(data.weak_root)) { + continue; + } + mirror::DexCache* dex_cache = + down_cast<mirror::DexCache*>(soa.Self()->DecodeJObject(data.weak_root)); + if (dex_cache == nullptr) { + continue; + } + const DexFile* dex_file = dex_cache->GetDexFile(); + const std::string& location = dex_file->GetLocation(); + const size_t num_class_defs = dex_file->NumClassDefs(); + // Use the resolved types, this will miss array classes. + const size_t num_types = dex_file->NumTypeIds(); + VLOG(class_linker) << "Collecting class profile for dex file " << location + << " types=" << num_types << " class_defs=" << num_class_defs; + DexCacheResolvedClasses resolved_classes(dex_file->GetLocation(), + dex_file->GetLocationChecksum()); + size_t num_resolved = 0; + std::unordered_set<uint16_t> class_set; + CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); + for (size_t i = 0; i < num_types; ++i) { + mirror::Class* klass = dex_cache->GetResolvedType(i); + // Filter out null class loader since that is the boot class loader. + if (klass == nullptr || (ignore_boot_classes && klass->GetClassLoader() == nullptr)) { + continue; + } + ++num_resolved; + DCHECK(!klass->IsProxyClass()); + DCHECK(klass->IsResolved()); + mirror::DexCache* klass_dex_cache = klass->GetDexCache(); + if (klass_dex_cache == dex_cache) { + const size_t class_def_idx = klass->GetDexClassDefIndex(); + DCHECK(klass->IsResolved()); + CHECK_LT(class_def_idx, num_class_defs); + class_set.insert(class_def_idx); + } + } + + if (!class_set.empty()) { + auto it = ret.find(resolved_classes); + if (it != ret.end()) { + // Already have the key, union the class def idxs. + it->AddClasses(class_set.begin(), class_set.end()); + } else { + resolved_classes.AddClasses(class_set.begin(), class_set.end()); + ret.insert(resolved_classes); + } + } + + VLOG(class_linker) << "Dex location " << location << " has " << num_resolved << " / " + << num_class_defs << " resolved classes"; + } + VLOG(class_linker) << "Collecting class profile took " << PrettyDuration(NanoTime() - start_time); + return ret; +} + +std::unordered_set<std::string> ClassLinker::GetClassDescriptorsForProfileKeys( + const std::set<DexCacheResolvedClasses>& classes) { + std::unordered_set<std::string> ret; + Thread* const self = Thread::Current(); + std::unordered_map<std::string, const DexFile*> location_to_dex_file; + ScopedObjectAccess soa(self); + ScopedAssertNoThreadSuspension ants(soa.Self(), __FUNCTION__); + ReaderMutexLock mu(self, *DexLock()); + for (const ClassLinker::DexCacheData& data : GetDexCachesData()) { + if (!self->IsJWeakCleared(data.weak_root)) { + mirror::DexCache* dex_cache = + down_cast<mirror::DexCache*>(soa.Self()->DecodeJObject(data.weak_root)); + if (dex_cache != nullptr) { + const DexFile* dex_file = dex_cache->GetDexFile(); + // There could be duplicates if two dex files with the same location are mapped. + location_to_dex_file.emplace( + ProfileCompilationInfo::GetProfileDexFileKey(dex_file->GetLocation()), dex_file); + } + } + } + for (const DexCacheResolvedClasses& info : classes) { + const std::string& profile_key = info.GetDexLocation(); + auto found = location_to_dex_file.find(profile_key); + if (found != location_to_dex_file.end()) { + const DexFile* dex_file = found->second; + VLOG(profiler) << "Found opened dex file for " << dex_file->GetLocation() << " with " + << info.GetClasses().size() << " classes"; + DCHECK_EQ(dex_file->GetLocationChecksum(), info.GetLocationChecksum()); + for (uint16_t class_def_idx : info.GetClasses()) { + if (class_def_idx >= dex_file->NumClassDefs()) { + LOG(WARNING) << "Class def index " << class_def_idx << " >= " << dex_file->NumClassDefs(); + continue; + } + const DexFile::TypeId& type_id = dex_file->GetTypeId( + dex_file->GetClassDef(class_def_idx).class_idx_); + const char* descriptor = dex_file->GetTypeDescriptor(type_id); + ret.insert(descriptor); + } + } else { + VLOG(class_linker) << "Failed to find opened dex file for profile key " << profile_key; + } + } + return ret; +} + // Instantiate ResolveMethod. template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::kForceICCECheck>( const DexFile& dex_file, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a9448f732c..caabab3144 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -17,8 +17,10 @@ #ifndef ART_RUNTIME_CLASS_LINKER_H_ #define ART_RUNTIME_CLASS_LINKER_H_ +#include <set> #include <string> #include <unordered_map> +#include <unordered_set> #include <utility> #include <vector> @@ -27,6 +29,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "class_table.h" +#include "dex_cache_resolved_classes.h" #include "dex_file.h" #include "gc_root.h" #include "jni.h" @@ -595,6 +598,13 @@ class ClassLinker { static bool ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) SHARED_REQUIRES(Locks::mutator_lock_); + std::set<DexCacheResolvedClasses> GetResolvedClasses(bool ignore_boot_classes) + REQUIRES(!dex_lock_); + + std::unordered_set<std::string> GetClassDescriptorsForProfileKeys( + const std::set<DexCacheResolvedClasses>& classes) + REQUIRES(!dex_lock_); + struct DexCacheData { // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may // not work properly. diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h new file mode 100644 index 0000000000..80c12cb642 --- /dev/null +++ b/runtime/dex_cache_resolved_classes.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_DEX_CACHE_RESOLVED_CLASSES_H_ +#define ART_RUNTIME_DEX_CACHE_RESOLVED_CLASSES_H_ + +#include <string> +#include <unordered_set> +#include <vector> + +namespace art { + +// Data structure for passing around which classes belonging to a dex cache / dex file are resolved. +class DexCacheResolvedClasses { + public: + DexCacheResolvedClasses(const std::string& dex_location, uint32_t location_checksum) + : dex_location_(dex_location), + location_checksum_(location_checksum) {} + + // Only compare the key elements, ignore the resolved classes. + int Compare(const DexCacheResolvedClasses& other) const { + if (location_checksum_ != other.location_checksum_) { + return static_cast<int>(location_checksum_ - other.location_checksum_); + } + return dex_location_.compare(other.dex_location_); + } + + template <class InputIt> + void AddClasses(InputIt begin, InputIt end) const { + classes_.insert(begin, end); + } + + const std::string& GetDexLocation() const { + return dex_location_; + } + + uint32_t GetLocationChecksum() const { + return location_checksum_; + } + + const std::unordered_set<uint16_t>& GetClasses() const { + return classes_; + } + + private: + const std::string dex_location_; + const uint32_t location_checksum_; + // Array of resolved class def indexes. + mutable std::unordered_set<uint16_t> classes_; +}; + +inline bool operator<(const DexCacheResolvedClasses& a, const DexCacheResolvedClasses& b) { + return a.Compare(b) < 0; +} + +} // namespace art + +#endif // ART_RUNTIME_DEX_CACHE_RESOLVED_CLASSES_H_ diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 69210c3fd3..6b65b7378d 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1202,7 +1202,7 @@ ImageSpace* ImageSpace::Init(const char* image_filename, PROT_READ | PROT_WRITE, /*low_4gb*/true, /*reuse*/false, - out_error_msg)); + /*out*/out_error_msg)); if (map != nullptr) { const size_t stored_size = image_header->GetDataSize(); const size_t write_offset = sizeof(ImageHeader); // Skip the header. diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index 747b112f57..67c9b5f679 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -48,9 +48,11 @@ std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_ } } -bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename, - const std::vector<ArtMethod*>& methods) { - if (methods.empty()) { +bool ProfileCompilationInfo::SaveProfilingInfo( + const std::string& filename, + const std::vector<ArtMethod*>& methods, + const std::set<DexCacheResolvedClasses>& resolved_classes) { + if (methods.empty() && resolved_classes.empty()) { VLOG(profiler) << "No info to save to " << filename; return true; } @@ -71,14 +73,17 @@ bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename, } { ScopedObjectAccess soa(Thread::Current()); - for (auto it = methods.begin(); it != methods.end(); it++) { - const DexFile* dex_file = (*it)->GetDexFile(); - if (!info.AddData(GetProfileDexFileKey(dex_file->GetLocation()), - dex_file->GetLocationChecksum(), - (*it)->GetDexMethodIndex())) { + for (ArtMethod* method : methods) { + const DexFile* dex_file = method->GetDexFile(); + if (!info.AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()), + dex_file->GetLocationChecksum(), + method->GetDexMethodIndex())) { return false; } } + for (const DexCacheResolvedClasses& dex_cache : resolved_classes) { + info.AddResolvedClasses(dex_cache); + } } if (!flock.GetFile()->ClearContent()) { @@ -116,13 +121,14 @@ static bool WriteToFile(int fd, const std::ostringstream& os) { static constexpr const char kFieldSeparator = ','; static constexpr const char kLineSeparator = '\n'; +static constexpr const char* kClassesMarker = "classes"; /** * Serialization format: - * dex_location1,dex_location_checksum1,method_id11,method_id12... - * dex_location2,dex_location_checksum2,method_id21,method_id22... + * dex_location1,dex_location_checksum1,method_id11,method_id12...,classes,class_id1,class_id2... + * dex_location2,dex_location_checksum2,method_id21,method_id22...,classes,class_id1,class_id2... * e.g. - * app.apk,131232145,11,23,454,54 + * app.apk,131232145,11,23,454,54,classes,1,2,4,1234 * app.apk:classes5.dex,218490184,39,13,49,1 **/ bool ProfileCompilationInfo::Save(int fd) { @@ -133,11 +139,20 @@ bool ProfileCompilationInfo::Save(int fd) { for (const auto& it : info_) { 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; + } os << dex_location << kFieldSeparator << dex_data.checksum; for (auto method_it : dex_data.method_set) { os << kFieldSeparator << method_it; } + if (!dex_data.class_set.empty()) { + os << kFieldSeparator << kClassesMarker; + for (auto class_id : dex_data.class_set) { + os << kFieldSeparator << class_id; + } + } os << kLineSeparator; } @@ -168,18 +183,50 @@ static void SplitString(const std::string& s, char separator, std::vector<std::s } } -bool ProfileCompilationInfo::AddData(const std::string& dex_location, - uint32_t checksum, - uint16_t method_idx) { +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)); } if (info_it->second.checksum != checksum) { LOG(WARNING) << "Checksum mismatch for dex " << dex_location; + return nullptr; + } + return &info_it->second; +} + +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); + if (data == nullptr) { return false; } - info_it->second.method_set.insert(method_idx); + data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end()); + return true; +} + +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) { + return false; + } + data->method_set.insert(method_idx); + return true; +} + +bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location, + uint32_t checksum, + uint16_t class_idx) { + DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); + if (data == nullptr) { + return false; + } + data->class_set.insert(class_idx); return true; } @@ -198,12 +245,30 @@ bool ProfileCompilationInfo::ProcessLine(const std::string& line) { } for (size_t i = 2; i < parts.size(); i++) { + if (parts[i] == kClassesMarker) { + ++i; + // All of the remaining idx are class def indexes. + for (++i; i < parts.size(); ++i) { + uint32_t class_def_idx; + if (!ParseInt(parts[i].c_str(), &class_def_idx)) { + LOG(WARNING) << "Cannot parse class_def_idx " << parts[i]; + return false; + } else if (class_def_idx >= std::numeric_limits<uint16_t>::max()) { + LOG(WARNING) << "Class def idx " << class_def_idx << " is larger than uint16_t max"; + return false; + } + if (!AddClassIndex(dex_location, checksum, class_def_idx)) { + return false; + } + } + break; + } uint32_t method_idx; if (!ParseInt(parts[i].c_str(), &method_idx)) { LOG(WARNING) << "Cannot parse method_idx " << parts[i]; return false; } - if (!AddData(dex_location, checksum, method_idx)) { + if (!AddMethodIndex(dex_location, checksum, method_idx)) { return false; } } @@ -280,6 +345,8 @@ bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) { } info_it->second.method_set.insert(other_dex_data.method_set.begin(), other_dex_data.method_set.end()); + info_it->second.class_set.insert(other_dex_data.class_set.begin(), + other_dex_data.class_set.end()); } return true; } @@ -347,4 +414,16 @@ bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { return info_.Equals(other.info_); } +std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses() const { + std::set<DexCacheResolvedClasses> ret; + for (auto&& pair : info_) { + const std::string& profile_key = pair.first; + const DexFileData& data = pair.second; + DexCacheResolvedClasses classes(profile_key, data.checksum); + classes.AddClasses(data.class_set.begin(), data.class_set.end()); + ret.insert(classes); + } + return ret; +} + } // namespace art diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index edc591c2eb..ee7ce27e4c 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -21,6 +21,7 @@ #include <vector> #include "atomic.h" +#include "dex_cache_resolved_classes.h" #include "dex_file.h" #include "method_reference.h" #include "safe_map.h" @@ -28,6 +29,7 @@ namespace art { class ArtMethod; +class DexCacheProfileData; // TODO: rename file. /** @@ -43,7 +45,8 @@ class ProfileCompilationInfo { // Note that the saving proceeds only if the file can be locked for exclusive access. // If not (the locking is not blocking), the function does not save and returns false. static bool SaveProfilingInfo(const std::string& filename, - const std::vector<ArtMethod*>& methods); + const std::vector<ArtMethod*>& methods, + const std::set<DexCacheResolvedClasses>& resolved_classes); // Loads profile information from the given file descriptor. bool Load(int fd); @@ -68,14 +71,17 @@ class ProfileCompilationInfo { bool Equals(const ProfileCompilationInfo& other); static std::string GetProfileDexFileKey(const std::string& dex_location); - private: - bool AddData(const std::string& dex_location, uint32_t checksum, uint16_t method_idx); - bool ProcessLine(const std::string& line); + // Returns 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; + private: struct DexFileData { explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {} uint32_t checksum; std::set<uint16_t> method_set; + std::set<uint16_t> class_set; bool operator==(const DexFileData& other) const { return checksum == other.checksum && method_set == other.method_set; @@ -84,6 +90,13 @@ class ProfileCompilationInfo { using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>; + DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum); + bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx); + bool AddClassIndex(const std::string& dex_location, uint32_t checksum, uint16_t class_idx); + bool AddResolvedClasses(const DexCacheResolvedClasses& classes) + SHARED_REQUIRES(Locks::mutator_lock_); + bool ProcessLine(const std::string& line); + friend class ProfileCompilationInfoTest; friend class CompilerDriverProfileTest; friend class ProfileAssistantTest; diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 482ea06395..fdd8c6e600 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -53,7 +53,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { uint32_t checksum, uint16_t method_index, ProfileCompilationInfo* info) { - return info->AddData(dex_location, checksum, method_index); + return info->AddMethodIndex(dex_location, checksum, method_index); } uint32_t GetFd(const ScratchFile& file) { @@ -73,8 +73,11 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { ASSERT_NE(class_loader, nullptr); // Save virtual methods from Main. + std::set<DexCacheResolvedClasses> resolved_classes; std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;"); - ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), main_methods)); + ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), + main_methods, + resolved_classes)); // Check that what we saved is in the profile. ProfileCompilationInfo info1; @@ -89,7 +92,9 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { // Save virtual methods from Second. std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;"); - ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), second_methods)); + ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), + second_methods, + resolved_classes)); // Check that what we saved is in the profile (methods form Main and Second). ProfileCompilationInfo info2; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index b1a5a4b24c..ab26f6ffa9 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -32,6 +32,7 @@ static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 2000 * k static constexpr const uint64_t kRandomDelayMaxMs = 20 * 1000; // 20 seconds static constexpr const uint64_t kMaxBackoffMs = 5 * 60 * 1000; // 5 minutes static constexpr const uint64_t kSavePeriodMs = 10 * 1000; // 10 seconds +static constexpr const uint64_t kInitialDelayMs = 2 * 1000; // 2 seconds static constexpr const double kBackoffCoef = 1.5; static constexpr const uint32_t kMinimumNrOrMethodsToSave = 10; @@ -45,6 +46,7 @@ ProfileSaver::ProfileSaver(const std::string& output_filename, : jit_code_cache_(jit_code_cache), code_cache_last_update_time_ns_(0), shutting_down_(false), + first_profile_(true), wait_lock_("ProfileSaver wait lock"), period_condition_("ProfileSaver period condition", wait_lock_) { AddTrackedLocations(output_filename, code_paths); @@ -56,13 +58,18 @@ void ProfileSaver::Run() { uint64_t save_period_ms = kSavePeriodMs; VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms"; - while (true) { - if (ShuttingDown(self)) { - break; - } - uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs; - uint64_t sleep_time_ms = save_period_ms + random_sleep_delay_ms; + bool first_iteration = true; + while (!ShuttingDown(self)) { + uint64_t sleep_time_ms; + if (first_iteration) { + // Sleep less long for the first iteration since we want to record loaded classes shortly + // after app launch. + sleep_time_ms = kInitialDelayMs; + } else { + const uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs; + sleep_time_ms = save_period_ms + random_sleep_delay_ms; + } { MutexLock mu(self, wait_lock_); period_condition_.TimedWait(self, sleep_time_ms, 0); @@ -81,13 +88,14 @@ void ProfileSaver::Run() { // Reset the period to the initial value as it's highly likely to JIT again. save_period_ms = kSavePeriodMs; } + first_iteration = false; } } bool ProfileSaver::ProcessProfilingInfo() { uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs(); - if (last_update_time_ns - code_cache_last_update_time_ns_ - < kMinimumTimeBetweenCodeCacheUpdatesNs) { + if (!first_profile_ && last_update_time_ns - code_cache_last_update_time_ns_ + < kMinimumTimeBetweenCodeCacheUpdatesNs) { VLOG(profiler) << "Not enough time has passed since the last code cache update." << "Last update: " << last_update_time_ns << " Last save: " << code_cache_last_update_time_ns_; @@ -113,19 +121,27 @@ bool ProfileSaver::ProcessProfilingInfo() { ScopedObjectAccess soa(Thread::Current()); jit_code_cache_->GetCompiledArtMethods(locations, methods); } - if (methods.size() < kMinimumNrOrMethodsToSave) { + // Always save for the first one for loaded classes profile. + if (methods.size() < kMinimumNrOrMethodsToSave && !first_profile_) { VLOG(profiler) << "Not enough information to save to: " << filename <<" Nr of methods: " << methods.size(); return false; } - if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods)) { + std::set<DexCacheResolvedClasses> resolved_classes; + if (first_profile_) { + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + resolved_classes = class_linker->GetResolvedClasses(/*ignore boot classes*/true); + } + + if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods, resolved_classes)) { LOG(WARNING) << "Could not save profiling info to " << filename; return false; } VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start); } + first_profile_ = false; return true; } diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index 3342790e43..21017c1acd 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -74,6 +74,7 @@ class ProfileSaver { GUARDED_BY(Locks::profiler_lock_); uint64_t code_cache_last_update_time_ns_; bool shutting_down_ GUARDED_BY(Locks::profiler_lock_); + bool first_profile_ = true; // Save period condition support. Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; |