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 166912 c4d7092d2e3c8fa8e890d1b75e14de45641bd936
parent 166911 a47615ea036ba85f96f38acd7ce9ba5c14367855
child 166913 dfc688afb0f96a22107eaad4d22ffcb5e0a0c9b2
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbenwa, jld
bugs925111
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);