Merge "Update run-test 097-duplicate-method"
am: e9b6bea
* commit 'e9b6bea979ee9a450ebc4b5611514ba3f82f24d8':
Update run-test 097-duplicate-method
diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk
index a06f45a..953cfc0 100644
--- a/build/Android.cpplint.mk
+++ b/build/Android.cpplint.mk
@@ -16,14 +16,10 @@
include art/build/Android.common_build.mk
-ART_CPPLINT := $(LOCAL_PATH)/tools/cpplint.py
+ART_CPPLINT := art/tools/cpplint.py
ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf
ART_CPPLINT_FLAGS := --quiet
-# This:
-# 1) Gets a list of all .h & .cc files in the art directory.
-# 2) Prepends 'art/' to each of them to make the full name.
-# 3) removes art/runtime/elf.h from the list.
-ART_CPPLINT_SRC := $(filter-out $(LOCAL_PATH)/runtime/elf.h, $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,*.h) $(call all-subdir-named-files,*$(ART_CPP_EXTENSION))))
+ART_CPPLINT_SRC := $(shell find art -name "*.h" -o -name "*$(ART_CPP_EXTENSION)" | grep -v art/compiler/llvm/generated/ | grep -v art/runtime/elf\.h)
# "mm cpplint-art" to verify we aren't regressing
.PHONY: cpplint-art
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index 58e8a3a..ac18657 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -54,7 +54,7 @@
for (size_t i = 0; i < new_info.size(); i++) {
// Merge all data into a single object.
- if (!info.Load(new_info[i])) {
+ if (!info.MergeWith(new_info[i])) {
LOG(WARNING) << "Could not merge profile data at index " << i;
return kErrorBadProfiles;
}
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index b0d5df2..157ffc4 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -102,8 +102,8 @@
ASSERT_TRUE(result.Load(reference_profile_fd));
ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
+ ASSERT_TRUE(expected.MergeWith(info1));
+ ASSERT_TRUE(expected.MergeWith(info2));
ASSERT_TRUE(expected.Equals(result));
// The information from profiles must remain the same.
@@ -145,9 +145,9 @@
ASSERT_TRUE(result.Load(reference_profile_fd));
ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
- ASSERT_TRUE(expected.Load(reference_info));
+ ASSERT_TRUE(expected.MergeWith(info1));
+ ASSERT_TRUE(expected.MergeWith(info2));
+ ASSERT_TRUE(expected.MergeWith(reference_info));
ASSERT_TRUE(expected.Equals(result));
// The information from profiles must remain the same.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 99e38d9..20946f0 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -50,7 +50,7 @@
#include "experimental_flags.h"
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
-#include "gc/accounting/heap_bitmap.h"
+#include "gc/accounting/heap_bitmap-inl.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
#include "handle_scope-inl.h"
@@ -96,6 +96,7 @@
namespace art {
static constexpr bool kSanityCheckObjects = kIsDebugBuild;
+static constexpr bool kVerifyArtMethodDeclaringClasses = true;
static void ThrowNoClassDefFoundError(const char* fmt, ...)
__attribute__((__format__(__printf__, 1, 2)))
@@ -1197,6 +1198,23 @@
ClassTable* const table_;
};
+class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
+ public:
+ VerifyDeclaringClassVisitor() SHARED_REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
+ : live_bitmap_(Runtime::Current()->GetHeap()->GetLiveBitmap()) {}
+
+ virtual void Visit(ArtMethod* method)
+ SHARED_REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ mirror::Class* klass = method->GetDeclaringClassUnchecked();
+ if (klass != nullptr) {
+ CHECK(live_bitmap_->Test(klass)) << "Image method has unmarked declaring class";
+ }
+ }
+
+ private:
+ gc::accounting::HeapBitmap* const live_bitmap_;
+};
+
bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
@@ -1426,6 +1444,15 @@
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
}
}
+ if (kVerifyArtMethodDeclaringClasses) {
+ ScopedTrace timing("Verify declaring classes");
+ ReaderMutexLock rmu(self, *Locks::heap_bitmap_lock_);
+ VerifyDeclaringClassVisitor visitor;
+ header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
+ &visitor,
+ space->Begin(),
+ sizeof(void*));
+ }
return true;
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 7e73e5c..8ca60b2 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -87,6 +87,11 @@
memory_use_.PrintMemoryUse(os);
}
+void Jit::DumpForSigQuit(std::ostream& os) {
+ DumpInfo(os);
+ ProfileSaver::DumpInstanceInfo(os);
+}
+
void Jit::AddTimingLogger(const TimingLogger& logger) {
cumulative_timings_.AddLogger(logger);
}
@@ -224,7 +229,7 @@
void Jit::StopProfileSaver() {
if (save_profiling_info_ && ProfileSaver::IsStarted()) {
- ProfileSaver::Stop();
+ ProfileSaver::Stop(dump_info_on_shutdown_);
}
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 37d0bdb..e3fa89d 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -84,9 +84,7 @@
const std::string& app_dir);
void StopProfileSaver();
- void DumpForSigQuit(std::ostream& os) REQUIRES(!lock_) {
- DumpInfo(os);
- }
+ void DumpForSigQuit(std::ostream& os) REQUIRES(!lock_);
static void NewTypeLoadedIfUsingJit(mirror::Class* type)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index f181ca3..e864d87 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -49,15 +49,27 @@
}
}
-bool ProfileCompilationInfo::SaveProfilingInfo(
- const std::string& filename,
+bool ProfileCompilationInfo::AddMethodsAndClasses(
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;
+ ScopedObjectAccess soa(Thread::Current());
+ for (ArtMethod* method : methods) {
+ const DexFile* dex_file = method->GetDexFile();
+ if (!AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()),
+ dex_file->GetLocationChecksum(),
+ method->GetDexMethodIndex())) {
+ return false;
+ }
}
+ for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
+ if (!AddResolvedClasses(dex_cache)) {
+ return false;
+ }
+ }
+ return true;
+}
+bool ProfileCompilationInfo::MergeAndSave(const std::string& filename, uint64_t* bytes_written) {
ScopedTrace trace(__PRETTY_FUNCTION__);
ScopedFlock flock;
std::string error;
@@ -68,26 +80,28 @@
int fd = flock.GetFile()->Fd();
- ProfileCompilationInfo info;
- if (!info.Load(fd)) {
+ // Load the file but keep a copy around to be able to infer if the content has changed.
+ ProfileCompilationInfo fileInfo;
+ if (!fileInfo.Load(fd)) {
LOG(WARNING) << "Could not load previous profile data from file " << filename;
return false;
}
- {
- ScopedObjectAccess soa(Thread::Current());
- 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);
- }
+
+ // Merge the content of file into the current object.
+ if (!MergeWith(fileInfo)) {
+ LOG(WARNING) << "Could not merge previous profile data from file " << filename;
}
+ // If after the merge we have the same data as what is the file there's no point
+ // in actually doing the write. The file will be exactly the same as before.
+ if (Equals(fileInfo)) {
+ if (bytes_written != nullptr) {
+ *bytes_written = 0;
+ }
+ return true;
+ }
+
+ // We need to clear the data because we don't support append to the profiles yet.
if (!flock.GetFile()->ClearContent()) {
PLOG(WARNING) << "Could not clear profile file: " << filename;
return false;
@@ -95,16 +109,44 @@
// This doesn't need locking because we are trying to lock the file for exclusive
// access and fail immediately if we can't.
- bool result = info.Save(fd);
+ bool result = Save(fd);
if (result) {
VLOG(profiler) << "Successfully saved profile info to " << filename
<< " Size: " << GetFileSizeBytes(filename);
+ if (bytes_written != nullptr) {
+ *bytes_written = GetFileSizeBytes(filename);
+ }
} else {
VLOG(profiler) << "Failed to save profile info to " << filename;
}
return result;
}
+bool ProfileCompilationInfo::SaveProfilingInfo(
+ const std::string& filename,
+ const std::vector<ArtMethod*>& methods,
+ const std::set<DexCacheResolvedClasses>& resolved_classes,
+ uint64_t* bytes_written) {
+ if (methods.empty() && resolved_classes.empty()) {
+ VLOG(profiler) << "No info to save to " << filename;
+ if (bytes_written != nullptr) {
+ *bytes_written = 0;
+ }
+ return true;
+ }
+
+ ProfileCompilationInfo info;
+ if (!info.AddMethodsAndClasses(methods, resolved_classes)) {
+ LOG(WARNING) << "Checksum mismatch when processing methods and resolved classes for "
+ << filename;
+ if (bytes_written != nullptr) {
+ *bytes_written = 0;
+ }
+ return false;
+ }
+ return info.MergeAndSave(filename, bytes_written);
+}
+
static bool WriteToFile(int fd, const std::ostringstream& os) {
std::string data(os.str());
const char *p = data.c_str();
@@ -334,7 +376,7 @@
return true;
}
-bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
+bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
for (const auto& other_it : other.info_) {
const std::string& other_dex_location = other_it.first;
const DexFileData& other_dex_data = other_it.second;
@@ -387,6 +429,14 @@
return total;
}
+uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
+ uint32_t total = 0;
+ for (const auto& it : info_) {
+ total += it.second.class_set.size();
+ }
+ return total;
+}
+
std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location) const {
std::ostringstream os;
@@ -442,4 +492,10 @@
return ret;
}
+void ProfileCompilationInfo::ClearResolvedClasses() {
+ for (auto& pair : info_) {
+ pair.second.class_set.clear();
+ }
+}
+
} // namespace art
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index df03244..2c819f1 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -46,16 +46,26 @@
// 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::set<DexCacheResolvedClasses>& resolved_classes);
+ const std::set<DexCacheResolvedClasses>& resolved_classes,
+ uint64_t* bytes_written = nullptr);
+ // Add the given methods and classes to the current profile object.
+ bool AddMethodsAndClasses(const std::vector<ArtMethod*>& methods,
+ const std::set<DexCacheResolvedClasses>& resolved_classes);
// Loads profile information from the given file descriptor.
bool Load(int fd);
- // Loads the data from another ProfileCompilationInfo object.
- bool Load(const ProfileCompilationInfo& info);
+ // Merge the data from another ProfileCompilationInfo into the current object.
+ bool MergeWith(const ProfileCompilationInfo& info);
// Saves the profile data to the given file descriptor.
bool Save(int fd);
+ // Loads and merges profile information from the given file into the current
+ // object and tries to save it back to disk.
+ bool MergeAndSave(const std::string& filename, uint64_t* bytes_written);
+
// Returns the number of methods that were profiled.
uint32_t GetNumberOfMethods() const;
+ // Returns the number of resolved classes that were profiled.
+ uint32_t GetNumberOfResolvedClasses() const;
// Returns true if the method reference is present in the profiling info.
bool ContainsMethod(const MethodReference& method_ref) const;
@@ -70,8 +80,8 @@
std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location = true) const;
- // For testing purposes.
bool Equals(const ProfileCompilationInfo& other);
+
static std::string GetProfileDexFileKey(const std::string& dex_location);
// Returns the class descriptors for all of the classes in the profiles' class sets.
@@ -79,6 +89,9 @@
// profile info stuff to generate a map back to the dex location.
std::set<DexCacheResolvedClasses> GetResolvedClasses() const;
+ // Clears the resolved classes from the current object.
+ void ClearResolvedClasses();
+
private:
struct DexFileData {
explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index fdd8c6e..54fd69f 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -49,7 +49,7 @@
return methods;
}
- bool AddData(const std::string& dex_location,
+ bool AddMethod(const std::string& dex_location,
uint32_t checksum,
uint16_t method_index,
ProfileCompilationInfo* info) {
@@ -118,8 +118,8 @@
ProfileCompilationInfo saved_info;
// Save a few methods.
for (uint16_t i = 0; i < 10; i++) {
- ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
}
ASSERT_TRUE(saved_info.Save(GetFd(profile)));
ASSERT_EQ(0, profile.GetFile()->Flush());
@@ -132,9 +132,9 @@
// Save more methods.
for (uint16_t i = 0; i < 100; i++) {
- ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
}
ASSERT_TRUE(profile.GetFile()->ResetOffset());
ASSERT_TRUE(saved_info.Save(GetFd(profile)));
@@ -147,25 +147,25 @@
ASSERT_TRUE(loaded_info2.Equals(saved_info));
}
-TEST_F(ProfileCompilationInfoTest, AddDataFail) {
+TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) {
ScratchFile profile;
ProfileCompilationInfo info;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
// Trying to add info for an existing file but with a different checksum.
- ASSERT_FALSE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
+ ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
}
-TEST_F(ProfileCompilationInfoTest, LoadFail) {
+TEST_F(ProfileCompilationInfoTest, MergeFail) {
ScratchFile profile;
ProfileCompilationInfo info1;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
// Use the same file, change the checksum.
ProfileCompilationInfo info2;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
- ASSERT_FALSE(info1.Load(info2));
+ ASSERT_FALSE(info1.MergeWith(info2));
}
} // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 6fe17db..81d81a5 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -29,18 +29,18 @@
// An arbitrary value to throttle save requests. Set to 2s for now.
static constexpr const uint64_t kMilisecondsToNano = 1000000;
-static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 2000 * kMilisecondsToNano;
// TODO: read the constants from ProfileOptions,
// Add a random delay each time we go to sleep so that we don't hammer the CPU
// with all profile savers running at the same time.
-static constexpr const uint64_t kRandomDelayMaxMs = 20 * 1000; // 20 seconds
+static constexpr const uint64_t kRandomDelayMaxMs = 30 * 1000; // 30 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 uint64_t kSavePeriodMs = 20 * 1000; // 20 seconds
+static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds
static constexpr const double kBackoffCoef = 1.5;
-static constexpr const uint32_t kMinimumNrOrMethodsToSave = 10;
+static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10;
+static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10;
ProfileSaver* ProfileSaver::instance_ = nullptr;
pthread_t ProfileSaver::profiler_pthread_ = 0U;
@@ -52,12 +52,24 @@
const std::string& app_data_dir)
: jit_code_cache_(jit_code_cache),
foreign_dex_profile_path_(foreign_dex_profile_path),
- code_cache_last_update_time_ns_(0),
shutting_down_(false),
- first_profile_(true),
+ last_save_number_of_methods_(0),
+ last_save_number_of_classes__(0),
wait_lock_("ProfileSaver wait lock"),
- period_condition_("ProfileSaver period condition", wait_lock_) {
+ period_condition_("ProfileSaver period condition", wait_lock_),
+ total_bytes_written_(0),
+ total_number_of_writes_(0),
+ total_number_of_code_cache_queries_(0),
+ total_number_of_skipped_writes_(0),
+ total_number_of_failed_writes_(0),
+ total_ms_of_sleep_(0),
+ total_ns_of_work_(0),
+ total_number_of_foreign_dex_marks_(0),
+ max_number_of_profile_entries_cached_(0) {
AddTrackedLocations(output_filename, code_paths);
+ // We only need to save the resolved classes if the profile file is empty.
+ // Otherwise we must have already save them (we always do it during the first
+ // ever profile save).
app_data_dir_ = "";
if (!app_data_dir.empty()) {
// The application directory is used to determine which dex files are owned by app.
@@ -80,14 +92,13 @@
uint64_t save_period_ms = kSavePeriodMs;
VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms";
-
- bool first_iteration = true;
+ bool cache_resolved_classes = true;
while (!ShuttingDown(self)) {
uint64_t sleep_time_ms;
- if (first_iteration) {
+ if (cache_resolved_classes) {
// Sleep less long for the first iteration since we want to record loaded classes shortly
// after app launch.
- sleep_time_ms = kInitialDelayMs;
+ sleep_time_ms = kSaveResolvedClassesDelayMs;
} else {
const uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs;
sleep_time_ms = save_period_ms + random_sleep_delay_ms;
@@ -96,43 +107,81 @@
MutexLock mu(self, wait_lock_);
period_condition_.TimedWait(self, sleep_time_ms, 0);
}
-
+ total_ms_of_sleep_ += sleep_time_ms;
if (ShuttingDown(self)) {
break;
}
- if (!ProcessProfilingInfo() && save_period_ms < kMaxBackoffMs) {
- // If we don't need to save now it is less likely that we will need to do
- // so in the future. Increase the time between saves according to the
- // kBackoffCoef, but make it no larger than kMaxBackoffMs.
- save_period_ms = static_cast<uint64_t>(kBackoffCoef * save_period_ms);
+ uint64_t start = NanoTime();
+ if (cache_resolved_classes) {
+ // TODO(calin) This only considers the case of the primary profile file.
+ // Anything that gets loaded in the same VM will not have their resolved
+ // classes save (unless they started before the initial saving was done).
+ FetchAndCacheResolvedClasses();
} else {
- // Reset the period to the initial value as it's highly likely to JIT again.
- save_period_ms = kSavePeriodMs;
+ bool profile_saved_to_disk = ProcessProfilingInfo();
+ if (!profile_saved_to_disk && save_period_ms < kMaxBackoffMs) {
+ // If we don't need to save now it is less likely that we will need to do
+ // so in the future. Increase the time between saves according to the
+ // kBackoffCoef, but make it no larger than kMaxBackoffMs.
+ save_period_ms = static_cast<uint64_t>(kBackoffCoef * save_period_ms);
+ } else {
+ // Reset the period to the initial value as it's highly likely to JIT again.
+ save_period_ms = kSavePeriodMs;
+ }
}
- first_iteration = false;
+ cache_resolved_classes = false;
+
+ total_ns_of_work_ += (NanoTime() - start);
}
}
+ProfileCompilationInfo* ProfileSaver::GetCachedProfiledInfo(const std::string& filename) {
+ auto info_it = profile_cache_.find(filename);
+ if (info_it == profile_cache_.end()) {
+ info_it = profile_cache_.Put(filename, ProfileCompilationInfo());
+ }
+ return &info_it->second;
+}
+
+void ProfileSaver::FetchAndCacheResolvedClasses() {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ std::set<DexCacheResolvedClasses> resolved_classes =
+ class_linker->GetResolvedClasses(/*ignore boot classes*/ true);
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ uint64_t total_number_of_profile_entries_cached = 0;
+ for (const auto& it : tracked_dex_base_locations_) {
+ std::set<DexCacheResolvedClasses> resolved_classes_for_location;
+ const std::string& filename = it.first;
+ const std::set<std::string>& locations = it.second;
+
+ for (const DexCacheResolvedClasses& classes : resolved_classes) {
+ if (locations.find(classes.GetDexLocation()) != locations.end()) {
+ resolved_classes_for_location.insert(classes);
+ }
+ }
+ ProfileCompilationInfo* info = GetCachedProfiledInfo(filename);
+ info->AddMethodsAndClasses(std::vector<ArtMethod*>(), resolved_classes_for_location);
+ total_number_of_profile_entries_cached += resolved_classes_for_location.size();
+ }
+ max_number_of_profile_entries_cached_ = std::max(
+ max_number_of_profile_entries_cached_,
+ total_number_of_profile_entries_cached);
+}
+
bool ProfileSaver::ProcessProfilingInfo() {
ScopedTrace trace(__PRETTY_FUNCTION__);
- uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs();
- 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_;
- return false;
- }
-
- uint64_t start = NanoTime();
- code_cache_last_update_time_ns_ = last_update_time_ns;
SafeMap<std::string, std::set<std::string>> tracked_locations;
{
// Make a copy so that we don't hold the lock while doing I/O.
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
tracked_locations = tracked_dex_base_locations_;
}
+
+ bool profile_file_saved = false;
+ uint64_t total_number_of_profile_entries_cached = 0;
for (const auto& it : tracked_locations) {
if (ShuttingDown(Thread::Current())) {
return true;
@@ -143,29 +192,56 @@
{
ScopedObjectAccess soa(Thread::Current());
jit_code_cache_->GetCompiledArtMethods(locations, methods);
- }
- // 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;
+ total_number_of_code_cache_queries_++;
}
- std::set<DexCacheResolvedClasses> resolved_classes;
- if (first_profile_) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- resolved_classes = class_linker->GetResolvedClasses(/*ignore boot classes*/true);
- }
+ ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename);
+ cached_info->AddMethodsAndClasses(methods, std::set<DexCacheResolvedClasses>());
+ uint32_t delta_number_of_methods =
+ cached_info->GetNumberOfMethods() - last_save_number_of_methods_;
+ uint32_t delta_number_of_classes =
+ cached_info->GetNumberOfResolvedClasses() - last_save_number_of_classes__;
- if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods, resolved_classes)) {
+ if (delta_number_of_methods < kMinimumNumberOfMethodsToSave &&
+ delta_number_of_classes < kMinimumNumberOfClassesToSave) {
+ VLOG(profiler) << "Not enough information to save to: " << filename
+ << " Nr of methods: " << delta_number_of_methods
+ << " Nr of classes: " << delta_number_of_classes;
+ total_number_of_skipped_writes_++;
+ continue;
+ }
+ uint64_t bytes_written;
+ if (cached_info->MergeAndSave(filename, &bytes_written)) {
+ last_save_number_of_methods_ = cached_info->GetNumberOfMethods();
+ last_save_number_of_classes__ = cached_info->GetNumberOfResolvedClasses();
+ // Clear resolved classes. No need to store them around as
+ // they don't change after the first write.
+ cached_info->ClearResolvedClasses();
+ if (bytes_written > 0) {
+ total_number_of_writes_++;
+ total_bytes_written_ += bytes_written;
+ } else {
+ // At this point we could still have avoided the write.
+ // We load and merge the data from the file lazily at its first ever
+ // save attempt. So, whatever we are trying to save could already be
+ // in the file.
+ total_number_of_skipped_writes_++;
+ }
+ profile_file_saved = true;
+ } else {
LOG(WARNING) << "Could not save profiling info to " << filename;
- return false;
+ total_number_of_failed_writes_++;
+ // TODO: (calin): if we failed because of bad profiling data or parsing
+ // errors we should clear the profile file.
}
-
- VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start);
+ total_number_of_profile_entries_cached +=
+ cached_info->GetNumberOfMethods() +
+ cached_info->GetNumberOfResolvedClasses();
}
- first_profile_ = false;
- return true;
+ max_number_of_profile_entries_cached_ = std::max(
+ max_number_of_profile_entries_cached_,
+ total_number_of_profile_entries_cached);
+ return profile_file_saved;
}
void* ProfileSaver::RunProfileSaverThread(void* arg) {
@@ -219,7 +295,7 @@
"Profile saver thread");
}
-void ProfileSaver::Stop() {
+void ProfileSaver::Stop(bool dump_info) {
ProfileSaver* profile_saver = nullptr;
pthread_t profiler_pthread = 0U;
@@ -237,6 +313,9 @@
return;
}
instance_->shutting_down_ = true;
+ if (dump_info) {
+ instance_->DumpInfo(LOG(INFO));
+ }
}
{
@@ -283,7 +362,9 @@
std::string app_data_dir;
{
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
- DCHECK(instance_ != nullptr);
+ if (instance_ == nullptr) {
+ return;
+ }
// Make a copy so that we don't hold the lock while doing I/O.
for (const auto& it : instance_->tracked_dex_base_locations_) {
app_code_paths.insert(it.second.begin(), it.second.end());
@@ -292,24 +373,30 @@
app_data_dir = instance_->app_data_dir_;
}
- MaybeRecordDexUseInternal(dex_location,
- app_code_paths,
- foreign_dex_profile_path,
- app_data_dir);
+ bool mark_created = MaybeRecordDexUseInternal(dex_location,
+ app_code_paths,
+ foreign_dex_profile_path,
+ app_data_dir);
+ if (mark_created) {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ if (instance_ != nullptr) {
+ instance_->total_number_of_foreign_dex_marks_++;
+ }
+ }
}
-void ProfileSaver::MaybeRecordDexUseInternal(
+bool ProfileSaver::MaybeRecordDexUseInternal(
const std::string& dex_location,
const std::set<std::string>& app_code_paths,
const std::string& foreign_dex_profile_path,
const std::string& app_data_dir) {
if (dex_location.empty()) {
LOG(WARNING) << "Asked to record foreign dex use with an empty dex location.";
- return;
+ return false;
}
if (foreign_dex_profile_path.empty()) {
LOG(WARNING) << "Asked to record foreign dex use without a valid profile path ";
- return;
+ return false;
}
UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr));
@@ -322,12 +409,12 @@
if (dex_location_real_path_str.compare(0, app_data_dir.length(), app_data_dir) == 0) {
// The dex location is under the application folder. Nothing to record.
- return;
+ return false;
}
if (app_code_paths.find(dex_location) != app_code_paths.end()) {
// The dex location belongs to the application code paths. Nothing to record.
- return;
+ return false;
}
// Do another round of checks with the real paths.
// Note that we could cache all the real locations in the saver (since it's an expensive
@@ -344,7 +431,7 @@
: real_app_code_location.get());
if (real_app_code_location_str == dex_location_real_path_str) {
// The dex location belongs to the application code paths. Nothing to record.
- return;
+ return false;
}
}
@@ -362,12 +449,37 @@
if (close(fd) != 0) {
PLOG(WARNING) << "Could not close file after flagging foreign dex use " << flag_path;
}
+ return true;
} else {
if (errno != EEXIST) {
// Another app could have already created the file.
PLOG(WARNING) << "Could not create foreign dex use mark " << flag_path;
+ return false;
}
+ return true;
}
}
+void ProfileSaver::DumpInstanceInfo(std::ostream& os) {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ if (instance_ != nullptr) {
+ instance_->DumpInfo(os);
+ }
+}
+
+void ProfileSaver::DumpInfo(std::ostream& os) {
+ os << "ProfileSaver total_bytes_written=" << total_bytes_written_ << '\n'
+ << "ProfileSaver total_number_of_writes=" << total_number_of_writes_ << '\n'
+ << "ProfileSaver total_number_of_code_cache_queries="
+ << total_number_of_code_cache_queries_ << '\n'
+ << "ProfileSaver total_number_of_skipped_writes=" << total_number_of_skipped_writes_ << '\n'
+ << "ProfileSaver total_number_of_failed_writes=" << total_number_of_failed_writes_ << '\n'
+ << "ProfileSaver total_ms_of_sleep=" << total_ms_of_sleep_ << '\n'
+ << "ProfileSaver total_ms_of_work=" << (total_ns_of_work_ / kMilisecondsToNano) << '\n'
+ << "ProfileSaver total_number_of_foreign_dex_marks="
+ << total_number_of_foreign_dex_marks_ << '\n'
+ << "ProfileSaver max_number_profile_entries_cached="
+ << max_number_of_profile_entries_cached_ << '\n';
+}
+
} // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index e7eab95..91390fe 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -37,7 +37,7 @@
// Stops the profile saver thread.
// NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
- static void Stop()
+ static void Stop(bool dump_info_)
REQUIRES(!Locks::profiler_lock_, !wait_lock_)
NO_THREAD_SAFETY_ANALYSIS;
@@ -46,6 +46,9 @@
static void NotifyDexUse(const std::string& dex_location);
+ // If the profile saver is running, dumps statistics to the `os`. Otherwise it does nothing.
+ static void DumpInstanceInfo(std::ostream& os);
+
private:
ProfileSaver(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
@@ -70,12 +73,22 @@
const std::vector<std::string>& code_paths)
REQUIRES(Locks::profiler_lock_);
- static void MaybeRecordDexUseInternal(
+ // Retrieves the cached profile compilation info for the given profile file.
+ // If no entry exists, a new empty one will be created, added to the cache and
+ // then returned.
+ ProfileCompilationInfo* GetCachedProfiledInfo(const std::string& filename);
+ // Fetches the current resolved classes from the ClassLinker and stores them
+ // in the profile_cache_ for later save.
+ void FetchAndCacheResolvedClasses();
+
+ static bool MaybeRecordDexUseInternal(
const std::string& dex_location,
const std::set<std::string>& tracked_locations,
const std::string& foreign_dex_profile_path,
const std::string& app_data_dir);
+ void DumpInfo(std::ostream& os);
+
// The only instance of the saver.
static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_);
// Profile saver thread.
@@ -86,14 +99,31 @@
GUARDED_BY(Locks::profiler_lock_);
std::string foreign_dex_profile_path_;
std::string app_data_dir_;
- uint64_t code_cache_last_update_time_ns_;
bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
- bool first_profile_ = true;
+ uint32_t last_save_number_of_methods_;
+ uint32_t last_save_number_of_classes__;
+
+ // A local cache for the profile information. Maps each tracked file to its
+ // profile information. The size of this cache is usually very small and tops
+ // to just a few hundreds entries in the ProfileCompilationInfo objects.
+ // It helps avoiding unnecessary writes to disk.
+ SafeMap<std::string, ProfileCompilationInfo> profile_cache_;
// Save period condition support.
Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
ConditionVariable period_condition_ GUARDED_BY(wait_lock_);
+ uint64_t total_bytes_written_;
+ uint64_t total_number_of_writes_;
+ uint64_t total_number_of_code_cache_queries_;
+ uint64_t total_number_of_skipped_writes_;
+ uint64_t total_number_of_failed_writes_;
+ uint64_t total_ms_of_sleep_;
+ uint64_t total_ns_of_work_;
+ uint64_t total_number_of_foreign_dex_marks_;
+ // TODO(calin): replace with an actual size.
+ uint64_t max_number_of_profile_entries_cached_;
+
DISALLOW_COPY_AND_ASSIGN(ProfileSaver);
};
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 472a85c..6a50b8e 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1459,6 +1459,14 @@
return stat(filename.c_str(), &buffer) == 0;
}
+bool FileExistsAndNotEmpty(const std::string& filename) {
+ struct stat buffer;
+ if (stat(filename.c_str(), &buffer) != 0) {
+ return false;
+ }
+ return buffer.st_size > 0;
+}
+
std::string PrettyDescriptor(Primitive::Type type) {
return PrettyDescriptor(Primitive::Descriptor(type));
}
diff --git a/runtime/utils.h b/runtime/utils.h
index 83ac0b8..c1e88a4 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -296,6 +296,7 @@
// Returns true if the file exists.
bool FileExists(const std::string& filename);
+bool FileExistsAndNotEmpty(const std::string& filename);
class VoidFunctor {
public: