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
(cherry picked from commit 8913fc1a27df8cf3b37fd99e94d87f290591328e)
Change-Id: Ifcd09230cbdc266305bc1247e0d31e7920eb353e
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index cd4daeb..b5e6532 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 @@
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.
}
}
}
@@ -7628,6 +7630,116 @@
}
}
+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 aa55dac..729617d 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"
@@ -589,6 +592,13 @@
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 0000000..80c12cb
--- /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 4ef36a4..5aaf104 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1278,7 +1278,7 @@
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 747b112..67c9b5f 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -48,9 +48,11 @@
}
}
-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 @@
}
{
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 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 @@
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 @@
}
}
-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 @@
}
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 @@
}
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 @@
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 edc591c..ee7ce27 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 @@
// 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 @@
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 @@
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 482ea06..fdd8c6e 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -53,7 +53,7 @@
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 @@
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 @@
// 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 b1a5a4b..ab26f6f 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -32,6 +32,7 @@
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 @@
: 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 @@
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 @@
// 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 @@
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 3342790..21017c1 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -74,6 +74,7 @@
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;