Bug 707308 - Add Dynamic profiling labels. r=jmuizel,ehsan

--HG--
extra : rebase_source : 1a72148342e7a71de62230ac2ca3a283724e4bbd
This commit is contained in:
Benoit Girard 2012-06-26 23:25:14 -04:00
parent eac4d6b53f
commit 1a3591793e
3 changed files with 155 additions and 18 deletions

View File

@ -68,6 +68,7 @@ using namespace mozilla;
#define snprintf _snprintf
#endif
static const int DYNAMIC_MAX_STRING = 512;
mozilla::ThreadLocal<ProfileStack *> tlsStack;
mozilla::ThreadLocal<TableTicker *> tlsTicker;
@ -121,6 +122,7 @@ private:
friend class ThreadProfile;
union {
const char* mTagData;
char mTagChars[sizeof(void*)];
void* mTagPtr;
double mTagFloat;
Address mTagAddress;
@ -226,6 +228,30 @@ public:
mWritePos = mLastFlushPos;
}
char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff)
{
int readAheadPos = (readPos + 1) % mEntrySize;
int tagBuffPos = 0;
// Read the string stored in mTagData until the null character is seen
bool seenNullByte = false;
while (readAheadPos != mLastFlushPos && !seenNullByte) {
(*tagsConsumed)++;
ProfileEntry readAheadEntry = mEntries[readAheadPos];
for (size_t pos = 0; pos < sizeof(void*); pos++) {
tagBuff[tagBuffPos] = readAheadEntry.mTagChars[pos];
if (tagBuff[tagBuffPos] == '\0' || tagBuffPos == DYNAMIC_MAX_STRING-2) {
seenNullByte = true;
break;
}
tagBuffPos++;
}
if (!seenNullByte)
readAheadPos = (readAheadPos + 1) % mEntrySize;
}
return tagBuff;
}
friend std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile);
JSObject *ToJSObject(JSContext *aCx)
@ -241,11 +267,25 @@ public:
int readPos = mReadPos;
while (readPos != mLastFlushPos) {
// Number of tag consumed
int incBy = 1;
ProfileEntry entry = mEntries[readPos];
// Read ahead to the next tag, if it's a 'd' tag process it now
const char* tagStringData = entry.mTagData;
int readAheadPos = (readPos + 1) % mEntrySize;
char tagBuff[DYNAMIC_MAX_STRING];
// Make sure the string is always null terminated if it fills up DYNAMIC_MAX_STRING-2
tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
tagStringData = processDynamicTag(readPos, &incBy, tagBuff);
}
switch (entry.mTagName) {
case 's':
sample = b.CreateObject();
b.DefineProperty(sample, "name", (const char*)entry.mTagData);
b.DefineProperty(sample, "name", tagStringData);
frames = b.CreateArray();
b.DefineProperty(sample, "frames", frames);
b.ArrayPush(samples, sample);
@ -255,18 +295,21 @@ public:
{
if (sample) {
JSObject *frame = b.CreateObject();
char tagBuff[1024];
// Bug 753041
// We need a double cast here to tell GCC that we don't want to sign
// extend 32-bit addresses starting with 0xFXXXXXX.
unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
snprintf(tagBuff, 1024, "%#llx", pc);
b.DefineProperty(frame, "location", tagBuff);
if (entry.mTagName == 'l') {
// Bug 753041
// We need a double cast here to tell GCC that we don't want to sign
// extend 32-bit addresses starting with 0xFXXXXXX.
unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc);
b.DefineProperty(frame, "location", tagBuff);
} else {
b.DefineProperty(frame, "location", tagStringData);
}
b.ArrayPush(frames, frame);
}
}
}
readPos = (readPos + 1) % mEntrySize;
readPos = (readPos + incBy) % mEntrySize;
}
return profile;
@ -560,11 +603,30 @@ void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSampl
// Sample
// 's' tag denotes the start of a sample block
// followed by 0 or more 'c' tags.
aProfile.addTag(ProfileEntry('s', "(root)"));
for (mozilla::sig_safe_t i = 0; i < aStack->mStackPointer; i++) {
if (i == 0) {
aProfile.addTag(ProfileEntry('s', aStack->mStack[i]));
// First entry has tagName 's' (start)
// Check for magic pointer bit 1 to indicate copy
const char* sampleLabel = aStack->mStack[i].mLabel;
if (aStack->mStack[i].isCopyLabel()) {
// Store the string using 1 or more 'd' (dynamic) tags
// that will happen to the preceding tag
aProfile.addTag(ProfileEntry('c', ""));
// Add one to store the null termination
size_t strLen = strlen(sampleLabel) + 1;
for (size_t j = 0; j < strLen;) {
// Store as many characters in the void* as the platform allows
char text[sizeof(void*)];
for (size_t pos = 0; pos < sizeof(void*) && j+pos < strLen; pos++) {
text[pos] = sampleLabel[j+pos];
}
j += sizeof(void*);
// Take '*((void**)(&text[0]))' to pass the char[] as a single void*
aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
}
} else {
aProfile.addTag(ProfileEntry('c', aStack->mStack[i]));
aProfile.addTag(ProfileEntry('c', sampleLabel));
}
}
#ifdef ENABLE_SPS_LEAF_DATA
@ -657,6 +719,8 @@ std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry)
unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
snprintf(tagBuff, 1024, "l-%#llx\n", pc);
stream << tagBuff;
} else if (entry.mTagName == 'd') {
// TODO implement 'd' tag for text profile
} else {
stream << entry.mTagName << "-" << entry.mTagData << "\n";
}

View File

@ -71,6 +71,11 @@
#define SAMPLER_GET_RESPONSIVENESS() NULL
#define SAMPLER_GET_FEATURES() NULL
#define SAMPLE_LABEL(name_space, info)
// Provide a default literal string to use if profiling is disabled
// and a printf argument to be computed if profiling is enabled.
// NOTE: This will store the formated string on the stack and consume
// over 128 bytes on the stack.
#define SAMPLE_LABEL_PRINTF(name_space, info, format, ...)
#define SAMPLE_LABEL_FN(name_space, info)
#define SAMPLE_MARKER(info)

View File

@ -5,6 +5,7 @@
#include <stdlib.h>
#include <signal.h>
#include <stdarg.h>
#include "mozilla/ThreadLocal.h"
#include "nscore.h"
#include "jsapi.h"
@ -50,6 +51,7 @@ extern bool stack_key_initialized;
#define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__)
#define SAMPLE_LABEL(name_space, info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info)
#define SAMPLE_LABEL_PRINTF(name_space, info, format, ...) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, format, __VA_ARGS__)
#define SAMPLE_MARKER(info) mozilla_sampler_add_marker(info)
/* we duplicate this code here to avoid header dependencies
@ -124,7 +126,7 @@ LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
// Returns a handdle to pass on exit. This can check that we are popping the
// correct callstack.
inline void* mozilla_sampler_call_enter(const char *aInfo);
inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress = NULL, bool aCopy = false);
inline void mozilla_sampler_call_exit(void* handle);
inline void mozilla_sampler_add_marker(const char *aInfo);
@ -154,8 +156,67 @@ private:
void* mHandle;
};
static const int SAMPLER_MAX_STRING = 128;
class NS_STACK_CLASS SamplerStackFramePrintfRAII {
public:
// we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
SamplerStackFramePrintfRAII(const char *aDefault, const char *aFormat, ...) {
if (mozilla_sampler_is_active()) {
va_list args;
va_start(args, aFormat);
char buff[SAMPLER_MAX_STRING];
// We have to use seperate printf's because we're using
// the vargs.
#if _MSC_VER
_vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args);
_snprintf(mDest, SAMPLER_MAX_STRING, "%s %s", aDefault, buff);
#else
vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args);
snprintf(mDest, SAMPLER_MAX_STRING, "%s %s", aDefault, buff);
#endif
mHandle = mozilla_sampler_call_enter(mDest, this, true);
va_end(args);
} else {
mHandle = mozilla_sampler_call_enter(aDefault);
}
}
~SamplerStackFramePrintfRAII() {
mozilla_sampler_call_exit(mHandle);
}
private:
char mDest[SAMPLER_MAX_STRING];
void* mHandle;
};
} //mozilla
class StackEntry
{
public:
// Encode the address and aCopy by dropping the last bit of aStackAddress
// and storing aCopy there.
static const void* EncodeStackAddress(const void* aStackAddress, bool aCopy) {
aStackAddress = reinterpret_cast<const void*>(
reinterpret_cast<uintptr_t>(aStackAddress) & ~0x1);
if (!aCopy)
aStackAddress = reinterpret_cast<const void*>(
reinterpret_cast<uintptr_t>(aStackAddress) | 0x1);
return aStackAddress;
}
bool isCopyLabel() const volatile {
return !((uintptr_t)mStackAddress & 0x1);
}
const char* mLabel;
// Tagged pointer. Less significant bit used to
// track if mLabel needs a copy. Note that we don't
// need the last bit of the stack address for proper ordering.
// Last bit 1 = Don't copy, Last bit 0 = Copy.
const void* mStackAddress;
};
// the SamplerStack members are read by signal
// handlers, so the mutation of them needs to be signal-safe.
struct ProfileStack
@ -191,7 +252,7 @@ public:
clearMarkers();
}
if (aMarkerId < 0 ||
static_cast<mozilla::sig_safe_t>(aMarkerId) >= mMarkerPointer) {
static_cast<mozilla::sig_safe_t>(aMarkerId) >= mMarkerPointer) {
return NULL;
}
return mMarkers[aMarkerId];
@ -205,6 +266,11 @@ public:
}
void push(const char *aName)
{
push(aName, NULL, false);
}
void push(const char *aName, void *aStackAddress, bool aCopy)
{
if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) {
mDroppedStackEntries++;
@ -213,7 +279,9 @@ public:
// Make sure we increment the pointer after the name has
// been written such that mStack is always consistent.
mStack[mStackPointer] = aName;
mStack[mStackPointer].mLabel = aName;
mStack[mStackPointer].mStackAddress = StackEntry::EncodeStackAddress(aStackAddress, aCopy);
// Prevent the optimizer from re-ordering these instructions
STORE_SEQUENCER();
mStackPointer++;
@ -232,7 +300,7 @@ public:
}
// Keep a list of active checkpoints
char const * volatile mStack[1024];
StackEntry volatile mStack[1024];
// Keep a list of active markers to be applied to the next sample taken
char const * volatile mMarkers[1024];
volatile mozilla::sig_safe_t mStackPointer;
@ -243,7 +311,7 @@ public:
volatile mozilla::sig_safe_t mQueueClearMarker;
};
inline void* mozilla_sampler_call_enter(const char *aInfo)
inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress, bool aCopy)
{
// check if we've been initialized to avoid calling pthread_getspecific
// with a null tlsStack which will return undefined results.
@ -258,7 +326,7 @@ inline void* mozilla_sampler_call_enter(const char *aInfo)
if (!stack) {
return stack;
}
stack->push(aInfo);
stack->push(aInfo, aFrameAddress, aCopy);
// The handle is meant to support future changes
// but for now it is simply use to save a call to