Bug 779291: Implement SPS stackwalk using the breakpad unwinder. r=bgirard,glandium

This commit is contained in:
Julian Seward 2013-03-10 23:00:23 +01:00
parent fab812ffeb
commit 7c8de49023
19 changed files with 3815 additions and 107 deletions

View File

@ -1354,7 +1354,7 @@ XPCJSRuntime::~XPCJSRuntime()
}
#ifdef MOZ_ENABLE_PROFILER_SPS
// Tell the profiler that the runtime is gone
if (ProfileStack *stack = mozilla_profile_stack())
if (PseudoStack *stack = mozilla_get_pseudo_stack())
stack->sampleRuntime(nullptr);
#endif
@ -2561,7 +2561,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
#endif
#ifdef MOZ_ENABLE_PROFILER_SPS
if (ProfileStack *stack = mozilla_profile_stack())
if (PseudoStack *stack = mozilla_get_pseudo_stack())
stack->sampleRuntime(mJSRuntime);
#endif
JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);

View File

@ -626,7 +626,7 @@ bool LoadSymbols(const string& obj_file,
}
}
if (!found_debug_info_section) {
if (!found_debug_info_section && symbol_data != ONLY_CFI) {
fprintf(stderr, "%s: file contains no debugging information"
" (no \".stab\" or \".debug_info\" sections)\n",
obj_file.c_str());

View File

@ -25,6 +25,7 @@ EXPORTS += \
LOCAL_INCLUDES += \
-I$(topsrcdir)/mozglue/linker \
-I$(topsrcdir)/ipc/chromium/src \
-I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src \
$(NULL)
ifneq (,$(filter armeabi,$(ANDROID_CPU_ARCH)))
@ -45,6 +46,10 @@ CPPSRCS = \
nsProfilerFactory.cpp \
nsProfiler.cpp \
TableTicker.cpp \
TableTicker2.cpp \
UnwinderThread2.cpp \
ProfileEntry2.cpp \
local_debug_info_symbolizer.cc \
JSObjectBuilder.cpp \
JSCustomObjectBuilder.cpp \
$(NULL)
@ -69,6 +74,8 @@ CPPSRCS += \
shared-libraries-macos.cc \
platform-macos.cc \
$(NULL)
CMMSRCS += \
shim_mac_dump_syms.mm
endif
ifeq ($(OS_TARGET),WINNT)

View File

@ -0,0 +1,70 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef SPS_PLATFORM_MACROS_H
#define SPS_PLATFORM_MACROS_H
/* Define platform selection macros in a consistent way. Don't add
anything else to this file, so it can remain freestanding. The
primary factorisation is on (ARCH,OS) pairs ("PLATforms") but ARCH_
and OS_ macros are defined too, since they are sometimes
convenient. */
#undef SPS_PLAT_arm_android
#undef SPS_PLAT_amd64_linux
#undef SPS_PLAT_x86_linux
#undef SPS_PLAT_amd64_darwin
#undef SPS_PLAT_x86_darwin
#undef SPS_PLAT_x86_windows
#undef SPS_ARCH_arm
#undef SPS_ARCH_x86
#undef SPS_ARCH_amd64
#undef SPS_OS_android
#undef SPS_OS_linux
#undef SPS_OS_darwin
#undef SPS_OS_windows
#if defined(__linux__) && defined(__x86_64__)
# define SPS_PLAT_amd64_linux 1
# define SPS_ARCH_amd64 1
# define SPS_OS_linux 1
#elif defined(__linux__) && defined(__arm__) && defined(ANDROID)
# define SPS_PLAT_arm_android 1
# define SPS_ARCH_arm 1
# define SPS_OS_android 1
#elif defined(__linux__) && defined(__i386__) && !defined(ANDROID)
# define SPS_PLAT_x86_linux 1
# define SPS_ARCH_x86 1
# define SPS_OS_linux 1
#elif defined(__APPLE__) && defined(__x86_64__)
# define SPS_PLAT_amd64_darwin 1
# define SPS_ARCH_amd64 1
# define SPS_OS_darwin 1
#elif defined(__APPLE__) && defined(__i386__)
# define SPS_PLAT_x86_darwin 1
# define SPS_ARCH_x86 1
# define SPS_OS_darwin 1
#elif defined(_MSC_VER) && defined(_WIN32) && !defined(_WIN64)
# define SPS_PLAT_x86_windows 1
# define SPS_ARCH_x86 1
# define SPS_OS_windows 1
#elif defined(__linux__) && defined(__i386__) && defined(ANDROID)
# define SPS_PLAT_x86_android 1
# define SPS_ARCH_x86 1
# define SPS_OS_android 1
#else
# error "Unsupported platform"
#endif
#endif /* ndef SPS_PLATFORM_MACROS_H */

View File

@ -0,0 +1,338 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <iostream>
#include "sps_sampler.h"
#include "platform.h"
#include "nsThreadUtils.h"
// JSON
#include "JSObjectBuilder.h"
// Self
#include "ProfileEntry2.h"
#if _MSC_VER
#define snprintf _snprintf
#endif
////////////////////////////////////////////////////////////////////////
// BEGIN ProfileEntry2
ProfileEntry2::ProfileEntry2()
: mTagData(NULL)
, mTagName(0)
{ }
// aTagData must not need release (i.e. be a string from the text segment)
ProfileEntry2::ProfileEntry2(char aTagName, const char *aTagData)
: mTagData(aTagData)
, mTagName(aTagName)
{ }
ProfileEntry2::ProfileEntry2(char aTagName, void *aTagPtr)
: mTagPtr(aTagPtr)
, mTagName(aTagName)
{ }
ProfileEntry2::ProfileEntry2(char aTagName, double aTagFloat)
: mTagFloat(aTagFloat)
, mTagName(aTagName)
{ }
ProfileEntry2::ProfileEntry2(char aTagName, uintptr_t aTagOffset)
: mTagOffset(aTagOffset)
, mTagName(aTagName)
{ }
ProfileEntry2::ProfileEntry2(char aTagName, Address aTagAddress)
: mTagAddress(aTagAddress)
, mTagName(aTagName)
{ }
ProfileEntry2::ProfileEntry2(char aTagName, int aTagLine)
: mTagLine(aTagLine)
, mTagName(aTagName)
{ }
ProfileEntry2::ProfileEntry2(char aTagName, char aTagChar)
: mTagChar(aTagChar)
, mTagName(aTagName)
{ }
bool ProfileEntry2::is_ent_hint(char hintChar) {
return mTagName == 'h' && mTagChar == hintChar;
}
bool ProfileEntry2::is_ent_hint() {
return mTagName == 'h';
}
bool ProfileEntry2::is_ent(char tagChar) {
return mTagName == tagChar;
}
void* ProfileEntry2::get_tagPtr() {
// No consistency checking. Oh well.
return mTagPtr;
}
void ProfileEntry2::log()
{
// There is no compiler enforced mapping between tag chars
// and union variant fields, so the following was derived
// by looking through all the use points of TableTicker.cpp.
// mTagData (const char*) m,c,s
// mTagPtr (void*) d,l,L, S(start-of-stack)
// mTagLine (int) n,f
// mTagChar (char) h
// mTagFloat (double) r,t
switch (mTagName) {
case 'm': case 'c': case 's':
LOGF("%c \"%s\"", mTagName, mTagData); break;
case 'd': case 'l': case 'L': case 'S':
LOGF("%c %p", mTagName, mTagPtr); break;
case 'n': case 'f':
LOGF("%c %d", mTagName, mTagLine); break;
case 'h':
LOGF("%c \'%c\'", mTagName, mTagChar); break;
case 'r': case 't':
LOGF("%c %f", mTagName, mTagFloat); break;
default:
LOGF("'%c' unknown_tag", mTagName); break;
}
}
// END ProfileEntry2
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// BEGIN ThreadProfile2
#define PROFILE_MAX_ENTRY 100000
#define DYNAMIC_MAX_STRING 512
ThreadProfile2::ThreadProfile2(int aEntrySize, PseudoStack *aStack)
: mWritePos(0)
, mLastFlushPos(0)
, mReadPos(0)
, mEntrySize(aEntrySize)
, mPseudoStack(aStack)
, mMutex("ThreadProfile2::mMutex")
{
mEntries = new ProfileEntry2[mEntrySize];
}
ThreadProfile2::~ThreadProfile2()
{
delete[] mEntries;
}
void ThreadProfile2::addTag(ProfileEntry2 aTag)
{
// Called from signal, call only reentrant functions
mEntries[mWritePos] = aTag;
mWritePos = (mWritePos + 1) % mEntrySize;
if (mWritePos == mReadPos) {
// Keep one slot open
mEntries[mReadPos] = ProfileEntry2();
mReadPos = (mReadPos + 1) % mEntrySize;
}
// we also need to move the flush pos to ensure we
// do not pass it
if (mWritePos == mLastFlushPos) {
mLastFlushPos = (mLastFlushPos + 1) % mEntrySize;
}
}
// flush the new entries
void ThreadProfile2::flush()
{
mLastFlushPos = mWritePos;
}
// discards all of the entries since the last flush()
// NOTE: that if mWritePos happens to wrap around past
// mLastFlushPos we actually only discard mWritePos - mLastFlushPos entries
//
// r = mReadPos
// w = mWritePos
// f = mLastFlushPos
//
// r f w
// |-----------------------------|
// | abcdefghijklmnopq | -> 'abcdefghijklmnopq'
// |-----------------------------|
//
//
// mWritePos and mReadPos have passed mLastFlushPos
// f
// w r
// |-----------------------------|
// |ABCDEFGHIJKLMNOPQRSqrstuvwxyz|
// |-----------------------------|
// w
// r
// |-----------------------------|
// |ABCDEFGHIJKLMNOPQRSqrstuvwxyz| -> ''
// |-----------------------------|
//
//
// mWritePos will end up the same as mReadPos
// r
// w f
// |-----------------------------|
// |ABCDEFGHIJKLMklmnopqrstuvwxyz|
// |-----------------------------|
// r
// w
// |-----------------------------|
// |ABCDEFGHIJKLMklmnopqrstuvwxyz| -> ''
// |-----------------------------|
//
//
// mWritePos has moved past mReadPos
// w r f
// |-----------------------------|
// |ABCDEFdefghijklmnopqrstuvwxyz|
// |-----------------------------|
// r w
// |-----------------------------|
// |ABCDEFdefghijklmnopqrstuvwxyz| -> 'defghijkl'
// |-----------------------------|
void ThreadProfile2::erase()
{
mWritePos = mLastFlushPos;
}
char* ThreadProfile2::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)++;
ProfileEntry2 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;
}
JSCustomObject* ThreadProfile2::ToJSObject(JSContext *aCx)
{
JSObjectBuilder b(aCx);
JSCustomObject *profile = b.CreateObject();
JSCustomArray *samples = b.CreateArray();
b.DefineProperty(profile, "samples", samples);
JSCustomObject *sample = NULL;
JSCustomArray *frames = NULL;
int readPos = mReadPos;
while (readPos != mLastFlushPos) {
// Number of tag consumed
int incBy = 1;
ProfileEntry2 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", tagStringData);
frames = b.CreateArray();
b.DefineProperty(sample, "frames", frames);
b.ArrayPush(samples, sample);
break;
case 'r':
{
if (sample) {
b.DefineProperty(sample, "responsiveness", entry.mTagFloat);
}
}
break;
case 'f':
{
if (sample) {
b.DefineProperty(sample, "frameNumber", entry.mTagLine);
}
}
break;
case 't':
{
if (sample) {
b.DefineProperty(sample, "time", entry.mTagFloat);
}
}
break;
case 'c':
case 'l':
{
if (sample) {
JSCustomObject *frame = b.CreateObject();
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);
readAheadPos = (readPos + incBy) % mEntrySize;
if (readAheadPos != mLastFlushPos &&
mEntries[readAheadPos].mTagName == 'n') {
b.DefineProperty(frame, "line",
mEntries[readAheadPos].mTagLine);
incBy++;
}
}
b.ArrayPush(frames, frame);
}
}
}
readPos = (readPos + incBy) % mEntrySize;
}
return profile;
}
PseudoStack* ThreadProfile2::GetPseudoStack()
{
return mPseudoStack;
}
mozilla::Mutex* ThreadProfile2::GetMutex()
{
return &mMutex;
}
// END ThreadProfile2
////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZ_PROFILE_ENTRY_H
#define MOZ_PROFILE_ENTRY_H
#include "mozilla/Mutex.h"
class ThreadProfile2;
class ProfileEntry2
{
public:
ProfileEntry2();
// aTagData must not need release (i.e. be a string from the text segment)
ProfileEntry2(char aTagName, const char *aTagData);
ProfileEntry2(char aTagName, void *aTagPtr);
ProfileEntry2(char aTagName, double aTagFloat);
ProfileEntry2(char aTagName, uintptr_t aTagOffset);
ProfileEntry2(char aTagName, Address aTagAddress);
ProfileEntry2(char aTagName, int aTagLine);
ProfileEntry2(char aTagName, char aTagChar);
friend std::ostream& operator<<(std::ostream& stream, const ProfileEntry2& entry);
bool is_ent_hint(char hintChar);
bool is_ent_hint();
bool is_ent(char tagName);
void* get_tagPtr();
void log();
private:
friend class ThreadProfile2;
union {
const char* mTagData;
char mTagChars[sizeof(void*)];
void* mTagPtr;
double mTagFloat;
Address mTagAddress;
uintptr_t mTagOffset;
int mTagLine;
char mTagChar;
};
char mTagName;
};
class ThreadProfile2
{
public:
ThreadProfile2(int aEntrySize, PseudoStack *aStack);
~ThreadProfile2();
void addTag(ProfileEntry2 aTag);
void flush();
void erase();
char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff);
friend std::ostream& operator<<(std::ostream& stream,
const ThreadProfile2& profile);
JSCustomObject *ToJSObject(JSContext *aCx);
PseudoStack* GetPseudoStack();
mozilla::Mutex* GetMutex();
private:
// Circular buffer 'Keep One Slot Open' implementation
// for simplicity
ProfileEntry2* mEntries;
int mWritePos; // points to the next entry we will write to
int mLastFlushPos; // points to the next entry since the last flush()
int mReadPos; // points to the next entry we will read to
int mEntrySize;
PseudoStack* mPseudoStack;
mozilla::Mutex mMutex;
};
#endif /* ndef MOZ_PROFILE_ENTRY_H */

