Bug 925111 - Enable Profiler to support config options during runtime. r=benwa, jld
authorMason Chang <mchang@mozilla.com>
Thu, 24 Oct 2013 17:09:33 -0700
changeset 152145 c4d7092d2e3c8fa8e890d1b75e14de45641bd936
parent 152144 a47615ea036ba85f96f38acd7ce9ba5c14367855
child 152146 dfc688afb0f96a22107eaad4d22ffcb5e0a0c9b2
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbenwa, jld
bugs925111
milestone27.0a1
Bug 925111 - Enable Profiler to support config options during runtime. r=benwa, jld
tools/profiler/platform-linux.cc
tools/profiler/platform.cpp
tools/profiler/platform.h
--- a/tools/profiler/platform-linux.cc
+++ b/tools/profiler/platform-linux.cc
@@ -451,49 +451,103 @@ void Sampler::UnregisterCurrentThread()
 
   uwt__unregister_thread_for_profiling();
 }
 
 #ifdef ANDROID
 static struct sigaction old_sigstart_signal_handler;
 const int SIGSTART = SIGUSR2;
 
+static void freeArray(const char** array, int size) {
+  for (int i = 0; i < size; i++) {
+    free((void*) array[i]);
+  }
+}
+
+static uint32_t readCSVArray(char* csvList, const char** buffer) {
+  uint32_t count;
+  char* savePtr;
+  int newlinePos = strlen(csvList) - 1;
+  if (csvList[newlinePos] == '\n') {
+    csvList[newlinePos] = '\0';
+  }
+
+  char* item = strtok_r(csvList, ",", &savePtr);
+  for (count = 0; item; item = strtok_r(NULL, ",", &savePtr)) {
+    int length = strlen(item) + 1;  // Include \0
+    char* newBuf = (char*) malloc(sizeof(char) * length);
+    buffer[count] = newBuf;
+    strncpy(newBuf, item, length);
+    count++;
+  }
+
+  return count;
+}
+
+// Currently support only the env variables
+// reported in read_profiler_env
+static void ReadProfilerVars(const char* fileName, const char** features,
+                            uint32_t* featureCount, const char** threadNames, uint32_t* threadCount) {
+  FILE* file = fopen(fileName, "r");
+  const int bufferSize = 1024;
+  char line[bufferSize];
+  char* feature;
+  char* value;
+  char* savePtr;
+
+  if (file) {
+    while (fgets(line, bufferSize, file) != NULL) {
+      feature = strtok_r(line, "=", &savePtr);
+      value = strtok_r(NULL, "", &savePtr);
+
+      if (strncmp(feature, PROFILER_MODE, bufferSize) == 0) {
+        set_profiler_mode(value);
+      } else if (strncmp(feature, PROFILER_INTERVAL, bufferSize) == 0) {
+        set_profiler_interval(value);
+      } else if (strncmp(feature, PROFILER_ENTRIES, bufferSize) == 0) {
+        set_profiler_entries(value);
+      } else if (strncmp(feature, PROFILER_STACK, bufferSize) == 0) {
+        set_profiler_scan(value);
+      } else if (strncmp(feature, PROFILER_FEATURES, bufferSize) == 0) {
+        *featureCount = readCSVArray(value, features);
+      } else if (strncmp(feature, "threads", bufferSize) == 0) {
+        *threadCount = readCSVArray(value, threadNames);
+      }
+    }
+
+    fclose(file);
+  }
+}
+
+
 static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
-
   // XXX: Everything we do here is NOT async signal safe. We risk nasty things
   // like deadlocks but we typically only do this once so it tends to be ok.
   // See bug 909403
-  const char* threadName = NULL;
+  uint32_t featureCount = 0;
   uint32_t threadCount = 0;
-  char thread[256];
 
