OSDN Git Service

Perfprofd: various changes related to config parameters.
[android-x86/system-extras.git] / perfprofd / perfprofdcore.cc
index 58773c7..1cf08ad 100644 (file)
 #include <string>
 #include <sstream>
 #include <map>
+#include <set>
 #include <cctype>
 
+#include <base/stringprintf.h>
 #include <cutils/properties.h>
 
 #include "perfprofdcore.h"
@@ -104,11 +106,6 @@ static const char *config_file_path =
     "/data/data/com.google.android.gms/files/perfprofd.conf";
 
 //
-// Set by SIGHUP signal handler
-//
-volatile unsigned please_reread_config_file = 0;
-
-//
 // This table describes the config file syntax in terms of key/value pairs.
 // Values come in two flavors: strings, or unsigned integers. In the latter
 // case the reader sets allowable minimum/maximum for the setting.
@@ -124,7 +121,7 @@ class ConfigReader {
   std::string getStringValue(const char *key) const;
 
   // read the specified config file, applying any settings it contains
-  void readFile();
+  void readFile(bool initial);
 
  private:
   void addUnsignedEntry(const char *key,
@@ -161,7 +158,7 @@ void ConfigReader::addDefaultEntries()
   // set to 100, then over time we want to see a perf profile
   // collected every 100 seconds). The actual time within the interval
   // for the collection is chosen randomly.
-  addUnsignedEntry("collection_interval", 901, 100, UINT32_MAX);
+  addUnsignedEntry("collection_interval", 14400, 100, UINT32_MAX);
 
   // Use the specified fixed seed for random number generation (unit
   // testing)
@@ -203,6 +200,11 @@ void ConfigReader::addDefaultEntries()
   addUnsignedEntry("hardwire_cpus", 1, 0, 1);
   addUnsignedEntry("hardwire_cpus_max_duration", 5, 1, UINT32_MAX);
 
+  // Maximum number of unprocessed profiles we can accumulate in the
+  // destination directory. Once we reach this limit, we continue
+  // to collect, but we just overwrite the most recent profile.
+  addUnsignedEntry("max_unprocessed_profiles", 10, 1, UINT32_MAX);
+
   // If set to 1, pass the -g option when invoking 'perf' (requests
   // stack traces as opposed to flat profile).
   addUnsignedEntry("stack_profile", 0, 0, 1);
@@ -321,11 +323,13 @@ static bool isblank(const std::string &line)
   return true;
 }
 
-void ConfigReader::readFile()
+void ConfigReader::readFile(bool initial)
 {
   FILE *fp = fopen(config_file_path, "r");
   if (!fp) {
-    W_ALOGE("unable to open configuration file %s", config_file_path);
+    if (initial) {
+      W_ALOGE("unable to open configuration file %s", config_file_path);
+    }
     return;
   }
 
@@ -482,7 +486,7 @@ inline char* string_as_array(std::string* str) {
 }
 
 PROFILE_RESULT encode_to_proto(const std::string &data_file_path,
-                               const std::string &encoded_file_path)
+                               const char *encoded_file_path)
 {
   //
   // Open and read perf.data file
@@ -510,7 +514,7 @@ PROFILE_RESULT encode_to_proto(const std::string &data_file_path,
   //
   // Open file and write encoded data to it
   //
-  FILE *fp = fopen(encoded_file_path.c_str(), "w");
+  FILE *fp = fopen(encoded_file_path, "w");
   if (!fp) {
     return ERR_OPEN_ENCODED_FILE_FAILED;
   }
@@ -520,8 +524,7 @@ PROFILE_RESULT encode_to_proto(const std::string &data_file_path,
     return ERR_WRITE_ENCODED_FILE_FAILED;
   }
   fclose(fp);
-  chmod(encoded_file_path.c_str(),
-        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+  chmod(encoded_file_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
 
   return OK_PROFILE_COLLECTION;
 }
@@ -568,8 +571,8 @@ static PROFILE_RESULT invoke_perf(const std::string &perf_path,
 
     // -c N
     argv[slot++] = "-c";
-    char pbuf[64]; snprintf(pbuf, 64, "%u", sampling_period);
-    argv[slot++] = pbuf;
+    std::string p_str = android::base::StringPrintf("%u", sampling_period);
+    argv[slot++] = p_str.c_str();
 
     // -g if desired
     if (stack_profile_opt)
@@ -580,8 +583,8 @@ static PROFILE_RESULT invoke_perf(const std::string &perf_path,
 
     // sleep <duration>
     argv[slot++] = "/system/bin/sleep";
-    char dbuf[64]; snprintf(dbuf, 64, "%u", duration);
-    argv[slot++] = dbuf;
+    std::string d_str = android::base::StringPrintf("%u", duration);
+    argv[slot++] = d_str.c_str();
 
     // terminator
     argv[slot++] = nullptr;
@@ -620,11 +623,95 @@ static PROFILE_RESULT invoke_perf(const std::string &perf_path,
 }
 
 //
+// Remove all files in the destination directory during initialization
+//
+static void cleanup_destination_dir(const ConfigReader &config)
+{
+  std::string dest_dir = config.getStringValue("destination_directory");
+  DIR* dir = opendir(dest_dir.c_str());
+  if (dir != NULL) {
+    struct dirent* e;
+    while ((e = readdir(dir)) != 0) {
+      if (e->d_name[0] != '.') {
+        std::string file_path = dest_dir + "/" + e->d_name;
+        remove(file_path.c_str());
+      }
+    }
+    closedir(dir);
+  } else {
+    W_ALOGW("unable to open destination dir %s for cleanup",
+            dest_dir.c_str());
+  }
+}
+
+//
+// Post-processes after profile is collected and converted to protobuf.
+// * GMS core stores processed file sequence numbers in
+//   /data/data/com.google.android.gms/files/perfprofd_processed.txt
+// * Update /data/misc/perfprofd/perfprofd_produced.txt to remove the sequence
+//   numbers that have been processed and append the current seq number
+// Returns true if the current_seq should increment.
+//
+static bool post_process(const ConfigReader &config, int current_seq)
+{
+  std::string dest_dir = config.getStringValue("destination_directory");
+  std::string processed_file_path =
+      config.getStringValue("config_directory") + "/" + PROCESSED_FILENAME;
+  std::string produced_file_path = dest_dir + "/" + PRODUCED_FILENAME;
+
+
+  std::set<int> processed;
+  FILE *fp = fopen(processed_file_path.c_str(), "r");
+  if (fp != NULL) {
+    int seq;
+    while(fscanf(fp, "%d\n", &seq) > 0) {
+      if (remove(android::base::StringPrintf(
+          "%s/perf.data.encoded.%d", dest_dir.c_str(),seq).c_str()) == 0) {
+        processed.insert(seq);
+      }
+    }
+    fclose(fp);
+  }
+
+  std::set<int> produced;
+  fp = fopen(produced_file_path.c_str(), "r");
+  if (fp != NULL) {
+    int seq;
+    while(fscanf(fp, "%d\n", &seq) > 0) {
+      if (processed.find(seq) == processed.end()) {
+        produced.insert(seq);
+      }
+    }
+    fclose(fp);
+  }
+
+  unsigned maxLive = config.getUnsignedValue("max_unprocessed_profiles");
+  if (produced.size() >= maxLive) {
+    return false;
+  }
+
+  produced.insert(current_seq);
+  fp = fopen(produced_file_path.c_str(), "w");
+  if (fp == NULL) {
+    W_ALOGW("Cannot write %s", produced_file_path.c_str());
+    return false;
+  }
+  for (std::set<int>::const_iterator iter = produced.begin();
+       iter != produced.end(); ++iter) {
+    fprintf(fp, "%d\n", *iter);
+  }
+  fclose(fp);
+  chmod(produced_file_path.c_str(),
+        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+  return true;
+}
+
+//
 // Collect a perf profile. Steps for this operation are:
 // - kick off 'perf record'
 // - read perf.data, convert to protocol buf
 //
-static PROFILE_RESULT collect_profile(ConfigReader &config)
+static PROFILE_RESULT collect_profile(const ConfigReader &config, int seq)
 {
   //
   // Form perf.data file name, perf error output file name
@@ -687,18 +774,17 @@ static PROFILE_RESULT collect_profile(ConfigReader &config)
   // Read the resulting perf.data file, encode into protocol buffer, then write
   // the result to the file perf.data.encoded
   //
-  std::string encoded_file_path(data_file_path);
-  encoded_file_path += ".encoded";
-  return encode_to_proto(data_file_path, encoded_file_path);
+  std::string path = android::base::StringPrintf(
+      "%s.encoded.%d", data_file_path.c_str(), seq);
+  return encode_to_proto(data_file_path, path.c_str());
 }
 
 //
-// SIGHUP handler. Sets a flag to indicate that we should reread the
-// config file
+// SIGHUP handler. Sending SIGHUP to the daemon can be used to break it
+// out of a sleep() call so as to trigger a new collection (debugging)
 //
 static void sig_hup(int /* signum */)
 {
-  please_reread_config_file = 1;
 }
 
 //
@@ -747,8 +833,9 @@ static void set_seed(ConfigReader &config)
 //
 static void init(ConfigReader &config)
 {
-  config.readFile();
+  config.readFile(true);
   set_seed(config);
+  cleanup_destination_dir(config);
 
   char propBuf[PROPERTY_VALUE_MAX];
   propBuf[0] = '\0';
@@ -786,6 +873,7 @@ int perfprofd_main(int argc, char** argv)
   }
 
   unsigned iterations = 0;
+  int seq = 0;
   while(config.getUnsignedValue("main_loop_iterations") == 0 ||
         iterations < config.getUnsignedValue("main_loop_iterations")) {
 
@@ -797,11 +885,9 @@ int perfprofd_main(int argc, char** argv)
                            config.getUnsignedValue("collection_interval"));
     perfprofd_sleep(sleep_before_collect);
 
-    // Reread config file if someone sent a SIGHUP
-    if (please_reread_config_file) {
-      config.readFile();
-      please_reread_config_file = 0;
-    }
+    // Reread config file -- the uploader may have rewritten it as a result
+    // of a gservices change
+    config.readFile(false);
 
     // Check for profiling enabled...
     CKPROFILE_RESULT ckresult = check_profiling_enabled(config);
@@ -811,11 +897,14 @@ int perfprofd_main(int argc, char** argv)
     } else {
       // Kick off the profiling run...
       W_ALOGI("initiating profile collection");
-      PROFILE_RESULT result = collect_profile(config);
+      PROFILE_RESULT result = collect_profile(config, seq);
       if (result != OK_PROFILE_COLLECTION) {
         W_ALOGI("profile collection failed (%s)",
                 profile_result_to_string(result));
       } else {
+        if (post_process(config, seq)) {
+          seq++;
+        }
         W_ALOGI("profile collection complete");
       }
     }