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_