-  // TODO support selecting features from profiler.options
-  const char* features[3] = {NULL, NULL, NULL};
-  uint32_t featureCount = 0;
-  features[0] = "leaf";
-  featureCount++;
-  features[1] = "js";
-  featureCount++;
-  const char* threadFeature = "threads";
+  // Just allocate 10 features for now
+  // FIXME: these don't really point to const chars*
+  // So we free them later, but we don't want to change the const char**
+  // declaration in profiler_start. Annoying but ok for now.
+  const char* threadNames[10];
+  const char* features[10];
+  const char* profilerConfigFile = "/data/local/tmp/profiler.options";
 
-  std::ifstream infile;
-  infile.open("/data/local/tmp/profiler.options");
-  if (infile.is_open()) {
-    infile.getline(thread, 256);
-    threadName = thread;
-    threadCount = 1;
-    features[featureCount] = threadFeature;
-    featureCount++;
-    printf_stderr("Profiling only %s\n", threadName);
-  }
-  infile.close();
+  ReadProfilerVars(profilerConfigFile, features, &featureCount, threadNames, &threadCount);
+  MOZ_ASSERT(featureCount < 10);
+  MOZ_ASSERT(threadCount < 10);
 
   profiler_start(PROFILE_DEFAULT_ENTRY, 1,
-                 features, featureCount,
-                 &threadName, threadCount);
+      features, featureCount,
+      threadNames, threadCount);
+
+  freeArray(threadNames, threadCount);
+  freeArray(features, featureCount);
 }
 
 void OS::RegisterStartHandler()
 {
   LOG("Registering start signal");
   struct sigaction sa;
   sa.sa_sigaction = StartSignalHandler;
   sigemptyset(&sa.sa_mask);
--- a/tools/profiler/platform.cpp
+++ b/tools/profiler/platform.cpp
@@ -41,16 +41,23 @@ bool stack_key_initialized;
 
 TimeStamp   sLastTracerEvent; // is raced on
 TimeStamp   sStartTime;
 int         sFrameNumber = 0;
 int         sLastFrameNumber = 0;
 int         sInitCount = 0; // Each init must have a matched shutdown.
 static bool sIsProfiling = false; // is raced on
 
+// env variables to control the profiler
+const char* PROFILER_MODE = "MOZ_PROFILER_MODE";
+const char* PROFILER_INTERVAL = "MOZ_PROFILER_INTERVAL";
+const char* PROFILER_ENTRIES = "MOZ_PROFILER_ENTRIES";
+const char* PROFILER_STACK = "MOZ_PROFILER_STACK_SCAN";
+const char* PROFILER_FEATURES = "MOZ_PROFILING_FEATURES";
+
 /* used to keep track of the last event that we sampled during */
 unsigned int sLastSampledEventGeneration = 0;
 
 /* a counter that's incremented everytime we get responsiveness event
  * note: it might also be worth trackplaing everytime we go around
  * the event loop */
 unsigned int sCurrentEventGeneration = 0;
 /* we don't need to worry about overflow because we only treat the
@@ -247,78 +254,122 @@ static inline const char* name_UnwMode(U
     case UnwINVALID:  return "invalid";
     case UnwNATIVE:   return "native";
     case UnwPSEUDO:   return "pseudo";
     case UnwCOMBINED: return "combined";
     default:          return "??name_UnwMode??";
   }
 }
 
+bool set_profiler_mode(const char* mode) {
+  if (mode) {
+    if (0 == strcmp(mode, "pseudo")) {
+      sUnwindMode = UnwPSEUDO;
+      return true;
+    }
+    else if (0 == strcmp(mode, "native") && is_native_unwinding_avail()) {
+      sUnwindMode = UnwNATIVE;
+      return true;
+    }
+    else if (0 == strcmp(mode, "combined") && is_native_unwinding_avail()) {
+      sUnwindMode = UnwCOMBINED;
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool set_profiler_interval(const char* interval) {
+  if (interval) {
+    errno = 0;
+    long int n = strtol(interval, (char**)NULL, 10);
+    if (errno == 0 && n >= 1 && n <= 1000) {
+      sUnwindInterval = n;
+      return true;
+    }
+    return false;
+  }
+
+  return true;
+}
+
+bool set_profiler_entries(const char* entries) {
+  if (entries) {
+    errno = 0;
+    long int n = strtol(entries, (char**)NULL, 10);
+    if (errno == 0 && n > 0) {
+      sProfileEntries = n;
+      return true;
+    }
+    return false;
+  }
+
+  return true;
+}
+
+bool set_profiler_scan(const char* scanCount) {
+  if (scanCount) {
+    errno = 0;
+    long int n = strtol(scanCount, (char**)NULL, 10);
+    if (errno == 0 && n >= 0 && n <= 100) {
+      sUnwindStackScan = n;
+      return true;
+    }
+    return false;
+  }
+
+  return true;
+}
+
+bool is_native_unwinding_avail() {
+# if defined(HAVE_NATIVE_UNWIND)
+  return true;
+#else
+  return false;
+#endif
+}
+
 // Read env vars at startup, so as to set sUnwindMode and sInterval.
 void read_profiler_env_vars()
 {
-  bool nativeAvail = false;
-# if defined(HAVE_NATIVE_UNWIND)
-  nativeAvail = true;
-# endif
-
-  MOZ_ASSERT(sUnwindMode     == UnwINVALID);
-  MOZ_ASSERT(sUnwindInterval == 0);
-  MOZ_ASSERT(sProfileEntries == 0);
+  bool nativeAvail = is_native_unwinding_avail();
 
   /* Set defaults */
   sUnwindMode     = nativeAvail ? UnwCOMBINED : UnwPSEUDO;
   sUnwindInterval = 0;  /* We'll have to look elsewhere */
   sProfileEntries = 0;
 
-  const char* strM = PR_GetEnv("MOZ_PROFILER_MODE");
-  const char* strI = PR_GetEnv("MOZ_PROFILER_INTERVAL");
-  const char* strE = PR_GetEnv("MOZ_PROFILER_ENTRIES");
-  const char* strF = PR_GetEnv("MOZ_PROFILER_STACK_SCAN");
+  const char* stackMode = PR_GetEnv(PROFILER_MODE);
+  const char* interval = PR_GetEnv(PROFILER_INTERVAL);
+  const char* entries = PR_GetEnv(PROFILER_ENTRIES);
+  const char* scanCount = PR_GetEnv(PROFILER_STACK);
 
-  if (strM) {
-    if (0 == strcmp(strM, "pseudo"))
-      sUnwindMode = UnwPSEUDO;
-    else if (0 == strcmp(strM, "native") && nativeAvail)
-      sUnwindMode = UnwNATIVE;
-    else if (0 == strcmp(strM, "combined") && nativeAvail)
-      sUnwindMode = UnwCOMBINED;
-    else goto usage;
-  }
-
-  if (strI) {
-    errno = 0;
-    long int n = strtol(strI, (char**)NULL, 10);
-    if (errno == 0 && n >= 1 && n <= 1000) {
-      sUnwindInterval = n;
-    }
-    else goto usage;
+  if (!set_profiler_mode(stackMode) ||
+      !set_profiler_interval(interval) ||
+      !set_profiler_entries(entries) ||
+      !set_profiler_scan(scanCount)) {
+      profiler_usage();
+  } else {
+    LOG( "SPS:");
+    LOGF("SPS: Unwind mode       = %s", name_UnwMode(sUnwindMode));
+    LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")",
+        (int)sUnwindInterval);
+    LOGF("SPS: Entry store size  = %d (zero means \"platform default\")",
+        (int)sProfileEntries);
+    LOGF("SPS: UnwindStackScan   = %d (max dubious frames per unwind).",
+        (int)sUnwindStackScan);
+    LOG( "SPS: Use env var MOZ_PROFILER_MODE=help for further information.");
+    LOG( "SPS:");
   }
-
-  if (strE) {
-    errno = 0;
-    long int n = strtol(strE, (char**)NULL, 10);
-    if (errno == 0 && n > 0) {
-      sProfileEntries = n;
-    }
-    else goto usage;
-  }
+}
 
