diff --git a/tools/profiler/TableTicker.cpp b/tools/profiler/TableTicker.cpp index fac2a90dee6..543fe24000a 100644 --- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -68,6 +68,7 @@ using namespace mozilla; #define snprintf _snprintf #endif +static const int DYNAMIC_MAX_STRING = 512; mozilla::ThreadLocal tlsStack; mozilla::ThreadLocal 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"; } diff --git a/tools/profiler/sampler.h b/tools/profiler/sampler.h index 47fd4570530..14301dd6226 100644 --- a/tools/profiler/sampler.h +++ b/tools/profiler/sampler.h @@ -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) diff --git a/tools/profiler/sps_sampler.h b/tools/profiler/sps_sampler.h index 219ae41942e..cc2e14bbd16 100644 --- a/tools/profiler/sps_sampler.h +++ b/tools/profiler/sps_sampler.h @@ -5,6 +5,7 @@ #include #include +#include #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( + reinterpret_cast(aStackAddress) & ~0x1); + if (!aCopy) + aStackAddress = reinterpret_cast( + reinterpret_cast(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(aMarkerId) >= mMarkerPointer) { + static_cast(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