Clean up the sampling profiler
- rename variables/fields names to match the code style (use
_underscore_names_)
- extract common property parsing in utils.cc
- fail to load profile file if any line is malformed
- added ProfileFile to manage the profile data generate in the previous
runs (replaces ProfileHelper and nests ProfileData)
Bug: 12877748
Change-Id: Ie7bda30bfdeb7e78534c986615b0649eac12a97b
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 8d4e283..15a086b 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -35,6 +35,7 @@
#include "driver/compiler_options.h"
#include "jni_internal.h"
#include "object_utils.h"
+#include "profiler.h"
#include "runtime.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
@@ -54,13 +55,10 @@
#include "thread_pool.h"
#include "trampolines/trampoline_compiler.h"
#include "transaction.h"
+#include "utils.h"
#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
namespace art {
static double Percentage(size_t x, size_t y) {
@@ -369,7 +367,7 @@
// Read the profile file if one is provided.
if (profile_file != "") {
- profile_ok_ = ProfileHelper::LoadProfileMap(profile_map_, profile_file);
+ profile_ok_ = profile_file_.LoadFile(profile_file);
}
dex_to_dex_compiler_ = reinterpret_cast<DexToDexCompilerFn>(ArtCompileDEX);
@@ -2049,36 +2047,27 @@
if (!profile_ok_) {
return false;
}
- // Methods that comprise topKPercentThreshold % of the total samples will be compiled.
- double topKPercentThreshold = 90.0;
-#ifdef HAVE_ANDROID_OS
- char buf[PROP_VALUE_MAX];
- property_get("dalvik.vm.profile.compile_thr", buf, "90.0");
- topKPercentThreshold = strtod(buf, nullptr);
-#endif
- // Test for reasonable thresholds.
- if (topKPercentThreshold < 10.0 || topKPercentThreshold > 90.0) {
- topKPercentThreshold = 90.0;
- }
-
- // First find the method in the profile map.
- ProfileMap::iterator i = profile_map_.find(method_name);
- if (i == profile_map_.end()) {
+ // First find the method in the profile file.
+ ProfileFile::ProfileData data;
+ if (!profile_file_.GetProfileData(&data, method_name)) {
// Not in profile, no information can be determined.
VLOG(compiler) << "not compiling " << method_name << " because it's not in the profile";
return true;
}
- const ProfileData& data = i->second;
+
+ // Methods that comprise top_k_threshold % of the total samples will be compiled.
+ double top_k_threshold = GetDoubleProperty("dalvik.vm.profiler.compile_thr", 10.0, 90.0, 90.0);
// Compare against the start of the topK percentage bucket just in case the threshold
// falls inside a bucket.
- bool compile = data.GetTopKUsedPercentage() - data.GetUsedPercent() <= topKPercentThreshold;
+ bool compile = data.GetTopKUsedPercentage() - data.GetUsedPercent() <= top_k_threshold;
if (compile) {
LOG(INFO) << "compiling method " << method_name << " because its usage is part of top "
- << data.GetTopKUsedPercentage() << "% with a percent of " << data.GetUsedPercent() << "%";
+ << data.GetTopKUsedPercentage() << "% with a percent of " << data.GetUsedPercent() << "%"
+ << " (topKThreshold=" << top_k_threshold << ")";
} else {
VLOG(compiler) << "not compiling method " << method_name << " because it's not part of leading "
- << topKPercentThreshold << "% samples)";
+ << top_k_threshold << "% samples)";
}
return !compile;
}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 14ccb50..e952f63 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -595,7 +595,7 @@
return cfi_info_.get();
}
- ProfileMap profile_map_;
+ ProfileFile profile_file_;
bool profile_ok_;
// Should the compiler run on this method given profile information?
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index a0a294a..585c88e 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -37,13 +37,10 @@
#include "scoped_thread_state_change.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
+#include "utils.h"
#include "well_known_classes.h"
#include "zip_archive.h"
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
namespace art {
// A smart pointer that provides read-only access to a Java string's UTF chars.
@@ -250,25 +247,6 @@
close(fd2);
}
-static double GetDoubleProperty(const char* property, double minValue, double maxValue, double defaultValue) {
-#ifndef HAVE_ANDROID_OS
- return defaultValue;
-#else
- char buf[PROP_VALUE_MAX];
- char* endptr;
-
- property_get(property, buf, "");
- double value = strtod(buf, &endptr);
-
- if (value == 0 && endptr == buf) {
- value = defaultValue;
- } else if (value < minValue || value > maxValue) {
- value = defaultValue;
- }
- return value;
-#endif
-}
-
static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename,
const char* pkgname, const char* instruction_set, const jboolean defer) {
const bool kVerboseLogging = false; // Spammy logging.
@@ -379,42 +357,46 @@
// There is a previous profile file. Check if the profile has changed significantly.
// A change in profile is considered significant if X% (change_thr property) of the top K%
// (compile_thr property) samples has changed.
-
- double topKThreshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.compile_thr", 10.0, 90.0, 90.0);
- double changeThreshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.change_thr", 1.0, 90.0, 10.0);
- double changePercent = 0.0;
- std::set<std::string> newTopK, oldTopK;
- bool newOk = ProfileHelper::LoadTopKSamples(newTopK, profile_file, topKThreshold);
- bool oldOk = ProfileHelper::LoadTopKSamples(oldTopK, prev_profile_file, topKThreshold);
- if (!newOk || !oldOk) {
+ double top_k_threshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.compile_thr", 10.0, 90.0, 90.0);
+ double change_threshold = GetDoubleProperty("dalvik.vm.profiler.dex2oat.change_thr", 1.0, 90.0, 10.0);
+ double change_percent = 0.0;
+ ProfileFile new_profile, old_profile;
+ bool new_ok = new_profile.LoadFile(profile_file);
+ bool old_ok = old_profile.LoadFile(prev_profile_file);
+ if (!new_ok || !old_ok) {
if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded Ignoring invalid profiles: "
- << (newOk ? "" : profile_file) << " " << (oldOk ? "" : prev_profile_file);
+ << (new_ok ? "" : profile_file) << " " << (old_ok ? "" : prev_profile_file);
}
- } else if (newTopK.empty()) {
- if (kVerboseLogging) {
- LOG(INFO) << "DexFile_isDexOptNeeded empty profile: " << profile_file;
- }
- // If the new topK is empty we shouldn't optimize so we leave the changePercent at 0.0.
} else {
- std::set<std::string> diff;
- std::set_difference(newTopK.begin(), newTopK.end(), oldTopK.begin(), oldTopK.end(),
- std::inserter(diff, diff.end()));
- // TODO: consider using the usedPercentage instead of the plain diff count.
- changePercent = 100.0 * static_cast<double>(diff.size()) / static_cast<double>(newTopK.size());
- if (kVerboseLogging) {
- std::set<std::string>::iterator end = diff.end();
- for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
- LOG(INFO) << "DexFile_isDexOptNeeded new in topK: " << *it;
+ std::set<std::string> new_top_k, old_top_k;
+ new_profile.GetTopKSamples(new_top_k, top_k_threshold);
+ old_profile.GetTopKSamples(old_top_k, top_k_threshold);
+ if (new_top_k.empty()) {
+ if (kVerboseLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded empty profile: " << profile_file;
+ }
+ // If the new topK is empty we shouldn't optimize so we leave the change_percent at 0.0.
+ } else {
+ std::set<std::string> diff;
+ std::set_difference(new_top_k.begin(), new_top_k.end(), old_top_k.begin(), old_top_k.end(),
+ std::inserter(diff, diff.end()));
+ // TODO: consider using the usedPercentage instead of the plain diff count.
+ change_percent = 100.0 * static_cast<double>(diff.size()) / static_cast<double>(new_top_k.size());
+ if (kVerboseLogging) {
+ std::set<std::string>::iterator end = diff.end();
+ for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
+ LOG(INFO) << "DexFile_isDexOptNeeded new in topK: " << *it;
+ }
}
}
}
- if (changePercent > changeThreshold) {
+ if (change_percent > change_threshold) {
if (kReasonLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded size of new profile file " << profile_file <<
" is significantly different from old profile file " << prev_profile_file << " (top "
- << topKThreshold << "% samples changed in proportion of " << changePercent << "%)";
+ << top_k_threshold << "% samples changed in proportion of " << change_percent << "%)";
}
if (!defer) {
CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index 5459ce3..75db9da 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -75,8 +75,6 @@
profiler->RecordMethod(method);
}
-
-
// A closure that is called by the thread checkpoint code.
class SampleCheckpoint : public Closure {
public:
@@ -443,7 +441,7 @@
}
}
-// Add a method to the profile table. If it the first time the method
+// Add a method to the profile table. If it's the first time the method
// has been seen, add it with count=1, otherwise increment the count.
void ProfileSampleResults::Put(mirror::ArtMethod* method) {
lock_.Lock(Thread::Current());
@@ -578,7 +576,7 @@
}
}
-bool ProfileHelper::LoadProfileMap(ProfileMap& profileMap, const std::string& fileName) {
+bool ProfileFile::LoadFile(const std::string& fileName) {
LOG(VERBOSE) << "reading profile file " << fileName;
struct stat st;
int err = stat(fileName.c_str(), &st);
@@ -629,7 +627,7 @@
Split(line, '/', info);
if (info.size() != 3) {
// Malformed.
- break;
+ return false;
}
int count = atoi(info[1].c_str());
countSet.insert(std::make_pair(-count, info));
@@ -652,21 +650,24 @@
// Add it to the profile map.
ProfileData curData = ProfileData(methodname, count, size, usedPercent, topKPercentage);
- profileMap[methodname] = curData;
+ profile_map_[methodname] = curData;
prevData = &curData;
}
return true;
}
-bool ProfileHelper::LoadTopKSamples(std::set<std::string>& topKSamples, const std::string& fileName,
- double topKPercentage) {
- ProfileMap profileMap;
- bool loadOk = LoadProfileMap(profileMap, fileName);
- if (!loadOk) {
+bool ProfileFile::GetProfileData(ProfileFile::ProfileData* data, const std::string& method_name) {
+ ProfileMap::iterator i = profile_map_.find(method_name);
+ if (i == profile_map_.end()) {
return false;
}
- ProfileMap::iterator end = profileMap.end();
- for (ProfileMap::iterator it = profileMap.begin(); it != end; it++) {
+ *data = i->second;
+ return true;
+}
+
+bool ProfileFile::GetTopKSamples(std::set<std::string>& topKSamples, double topKPercentage) {
+ ProfileMap::iterator end = profile_map_.end();
+ for (ProfileMap::iterator it = profile_map_.begin(); it != end; it++) {
if (it->second.GetTopKUsedPercentage() < topKPercentage) {
topKSamples.insert(it->first);
}
diff --git a/runtime/profiler.h b/runtime/profiler.h
index 938fdb7..2502289 100644
--- a/runtime/profiler.h
+++ b/runtime/profiler.h
@@ -188,52 +188,50 @@
DISALLOW_COPY_AND_ASSIGN(BackgroundMethodSamplingProfiler);
};
-// TODO: incorporate in ProfileSampleResults
-
-// Profile data. This is generated from previous runs of the program and stored
+// Contains profile data generated from previous runs of the program and stored
// in a file. It is used to determine whether to compile a particular method or not.
-class ProfileData {
+class ProfileFile {
public:
- ProfileData() : count_(0), method_size_(0), usedPercent_(0) {}
- ProfileData(const std::string& method_name, uint32_t count, uint32_t method_size,
- double usedPercent, double topKUsedPercentage) :
- method_name_(method_name), count_(count), method_size_(method_size),
- usedPercent_(usedPercent), topKUsedPercentage_(topKUsedPercentage) {
- // TODO: currently method_size_ and count_ are unused.
- UNUSED(method_size_);
- UNUSED(count_);
- }
+ class ProfileData {
+ public:
+ ProfileData() : count_(0), method_size_(0), used_percent_(0) {}
+ ProfileData(const std::string& method_name, uint32_t count, uint32_t method_size,
+ double used_percent, double top_k_used_percentage) :
+ method_name_(method_name), count_(count), method_size_(method_size),
+ used_percent_(used_percent), top_k_used_percentage_(top_k_used_percentage) {
+ // TODO: currently method_size_ is unused
+ UNUSED(method_size_);
+ }
- bool IsAbove(double v) const { return usedPercent_ >= v; }
- double GetUsedPercent() const { return usedPercent_; }
- uint32_t GetCount() const { return count_; }
- double GetTopKUsedPercentage() const { return topKUsedPercentage_; }
+ double GetUsedPercent() const { return used_percent_; }
+ uint32_t GetCount() const { return count_; }
+ double GetTopKUsedPercentage() const { return top_k_used_percentage_; }
- private:
- std::string method_name_; // Method name.
- uint32_t count_; // Number of times it has been called.
- uint32_t method_size_; // Size of the method on dex instructions.
- double usedPercent_; // Percentage of how many times this method was called.
- double topKUsedPercentage_; // The percentage of the group that comprise K% of the total used
- // methods this methods belongs to.
-};
-
-// Profile data is stored in a map, indexed by the full method name.
-typedef std::map<std::string, ProfileData> ProfileMap;
-
-class ProfileHelper {
- private:
- ProfileHelper();
+ private:
+ std::string method_name_; // Method name.
+ uint32_t count_; // Number of times it has been called.
+ uint32_t method_size_; // Size of the method on dex instructions.
+ double used_percent_; // Percentage of how many times this method was called.
+ double top_k_used_percentage_; // The percentage of the group that comprise K% of the total used
+ // methods this methods belongs to.
+ };
public:
- // Read the profile data from the given file. Calculates the percentage for each method.
- // Returns false if there was no profile file or it was malformed.
- static bool LoadProfileMap(ProfileMap& profileMap, const std::string& fileName);
+ // Loads profile data from the given file. The data are merged with any existing data.
+ // Returns true if the file was loaded successfully and false otherwise.
+ bool LoadFile(const std::string& filename);
- // Read the profile data from the given file and computes the group that comprise
- // topKPercentage of the total used methods.
- static bool LoadTopKSamples(std::set<std::string>& topKMethods, const std::string& fileName,
- double topKPercentage);
+ // Computes the group that comprise top_k_percentage of the total used methods.
+ bool GetTopKSamples(std::set<std::string>& top_k_methods, double top_k_percentage);
+
+ // If the given method has an entry in the profile table it updates the data
+ // and returns true. Otherwise returns false and leaves the data unchanged.
+ bool GetProfileData(ProfileData* data, const std::string& method_name);
+
+ private:
+ // Profile data is stored in a map, indexed by the full method name.
+ typedef std::map<std::string, ProfileData> ProfileMap;
+ ProfileMap profile_map_;
};
} // namespace art
diff --git a/runtime/utils.cc b/runtime/utils.cc
index f562252..05ff5ff 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1302,4 +1302,23 @@
return true;
}
+double GetDoubleProperty(const char* property, double min_value, double max_value, double default_value) {
+#ifndef HAVE_ANDROID_OS
+ return default_value;
+#else
+ char buf[PROP_VALUE_MAX];
+ char* endptr;
+
+ property_get(property, buf, "");
+ double value = strtod(buf, &endptr);
+
+ // Return the defalt value if the string is invalid or the value is outside the given range
+ if ((value == 0 && endptr == buf) // Invalid string
+ || (value < min_value) || (value > max_value)) { // Out of range value
+ return default_value;
+ }
+ return value;
+#endif
+}
+
} // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index 4a9236a..0f9b22b 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -28,6 +28,10 @@
#include "instruction_set.h"
#include "primitive.h"
+#ifdef HAVE_ANDROID_OS
+#include "cutils/properties.h"
+#endif
+
namespace art {
class DexFile;
@@ -439,6 +443,10 @@
}
};
+// Returns the given property as a double or its default_value if the property string is not valid
+// or the parsed value is outside the interval [min_value, max_value].
+double GetDoubleProperty(const char* property, double min_value, double max_value, double default_value);
+
} // namespace art
#endif // ART_RUNTIME_UTILS_H_