View File

@ -31,6 +31,7 @@
#include "nsDirectoryServiceDefs.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "PlatformMacros.h"
// JS
#include "jsdbgapi.h"
@ -82,13 +83,7 @@ using namespace mozilla;
static const int DYNAMIC_MAX_STRING = 512;
mozilla::ThreadLocal<ProfileStack *> tlsStack;
mozilla::ThreadLocal<TableTicker *> tlsTicker;
// We need to track whether we've been initialized otherwise
// we end up using tlsStack without initializing it.
// Because tlsStack is totally opaque to us we can't reuse
// it as the flag itself.
bool stack_key_initialized;
static mozilla::ThreadLocal<TableTicker *> tlsTicker;
TimeStamp sLastTracerEvent;
int sFrameNumber = 0;
@ -155,7 +150,7 @@ typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagSt
class ThreadProfile
{
public:
ThreadProfile(int aEntrySize, ProfileStack *aStack)
ThreadProfile(int aEntrySize, PseudoStack *aStack)
: mWritePos(0)
, mLastFlushPos(0)
, mReadPos(0)
@ -416,7 +411,7 @@ public:
}
}
ProfileStack* GetStack()
PseudoStack* GetStack()
{
return mStack;
}
@ -428,7 +423,7 @@ private:
int mLastFlushPos; // points to the next entry since the last flush()
int mReadPos; // points to the next entry we will read to
int mEntrySize;
ProfileStack *mStack;
PseudoStack *mStack;
};
class SaveProfileTask;
@ -444,7 +439,7 @@ hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature)
class TableTicker: public Sampler {
public:
TableTicker(int aInterval, int aEntrySize, ProfileStack *aStack,
TableTicker(int aInterval, int aEntrySize, PseudoStack *aStack,
const char** aFeatures, uint32_t aFeatureCount)
: Sampler(aInterval, true)
, mPrimaryThreadProfile(aEntrySize, aStack)
@ -586,7 +581,7 @@ public:
// being thread safe. Bug 750989.
if (stream.is_open()) {
JSAutoCompartment autoComp(cx, obj);
JSObject* profileObj = mozilla_sampler_get_profile_data(cx);
JSObject* profileObj = mozilla_sampler_get_profile_data1(cx);
jsval val = OBJECT_TO_JSVAL(profileObj);
JS_Stringify(cx, &val, nullptr, JSVAL_NULL, WriteCallback, &stream);
stream.close();
@ -729,7 +724,7 @@ void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
static
void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
ProfileStack *stack, void *lastpc)
PseudoStack *stack, void *lastpc)
{
int lineno = -1;
@ -843,7 +838,7 @@ void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
if (NS_SUCCEEDED(rv)) {
aProfile.addTag(ProfileEntry('s', "(root)"));
ProfileStack* stack = aProfile.GetStack();
PseudoStack* stack = aProfile.GetStack();
uint32_t pseudoStackPos = 0;
/* We have two stacks, the native C stack we extracted from unwinding,
@ -882,7 +877,7 @@ void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
#endif
static
void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSample *sample)
void doSampleStackTrace(PseudoStack *aStack, ThreadProfile &aProfile, TickSample *sample)
{
// Sample
// 's' tag denotes the start of a sample block
@ -916,7 +911,7 @@ unsigned int sCurrentEventGeneration = 0;
void TableTicker::Tick(TickSample* sample)
{
// Marker(s) come before the sample
ProfileStack* stack = mPrimaryThreadProfile.GetStack();
PseudoStack* stack = mPrimaryThreadProfile.GetStack();
for (int i = 0; stack->getMarker(i) != NULL; i++) {
addDynamicTag(mPrimaryThreadProfile, 'm', stack->getMarker(i));
}
@ -1002,19 +997,61 @@ std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry)
return stream;
}
void mozilla_sampler_init()
bool sps_version2()
{
static int version = 0; // Raced on, potentially
if (version == 0) {
bool allow2 = false; // Is v2 allowable on this platform?
# if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
|| defined(SPS_PLAT_x86_linux)
allow2 = true;
# elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) \
|| defined(SPS_PLAT_x86_windows) || defined(SPS_PLAT_x86_android)
allow2 = false;
# else
# error "Unknown platform"
# endif
bool req2 = PR_GetEnv("MOZ_PROFILER_NEW") != NULL; // Has v2 been requested?
bool elfhackd = false;
# if defined(USE_ELF_HACK)
bool elfhackd = true;
# endif
if (req2 && allow2) {
version = 2;
LOG("------------------- MOZ_PROFILER_NEW set -------------------");
} else if (req2 && !allow2) {
version = 1;
LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
LOG("---------- but is not available on this platform -----------");
} else if (req2 && elfhackd) {
version = 1;
LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
LOG("--- but this build was not done with --disable-elf-hack ----");
} else {
version = 1;
LOG("----------------- MOZ_PROFILER_NEW not set -----------------");
}
}
return version == 2;
}
void mozilla_sampler_init1()
{
if (stack_key_initialized)
return;
if (!tlsStack.init() || !tlsTicker.init()) {
if (!tlsPseudoStack.init() || !tlsTicker.init()) {
LOG("Failed to init.");
return;
}
stack_key_initialized = true;
ProfileStack *stack = new ProfileStack();
tlsStack.set(stack);
PseudoStack *stack = new PseudoStack();
tlsPseudoStack.set(stack);
// Allow the profiler to be started using signals
OS::RegisterStartHandler();
@ -1032,11 +1069,11 @@ void mozilla_sampler_init()
, "stackwalk"
#endif
};
mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
features, sizeof(features)/sizeof(const char*));
mozilla_sampler_start1(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
features, sizeof(features)/sizeof(const char*));
}
void mozilla_sampler_shutdown()
void mozilla_sampler_shutdown1()
{
TableTicker *t = tlsTicker.get();
if (t) {
@ -1051,13 +1088,13 @@ void mozilla_sampler_shutdown()
}
}
mozilla_sampler_stop();
mozilla_sampler_stop1();
// We can't delete the Stack because we can be between a
// sampler call_enter/call_exit point.
// TODO Need to find a safe time to delete Stack
}
void mozilla_sampler_save()
void mozilla_sampler_save1()
{
TableTicker *t = tlsTicker.get();
if (!t) {
@ -1070,7 +1107,7 @@ void mozilla_sampler_save()
t->HandleSaveRequest();
}
char* mozilla_sampler_get_profile()
char* mozilla_sampler_get_profile1()
{
TableTicker *t = tlsTicker.get();
if (!t) {
@ -1088,7 +1125,7 @@ char* mozilla_sampler_get_profile()
return rtn;
}
JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
JSObject *mozilla_sampler_get_profile_data1(JSContext *aCx)
{
TableTicker *t = tlsTicker.get();
if (!t) {
@ -1099,7 +1136,7 @@ JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
}
const char** mozilla_sampler_get_features()
const char** mozilla_sampler_get_features1()
{
static const char* features[] = {
#if defined(MOZ_PROFILING) && (defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK))
@ -1117,19 +1154,19 @@ const char** mozilla_sampler_get_features()
}
// Values are only honored on the first start
void mozilla_sampler_start(int aProfileEntries, int aInterval,
const char** aFeatures, uint32_t aFeatureCount)
void mozilla_sampler_start1(int aProfileEntries, int aInterval,
const char** aFeatures, uint32_t aFeatureCount)
{
if (!stack_key_initialized)
mozilla_sampler_init();
mozilla_sampler_init1();
ProfileStack *stack = tlsStack.get();
PseudoStack *stack = tlsPseudoStack.get();
if (!stack) {
ASSERT(false);
return;
}
mozilla_sampler_stop();
mozilla_sampler_stop1();
TableTicker *t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
@ -1144,10 +1181,10 @@ void mozilla_sampler_start(int aProfileEntries, int aInterval,
os->NotifyObservers(nullptr, "profiler-started", nullptr);
}
void mozilla_sampler_stop()
void mozilla_sampler_stop1()
{
if (!stack_key_initialized)
mozilla_sampler_init();
mozilla_sampler_init1();
TableTicker *t = tlsTicker.get();
if (!t) {
@ -1159,7 +1196,7 @@ void mozilla_sampler_stop()
t->Stop();
delete t;
tlsTicker.set(NULL);
ProfileStack *stack = tlsStack.get();
PseudoStack *stack = tlsPseudoStack.get();
ASSERT(stack != NULL);
if (disableJS)
@ -1170,10 +1207,10 @@ void mozilla_sampler_stop()
os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
}
bool mozilla_sampler_is_active()
bool mozilla_sampler_is_active1()
{
if (!stack_key_initialized)
mozilla_sampler_init();
mozilla_sampler_init1();
TableTicker *t = tlsTicker.get();
if (!t) {
@ -1183,10 +1220,9 @@ bool mozilla_sampler_is_active()
return t->IsActive();
}
double sResponsivenessTimes[100];
double sCurrResponsiveness = 0.f;
unsigned int sResponsivenessLoc = 0;
void mozilla_sampler_responsiveness(TimeStamp aTime)
static double sResponsivenessTimes[100];
static unsigned int sResponsivenessLoc = 0;
void mozilla_sampler_responsiveness1(TimeStamp aTime)
{
if (!sLastTracerEvent.IsNull()) {
if (sResponsivenessLoc == 100) {
@ -1203,17 +1239,17 @@ void mozilla_sampler_responsiveness(TimeStamp aTime)
sLastTracerEvent = aTime;
}
const double* mozilla_sampler_get_responsiveness()
const double* mozilla_sampler_get_responsiveness1()
{
return sResponsivenessTimes;
}
void mozilla_sampler_frame_number(int frameNumber)
void mozilla_sampler_frame_number1(int frameNumber)
{
sFrameNumber = frameNumber;
}
void print_callback(const ProfileEntry& entry, const char* tagStringData) {
static void print_callback(const ProfileEntry& entry, const char* tagStringData) {
switch (entry.mTagName) {
case 's':
case 'c':
@ -1221,12 +1257,12 @@ void print_callback(const ProfileEntry& entry, const char* tagStringData) {
}
}
void mozilla_sampler_print_location()
void mozilla_sampler_print_location1()
{
if (!stack_key_initialized)
mozilla_sampler_init();
mozilla_sampler_init1();
ProfileStack *stack = tlsStack.get();
PseudoStack *stack = tlsPseudoStack.get();
if (!stack) {
MOZ_ASSERT(false);
return;
@ -1241,15 +1277,15 @@ void mozilla_sampler_print_location()
threadProfile.IterateTags(print_callback);
}
void mozilla_sampler_lock()
void mozilla_sampler_lock1()
{
mozilla_sampler_stop();
mozilla_sampler_stop1();
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os)
os->NotifyObservers(nullptr, "profiler-locked", nullptr);
}
void mozilla_sampler_unlock()
void mozilla_sampler_unlock1()
{
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,60 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZ_UNWINDER_THREAD_2_H
#define MOZ_UNWINDER_THREAD_2_H
#include "sps_sampler.h"
#include "ProfileEntry2.h"
/* Top level exports of UnwinderThread.cpp. */
// Abstract type. A buffer which is used to transfer information between
// the sampled thread(s) and the unwinder thread(s).
typedef
struct _UnwinderThreadBuffer
UnwinderThreadBuffer;
// RUNS IN SIGHANDLER CONTEXT
// Called in the sampled thread (signal) context. Adds a ProfileEntry2
// into an UnwinderThreadBuffer that the thread has previously obtained
// by a call to utb__acquire_empty_buffer.
void utb__addEntry(/*MOD*/UnwinderThreadBuffer* utb,
ProfileEntry2 ent);
// Create the unwinder thread. At the moment there can be only one.
void uwt__init();
// Request the unwinder thread to exit, and wait until it has done so.
void uwt__deinit();
// Registers a sampler thread for profiling. Threads must be registered
// before they are allowed to call utb__acquire_empty_buffer or
// utb__release_full_buffer.
void uwt__register_thread_for_profiling(void* stackTop);
// RUNS IN SIGHANDLER CONTEXT
// Called in the sampled thread (signal) context. Get an empty buffer
// into which ProfileEntries can be put. It may return NULL if no
// empty buffers can be found, which will be the case if the unwinder
// thread(s) have fallen behind for some reason. In this case the
// sampled thread must simply give up and return from the signal handler
// immediately, else it risks deadlock.
UnwinderThreadBuffer* uwt__acquire_empty_buffer();
// RUNS IN SIGHANDLER CONTEXT
// Called in the sampled thread (signal) context. Release a buffer
// that the sampled thread has acquired, handing the contents to
// the unwinder thread, and, if necessary, passing sufficient
// information (stack top chunk, + registers) to also do a native
// unwind. If 'ucV' is NULL, no native unwind is done. If non-NULL,
// it is assumed to point to a ucontext_t* that holds the initial
// register state for the unwind. The results of all of this are
// dumped into |aProfile| (by the unwinder thread, not the calling thread).
void uwt__release_full_buffer(ThreadProfile2* aProfile,
UnwinderThreadBuffer* utb,
void* /* ucontext_t*, really */ ucV);
#endif /* ndef MOZ_UNWINDER_THREAD_2_H */

View File

@ -0,0 +1,160 @@
#include "PlatformMacros.h"
#if !defined(SPS_PLAT_x86_windows)
# include "common/module.h"
# include "processor/cfi_frame_info.h"
#endif
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/code_modules.h"
#include "google_breakpad/processor/stack_frame.h"
#include "processor/logging.h"
#include "common/scoped_ptr.h"
#if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
|| defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
# include "common/linux/dump_symbols.h"
#elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin)
# include "shim_mac_dump_syms.h"
#elif defined(SPS_PLAT_x86_windows)
/* This is all stubbed out anyway, so don't do anything. */
#else
# error "Unknown platform"
#endif
#include "platform.h"
#include "local_debug_info_symbolizer.h"
namespace google_breakpad {
LocalDebugInfoSymbolizer::~LocalDebugInfoSymbolizer() {
# if !defined(SPS_PLAT_x86_windows)
for (SymbolMap::iterator it = symbols_.begin();
it != symbols_.end();
++it) {
delete it->second;
}
# endif
}
StackFrameSymbolizer::SymbolizerResult
LocalDebugInfoSymbolizer::FillSourceLineInfo(const CodeModules* modules,
const SystemInfo* system_info,
StackFrame* frame) {
if (!modules) {
return kError;
}
const CodeModule* module = modules->GetModuleForAddress(frame->instruction);
if (!module) {
return kError;
}
frame->module = module;
# if !defined(SPS_PLAT_x86_windows)
Module* debug_info_module = NULL;
SymbolMap::const_iterator it = symbols_.find(module->code_file());
if (it == symbols_.end()) {
if (no_symbol_modules_.find(module->code_file()) !=
no_symbol_modules_.end()) {
return kNoError;
}
LOG("BPUnw:");
LOGF("BPUnw: ReadSymbolData: BEGIN %s", module->code_file().c_str());
if (!ReadSymbolData(module->code_file(),
debug_dirs_,
ONLY_CFI,
&debug_info_module)) {
BPLOG(ERROR) << "ReadSymbolData failed for " << module->code_file();
LOGF("BPUnw: ReadSymbolData: FAIL %s", module->code_file().c_str());
if (debug_info_module)
delete debug_info_module;
no_symbol_modules_.insert(module->code_file());
return kNoError;
}
LOGF("BPUnw: ReadSymbolData: SUCCESS %s", module->code_file().c_str());
symbols_[module->code_file()] = debug_info_module;
} else {
debug_info_module = it->second;
}
u_int64_t address = frame->instruction - frame->module->base_address();
Module::Function* function =
debug_info_module->FindFunctionByAddress(address);
if (function) {
frame->function_name = function->name;
//TODO: line info: function->lines
} else {
Module::Extern* ex = debug_info_module->FindExternByAddress(address);
if (ex) {
frame->function_name = ex->name;
}
}
# endif /* !defined(SPS_PLAT_x86_windows) */
return kNoError;
}
WindowsFrameInfo* LocalDebugInfoSymbolizer::FindWindowsFrameInfo(
const StackFrame* frame) {
// Not currently implemented, would require PDBSourceLineWriter to
// implement an API to return symbol data.
return NULL;
}
#if !defined(SPS_PLAT_x86_windows)
// Taken wholesale from source_line_resolver_base.cc
bool ParseCFIRuleSet(const string& rule_set, CFIFrameInfo* frame_info) {
CFIFrameInfoParseHandler handler(frame_info);
CFIRuleParser parser(&handler);
return parser.Parse(rule_set);
}
static void ConvertCFI(const UniqueString* name, const Module::Expr& rule,
CFIFrameInfo* frame_info) {
if (name == ustr__ZDcfa()) frame_info->SetCFARule(rule);
else if (name == ustr__ZDra()) frame_info->SetRARule(rule);
else frame_info->SetRegisterRule(name, rule);
}
static void ConvertCFI(const Module::RuleMap& rule_map,
CFIFrameInfo* frame_info) {
for (Module::RuleMap::const_iterator it = rule_map.begin();
it != rule_map.end(); ++it) {
ConvertCFI(it->first, it->second, frame_info);
}
}
#endif
CFIFrameInfo* LocalDebugInfoSymbolizer::FindCFIFrameInfo(
const StackFrame* frame) {
#if defined(SPS_PLAT_x86_windows)
return NULL;
#else
if (!frame || !frame->module) return NULL;
SymbolMap::const_iterator it = symbols_.find(frame->module->code_file());
if (it == symbols_.end()) return NULL;
Module* module = it->second;
u_int64_t address = frame->instruction - frame->module->base_address();
Module::StackFrameEntry* entry =
module->FindStackFrameEntryByAddress(address);
if (!entry)
return NULL;
//TODO: can we cache this data per-address? does that make sense?
scoped_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
ConvertCFI(entry->initial_rules, rules.get());
for (Module::RuleChangeMap::const_iterator delta_it =
entry->rule_changes.begin();
delta_it != entry->rule_changes.end() && delta_it->first < address;
++delta_it) {
ConvertCFI(delta_it->second, rules.get());
}
return rules.release();
#endif /* defined(SPS_PLAT_x86_windows) */
}
} // namespace google_breakpad

View File

@ -0,0 +1,40 @@
#ifndef PROCESSOR_LOCAL_DEBUG_INFO_SYMBOLIZER_H_
#define PROCESSOR_LOCAL_DEBUG_INFO_SYMBOLIZER_H_
#include "google_breakpad/processor/stack_frame_symbolizer.h"
#include <map>
#include <vector>
namespace google_breakpad {
class Module;
class LocalDebugInfoSymbolizer : public StackFrameSymbolizer {
public:
using StackFrameSymbolizer::SymbolizerResult;
LocalDebugInfoSymbolizer(const std::vector<string>& debug_dirs) :
StackFrameSymbolizer(NULL, NULL),
debug_dirs_(debug_dirs) {}
virtual ~LocalDebugInfoSymbolizer();
virtual SymbolizerResult FillSourceLineInfo(const CodeModules* modules,
const SystemInfo* system_info,
StackFrame* stack_frame);
virtual WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame);
virtual CFIFrameInfo* FindCFIFrameInfo(const StackFrame* frame);
// Lie to the stackwalker to short-circuit stack-scanning heuristics.
virtual bool HasImplementation() { return false; }
private:
typedef std::map<string, Module*> SymbolMap;
SymbolMap symbols_;
std::vector<string> debug_dirs_;
};
} // namespace google_breakpad
#endif // PROCESSOR_LOCAL_DEBUG_INFO_SYMBOLIZER_H_

View File

@ -60,11 +60,11 @@ nsProfiler::Observe(nsISupports *aSubject,
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(parentWebNav);
if (loadContext && loadContext->UsePrivateBrowsing() && !mLockedForPrivateBrowsing) {
mLockedForPrivateBrowsing = true;
mozilla_sampler_lock();
SAMPLER_LOCK();
}
} else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
mLockedForPrivateBrowsing = false;
mozilla_sampler_unlock();
SAMPLER_UNLOCK();
}
return NS_OK;
}

