Bug 925111 - Enable Profiler to support config options during runtime. r=benwa, jld

This commit is contained in:
Mason Chang 2013-10-24 17:09:33 -07:00
parent 62829542b3
commit 575461dbb9
3 changed files with 198 additions and 78 deletions

View File

@ -456,39 +456,93 @@ void Sampler::UnregisterCurrentThread()
static struct sigaction old_sigstart_signal_handler;
const int SIGSTART = SIGUSR2;
static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
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 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";
uint32_t threadCount = 0;
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();
// 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";
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()

View File

@ -46,6 +46,13 @@ 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;
@ -252,68 +259,112 @@ static inline const char* name_UnwMode(UnwMode m)
}
}
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 (!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 (strI) {
errno = 0;
long int n = strtol(strI, (char**)NULL, 10);
if (errno == 0 && n >= 1 && n <= 1000) {
sUnwindInterval = n;
}
else goto usage;
}
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: ");
@ -339,15 +390,15 @@ void read_profiler_env_vars()
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\")",

View File

@ -240,7 +240,22 @@ class Thread {
/* 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 */