-  if (strF) {
-    errno = 0;
-    long int n = strtol(strF, (char**)NULL, 10);
-    if (errno == 0 && n >= 0 && n <= 100) {
-      sUnwindStackScan = n;
-    }
-    else goto usage;
-  }
-
-  goto out;
-
- usage:
+void profiler_usage() {
   LOG( "SPS: ");
   LOG( "SPS: Environment variable usage:");
   LOG( "SPS: ");
   LOG( "SPS:   MOZ_PROFILER_MODE=native    for native unwind only");
   LOG( "SPS:   MOZ_PROFILER_MODE=pseudo    for pseudo unwind only");
   LOG( "SPS:   MOZ_PROFILER_MODE=combined  for combined native & pseudo unwind");
   LOG( "SPS:   If unset, default is 'combined' on native-capable");
   LOG( "SPS:     platforms, 'pseudo' on others.");
@@ -334,25 +385,25 @@ void read_profiler_env_vars()
   LOG( "SPS: ");
   LOG( "SPS:   MOZ_PROFILER_STACK_SCAN=<number>   (default is zero)");
   LOG( "SPS:   The number of dubious (stack-scanned) frames allowed");
   LOG( "SPS: ");
   LOG( "SPS:   MOZ_PROFILER_NEW");
   LOG( "SPS:   Needs to be set to use Breakpad-based unwinding.");
   LOG( "SPS: ");
   LOGF("SPS:   This platform %s native unwinding.",
-       nativeAvail ? "supports" : "does not support");
+       is_native_unwinding_avail() ? "supports" : "does not support");
   LOG( "SPS: ");