View File

@ -224,7 +224,7 @@ Sampler::~Sampler() {
void Sampler::Start() {
LOG("Sampler Started");
LOG("Sampler started");
if (sActiveSampler != NULL) return;
// Request profiling signals.
@ -291,8 +291,8 @@ static struct sigaction old_sigstart_signal_handler;
const int SIGSTART = SIGUSR1;
static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
PROFILE_DEFAULT_FEATURES, PROFILE_DEFAULT_FEATURE_COUNT);
SAMPLER_START(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
PROFILE_DEFAULT_FEATURES, PROFILE_DEFAULT_FEATURE_COUNT);
}
void OS::RegisterStartHandler()

View File

@ -28,8 +28,8 @@
#include <string.h>
#include <errno.h>
#include "platform.h"
#include "UnwinderThread2.h" /* uwt__register_thread_for_profiling */
// this port is based off of v8 svn revision 9837
@ -129,6 +129,16 @@ static void* ThreadEntry(void* arg) {
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
// BEGIN temp hack for SPS v1-vs-v2
extern bool sps_version2();
if (sps_version2()) {
// Register this thread for profiling.
int aLocal;
uwt__register_thread_for_profiling( &aLocal );
}
// END temp hack for SPS v1-vs-v2
thread->data()->thread_ = pthread_self();
SetThreadName(thread->name());
ASSERT(thread->data()->thread_ != kNoThread);

View File

@ -26,6 +26,9 @@
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
#ifndef TOOLS_PLATFORM_H_
#define TOOLS_PLATFORM_H_
#ifdef ANDROID
#include <android/log.h>
#else
@ -44,11 +47,11 @@
#define ENABLE_SPS_LEAF_DATA
#define ENABLE_ARM_LR_SAVING
#endif
#define LOG(text) __android_log_write(ANDROID_LOG_ERROR, "profiler", text)
#define LOGF(format, ...) __android_log_print(ANDROID_LOG_ERROR, "profiler", format, __VA_ARGS__)
#define LOG(text) __android_log_write(ANDROID_LOG_ERROR, "Profiler", text)
#define LOGF(format, ...) __android_log_print(ANDROID_LOG_ERROR, "Profiler", format, __VA_ARGS__)
#else
#define LOG(text) printf("Profiler: %s\n", text)
#define LOGF(format, ...) printf("Profiler: " format "\n", __VA_ARGS__)
#define LOG(text) fprintf(stderr, "Profiler: %s\n", text)
#define LOGF(format, ...) fprintf(stderr, "Profiler: " format "\n", __VA_ARGS__)
#endif
#if defined(XP_MACOSX) || defined(XP_WIN)
@ -276,3 +279,4 @@ class Sampler {
PlatformData* data_; // Platform specific data.
};
#endif /* ndef TOOLS_PLATFORM_H_ */

View File

@ -0,0 +1,8 @@
// Read debug info from |obj_file| and park it in a Module, returned
// via |module|. Caller owns the Module and is responsible for
// deallocating it. Note that |debug_dirs| is ignored.
bool ReadSymbolData(const string& obj_file,
const std::vector<string> &debug_dirs,
SymbolData symbol_data,
google_breakpad::Module** module);

View File

@ -0,0 +1,20 @@
// -*- mode: c++ -*-
#include "common/mac/dump_syms.h"
#include "shim_mac_dump_syms.h"
bool ReadSymbolData(const string& obj_file,
const std::vector<string> &debug_dirs,
SymbolData symbol_data,
google_breakpad::Module** module)
{
google_breakpad::DumpSymbols ds(symbol_data);
NSString* obj_file_ns = [NSString stringWithUTF8String:obj_file.c_str()];
// TODO: remember to [obj_file_ns release] this at the exit points
if (!ds.Read(obj_file_ns))
return false;
return ds.ReadSymbolData(module);
}

View File

@ -3,6 +3,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef TOOLS_SPS_SAMPLER_H_
#define TOOLS_SPS_SAMPLER_H_
#include <stdlib.h>
#include <signal.h>
#include <stdarg.h>
@ -26,12 +29,11 @@
using mozilla::TimeStamp;
using mozilla::TimeDuration;
struct ProfileStack;
struct PseudoStack;
class TableTicker;
class JSCustomObject;
extern mozilla::ThreadLocal<ProfileStack *> tlsStack;
extern mozilla::ThreadLocal<TableTicker *> tlsTicker;
extern mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
extern bool stack_key_initialized;
#ifndef SAMPLE_FUNCTION_NAME
@ -44,18 +46,73 @@ extern bool stack_key_initialized;
# endif
#endif
#define SAMPLER_INIT() mozilla_sampler_init()
#define SAMPLER_SHUTDOWN() mozilla_sampler_shutdown()
#define SAMPLER_START(entries, interval, features, featureCount) mozilla_sampler_start(entries, interval, features, featureCount)
#define SAMPLER_STOP() mozilla_sampler_stop()
#define SAMPLER_IS_ACTIVE() mozilla_sampler_is_active()
#define SAMPLER_RESPONSIVENESS(time) mozilla_sampler_responsiveness(time)
#define SAMPLER_GET_RESPONSIVENESS() mozilla_sampler_get_responsiveness()
#define SAMPLER_FRAME_NUMBER(frameNumber) mozilla_sampler_frame_number(frameNumber)
#define SAMPLER_SAVE() mozilla_sampler_save()
#define SAMPLER_GET_PROFILE() mozilla_sampler_get_profile()
#define SAMPLER_GET_PROFILE_DATA(ctx) mozilla_sampler_get_profile_data(ctx)
#define SAMPLER_GET_FEATURES() mozilla_sampler_get_features()
/* Returns true if env var SPS_NEW is set to anything, else false. */
extern bool sps_version2();
#define SAMPLER_INIT() \
do { \
if (!sps_version2()) mozilla_sampler_init1(); \
else mozilla_sampler_init2(); \
} while (0)
#define SAMPLER_SHUTDOWN() \
do { \
if (!sps_version2()) mozilla_sampler_shutdown1(); \
else mozilla_sampler_shutdown2(); \
} while (0)
#define SAMPLER_START(entries, interval, features, featureCount) \
do { \
if (!sps_version2()) \
mozilla_sampler_start1(entries, interval, features, featureCount); \
else \
mozilla_sampler_start2(entries, interval, features, featureCount); \
} while (0)
#define SAMPLER_STOP() \
do { \
if (!sps_version2()) mozilla_sampler_stop1(); \
else mozilla_sampler_stop2(); \
} while (0)
#define SAMPLER_IS_ACTIVE() \
(!sps_version2() ? mozilla_sampler_is_active1() \
: mozilla_sampler_is_active2() )
#define SAMPLER_RESPONSIVENESS(time) \
do { \
if (!sps_version2()) mozilla_sampler_responsiveness1(time); \
else mozilla_sampler_responsiveness2(time); \
} while (0)
#define SAMPLER_GET_RESPONSIVENESS() \
(!sps_version2() ? mozilla_sampler_get_responsiveness1() \
: mozilla_sampler_get_responsiveness2() )
#define SAMPLER_FRAME_NUMBER(frameNumber) \
do { \
if (!sps_version2()) mozilla_sampler_frame_number1(frameNumber); \
else mozilla_sampler_frame_number2(frameNumber); \
} while (0)
#define SAMPLER_SAVE() \
do { \
if (!sps_version2()) mozilla_sampler_save1(); \
else mozilla_sampler_save2(); \
} while (0)
#define SAMPLER_GET_PROFILE() \
(!sps_version2() ? mozilla_sampler_get_profile1() \
: mozilla_sampler_get_profile2() )
#define SAMPLER_GET_PROFILE_DATA(ctx) \
(!sps_version2() ? mozilla_sampler_get_profile_data1(ctx) \
: mozilla_sampler_get_profile_data2(ctx) )
#define SAMPLER_GET_FEATURES() \
(!sps_version2() ? mozilla_sampler_get_features1() \
: mozilla_sampler_get_features2() )
// we want the class and function name but can't easily get that using preprocessor macros
// __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
@ -70,7 +127,23 @@ extern bool stack_key_initialized;
#define SAMPLE_MAIN_THREAD_LABEL_PRINTF(name_space, info, ...) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__)
#define SAMPLE_MAIN_THREAD_MARKER(info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info)
#define SAMPLER_PRINT_LOCATION() mozilla_sampler_print_location()
#define SAMPLER_PRINT_LOCATION() \
do { \
if (!sps_version2()) mozilla_sampler_print_location1(); \
else mozilla_sampler_print_location2(); \
} while (0)
#define SAMPLER_LOCK() \
do { \
if (!sps_version2()) mozilla_sampler_lock1(); \
else mozilla_sampler_lock2(); \
} while (0)
#define SAMPLER_UNLOCK() \
do { \
if (!sps_version2()) mozilla_sampler_unlock1(); \
else mozilla_sampler_unlock2(); \
} while (0)
/* we duplicate this code here to avoid header dependencies
* which make it more difficult to include in other places */
@ -165,31 +238,63 @@ LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
# error "Memory clobber not supported for your platform."
#endif
// Returns a handdle to pass on exit. This can check that we are popping the
// Returns a handle to pass on exit. This can check that we are popping the
// correct callstack.
inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress = NULL, bool aCopy = false, uint32_t line = 0);
inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress = NULL,
bool aCopy = false, uint32_t line = 0);
inline void mozilla_sampler_call_exit(void* handle);
inline void mozilla_sampler_add_marker(const char *aInfo);
void mozilla_sampler_start(int aEntries, int aInterval, const char** aFeatures, uint32_t aFeatureCount);
void mozilla_sampler_stop();
bool mozilla_sampler_is_active();
void mozilla_sampler_responsiveness(TimeStamp time);
void mozilla_sampler_frame_number(int frameNumber);
const double* mozilla_sampler_get_responsiveness();
void mozilla_sampler_save();
char* mozilla_sampler_get_profile();
JSObject *mozilla_sampler_get_profile_data(JSContext *aCx);
const char** mozilla_sampler_get_features();
void mozilla_sampler_init();
void mozilla_sampler_shutdown();
void mozilla_sampler_print_location();
void mozilla_sampler_start1(int aEntries, int aInterval, const char** aFeatures,
uint32_t aFeatureCount);
void mozilla_sampler_start2(int aEntries, int aInterval, const char** aFeatures,
uint32_t aFeatureCount);
void mozilla_sampler_stop1();
void mozilla_sampler_stop2();
bool mozilla_sampler_is_active1();
bool mozilla_sampler_is_active2();
void mozilla_sampler_responsiveness1(TimeStamp time);
void mozilla_sampler_responsiveness2(TimeStamp time);
void mozilla_sampler_frame_number1(int frameNumber);
void mozilla_sampler_frame_number2(int frameNumber);
const double* mozilla_sampler_get_responsiveness1();
const double* mozilla_sampler_get_responsiveness2();
void mozilla_sampler_save1();
void mozilla_sampler_save2();
char* mozilla_sampler_get_profile1();
char* mozilla_sampler_get_profile2();
JSObject *mozilla_sampler_get_profile_data1(JSContext *aCx);
JSObject *mozilla_sampler_get_profile_data2(JSContext *aCx);
const char** mozilla_sampler_get_features1();
const char** mozilla_sampler_get_features2();
void mozilla_sampler_init1();
void mozilla_sampler_init2();
void mozilla_sampler_shutdown1();
void mozilla_sampler_shutdown2();
void mozilla_sampler_print_location1();
void mozilla_sampler_print_location2();
// Lock the profiler. When locked the profiler is (1) stopped,
// (2) profile data is cleared, (3) profiler-locked is fired.
// This is used to lock down the profiler during private browsing
void mozilla_sampler_lock();
void mozilla_sampler_lock1();
void mozilla_sampler_lock2();
// Unlock the profiler, leaving it stopped and fires profiler-unlocked.
void mozilla_sampler_unlock();
void mozilla_sampler_unlock1();
void mozilla_sampler_unlock2();
namespace mozilla {
@ -211,7 +316,7 @@ 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, uint32_t line, const char *aFormat, ...) {
if (mozilla_sampler_is_active()) {
if (SAMPLER_IS_ACTIVE()) {
va_list args;
va_start(args, aFormat);
char buff[SAMPLER_MAX_STRING];
@ -273,13 +378,14 @@ public:
}
};
// the SamplerStack members are read by signal
// the PseudoStack members are read by signal
// handlers, so the mutation of them needs to be signal-safe.
struct ProfileStack
struct PseudoStack
{
public:
ProfileStack()
PseudoStack()
: mStackPointer(0)
, mSignalLock(false)
, mMarkerPointer(0)
, mQueueClearMarker(false)
, mRuntime(NULL)
@ -420,11 +526,11 @@ public:
bool mStartJSSampling;
};
inline ProfileStack* mozilla_profile_stack(void)
inline PseudoStack* mozilla_get_pseudo_stack(void)
{
if (!stack_key_initialized)
return NULL;
return tlsStack.get();
return tlsPseudoStack.get();
}
inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress,
@ -435,7 +541,7 @@ inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress,
if (!stack_key_initialized)
return NULL;
ProfileStack *stack = tlsStack.get();
PseudoStack *stack = tlsPseudoStack.get();
// we can't infer whether 'stack' has been initialized
// based on the value of stack_key_intiailized because
// 'stack' is only intialized when a thread is being
@ -458,7 +564,7 @@ inline void mozilla_sampler_call_exit(void *aHandle)
if (!aHandle)
return;
ProfileStack *stack = (ProfileStack*)aHandle;
PseudoStack *stack = (PseudoStack*)aHandle;
stack->pop();
}
@ -469,14 +575,15 @@ inline void mozilla_sampler_add_marker(const char *aMarker)
// Don't insert a marker if we're not profiling to avoid
// the heap copy (malloc).
if (!mozilla_sampler_is_active()) {
if (!SAMPLER_IS_ACTIVE()) {
return;
}
ProfileStack *stack = tlsStack.get();
PseudoStack *stack = tlsPseudoStack.get();
if (!stack) {
return;
}
stack->addMarker(aMarker);
}
#endif /* ndef TOOLS_SPS_SAMPLER_H_ */