+
   /* Re-set defaults */
-  sUnwindMode       = nativeAvail ? UnwCOMBINED : UnwPSEUDO;
+  sUnwindMode       = is_native_unwinding_avail() ? UnwCOMBINED : UnwPSEUDO;
   sUnwindInterval   = 0;  /* We'll have to look elsewhere */
   sProfileEntries   = 0;
   sUnwindStackScan  = 0;
 
- out:
   LOG( "SPS:");
   LOGF("SPS: Unwind mode       = %s", name_UnwMode(sUnwindMode));
   LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")",
        (int)sUnwindInterval);
   LOGF("SPS: Entry store size  = %d (zero means \"platform default\")",
        (int)sProfileEntries);
   LOGF("SPS: UnwindStackScan   = %d (max dubious frames per unwind).",
        (int)sUnwindStackScan);
--- a/tools/profiler/platform.h
+++ b/tools/profiler/platform.h
@@ -235,17 +235,32 @@ class Thread {
         || defined(SPS_PLAT_x86_linux) \
         || defined(SPS_OS_windows) \
         || defined(SPS_OS_darwin))
 # define HAVE_NATIVE_UNWIND
 #endif
 
 /* Some values extracted at startup from environment variables, that
    control the behaviour of the breakpad unwinder. */
+extern const char* PROFILER_MODE;
+extern const char* PROFILER_INTERVAL;
+extern const char* PROFILER_ENTRIES;
+extern const char* PROFILER_STACK;
+extern const char* PROFILER_FEATURES;
+
 void read_profiler_env_vars();
+void profiler_usage();
+
+// Helper methods to expose modifying profiler behavior
+bool set_profiler_mode(const char*);
+bool set_profiler_interval(const char*);
+bool set_profiler_entries(const char*);
+bool set_profiler_scan(const char*);
+bool is_native_unwinding_avail();
+
 typedef  enum { UnwINVALID, UnwNATIVE, UnwPSEUDO, UnwCOMBINED }  UnwMode;
 extern UnwMode sUnwindMode;       /* what mode? */
 extern int     sUnwindInterval;   /* in milliseconds */
 extern int     sUnwindStackScan;  /* max # of dubious frames allowed */
 
 extern int     sProfileEntries;   /* how many entries do we store? */
 
 void set_tls_stack_top(void* stackTop);