Bug 856331 - Make breakpad unwinding runtime switchable. r=jseward

--HG--
extra : rebase_source : 1094beb17115a0d0e4d27eeb848c3932dfe0b2b0
This commit is contained in:
Benoit Girard 2013-04-01 15:45:03 -04:00
parent 37e2b2a059
commit 8e7e831c1d
7 changed files with 86 additions and 75 deletions

View File

@ -156,7 +156,7 @@ void genPseudoBacktraceEntries(/*MODIFIED*/UnwinderThreadBuffer* utb,
}
// RUNS IN SIGHANDLER CONTEXT
void BreakpadSampler::Tick(TickSample* sample)
void TableTicker::UnwinderTick(TickSample* sample)
{
if (!sample->threadProfile) {
// Platform doesn't support multithread, so use the main thread profile we created

View File

@ -392,6 +392,15 @@ void doSampleStackTrace(PseudoStack *aStack, ThreadProfile &aProfile, TickSample
}
void TableTicker::Tick(TickSample* sample)
{
if (HasUnwinderThread()) {
UnwinderTick(sample);
} else {
InplaceTick(sample);
}
}
void TableTicker::InplaceTick(TickSample* sample)
{
ThreadProfile& currThreadProfile = *sample->threadProfile;

View File

@ -32,6 +32,7 @@ class TableTicker: public Sampler {
, mPrimaryThreadProfile(nullptr)
, mStartTime(TimeStamp::Now())
, mSaveRequested(false)
, mUnwinderThread(false)
{
mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
@ -39,6 +40,7 @@ class TableTicker: public Sampler {
mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
mProfileThreads = true || hasFeature(aFeatures, aFeatureCount, "threads");
mUnwinderThread = hasFeature(aFeatures, aFeatureCount, "unwinder") || sps_version2();
mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
{
@ -119,12 +121,17 @@ class TableTicker: public Sampler {
virtual JSObject *ToJSObject(JSContext *aCx);
JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b);
bool HasUnwinderThread() const { return mUnwinderThread; }
bool ProfileJS() const { return mProfileJS; }
bool ProfileThreads() const { return mProfileThreads; }
virtual BreakpadSampler* AsBreakpadSampler() { return nullptr; }
protected:
// Called within a signal. This function must be reentrant
virtual void UnwinderTick(TickSample* sample);
// Called within a signal. This function must be reentrant
virtual void InplaceTick(TickSample* sample);
// Not implemented on platforms which do not support backtracing
void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
@ -139,18 +146,6 @@ protected:
bool mJankOnly;
bool mProfileJS;
bool mProfileThreads;
};
class BreakpadSampler: public TableTicker {
public:
BreakpadSampler(int aInterval, int aEntrySize,
const char** aFeatures, uint32_t aFeatureCount)
: TableTicker(aInterval, aEntrySize, aFeatures, aFeatureCount)
{}
// Called within a signal. This function must be reentrant
virtual void Tick(TickSample* sample);
virtual BreakpadSampler* AsBreakpadSampler() { return this; }
bool mUnwinderThread;
};

View File

@ -71,6 +71,10 @@ void uwt__init()
{
}
void uwt__stop()
{
}
void uwt__deinit()
{
}
@ -153,13 +157,17 @@ void uwt__init()
MOZ_ALWAYS_TRUE(r==0);
}
void uwt__deinit()
void uwt__stop()
{
// Shut down the unwinder thread.
MOZ_ASSERT(unwind_thr_exit_now == 0);
unwind_thr_exit_now = 1;
do_MBAR();
int r = pthread_join(unwind_thr, NULL); MOZ_ALWAYS_TRUE(r==0);
}
void uwt__deinit()
{
do_breakpad_unwind_Buffer_free_singletons();
}
@ -817,14 +825,9 @@ static void* unwind_thr_fn(void* exit_nowV)
MOZ_ASSERT(buffers);
int i;
for (i = 0; i < N_UNW_THR_BUFFERS; i++) {
/* These calloc-ations are never freed, even when the unwinder
thread is shut down. The reason is that sampler threads
might still be filling them up even after this thread is shut
down. The buffers themselves are not protected by the
spinlock, so we have no way to stop them being accessed
whilst we free them. It doesn't matter much since they will
not be reallocated if a new unwinder thread is restarted
later. */
/* These calloc-ations are shared between the sampler and the unwinder.
* They must be free after both threads have terminated.
*/
buffers[i] = (UnwinderThreadBuffer*)
calloc(sizeof(UnwinderThreadBuffer), 1);
MOZ_ASSERT(buffers[i]);
@ -872,7 +875,10 @@ static void* unwind_thr_fn(void* exit_nowV)
while (1) {
if (*exit_now != 0) break;
if (*exit_now != 0) {
*exit_now = 0;
break;
}
spinLock_acquire(&g_spinLock);
@ -1528,6 +1534,11 @@ void do_breakpad_unwind_Buffer_free_singletons()
delete sModules;
sModules = NULL;
}
g_stackLimitsUsed = 0;
g_seqNo = 0;
free(g_buffers);
g_buffers = NULL;
}
static

View File

@ -28,6 +28,13 @@ void utb__addEntry(/*MOD*/UnwinderThreadBuffer* utb,
void uwt__init();
// Request the unwinder thread to exit, and wait until it has done so.
// This must be called before stopping the profiler because we hold a
// reference to the profile which is owned by the profiler.
void uwt__stop();
// Release the unwinder resources. This must be called after profiling
// has stop. At this point we know the profiler doesn't hold any buffer
// and can safely release any resources.
void uwt__deinit();
// Registers a sampler thread for profiling. Threads must be registered

View File

@ -121,18 +121,6 @@ static void SetThreadName(const char* name) {
static void* ThreadEntry(void* arg) {
Thread* thread = reinterpret_cast<Thread*>(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->thread_ = pthread_self();
SetThreadName(thread->name());

View File

@ -268,27 +268,10 @@ void mozilla_sampler_init()
Sampler::RegisterCurrentThread("Gecko", stack, true);
if (sps_version2()) {
// Read mode settings from MOZ_PROFILER_MODE and interval
// settings from MOZ_PROFILER_INTERVAL and stack-scan threshhold
// from MOZ_PROFILER_STACK_SCAN.
read_profiler_env_vars();
// Create the unwinder thread. ATM there is only one.
uwt__init();
# if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
|| defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android) \
|| defined(SPS_PLAT_x86_windows) || defined(SPS_PLAT_amd64_windows) /* no idea if windows is correct */
// On Linuxes, register this thread (temporarily) for profiling
int aLocal;
uwt__register_thread_for_profiling( &aLocal );
# elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin)
// Registration is done in platform-macos.cc
# else
# error "Unknown plat"
# endif
}
// Read mode settings from MOZ_PROFILER_MODE and interval
// settings from MOZ_PROFILER_INTERVAL and stack-scan threshhold
// from MOZ_PROFILER_STACK_SCAN.
read_profiler_env_vars();
// Allow the profiler to be started using signals
OS::RegisterStartHandler();
@ -333,14 +316,6 @@ void mozilla_sampler_shutdown()
}
}
// Shut down and reap the unwinder thread. We have to do this
// before stopping the sampler, so as to guarantee that the unwinder
// thread doesn't try to access memory that the subsequent call to
// mozilla_sampler_stop causes to be freed.
if (sps_version2()) {
uwt__deinit();
}
profiler_stop();
Sampler::Shutdown();
@ -396,13 +371,24 @@ const char** mozilla_sampler_get_features()
{
static const char* features[] = {
#if defined(MOZ_PROFILING) && defined(HAVE_NATIVE_UNWIND)
// Walk the C++ stack.
"stackwalk",
#endif
#if defined(ENABLE_SPS_LEAF_DATA)
// Include the C++ leaf node if not stackwalking. DevTools
// profiler doesn't want the native addresses.
"leaf",
#endif
#if !defined(SPS_OS_windows)
// Use a seperate thread of walking the stack.
"unwinder",
#endif
// Only record samples during periods of bad responsiveness
"jank",
// Tell the JS engine to emmit pseudostack entries in the
// pro/epilogue.
"js",
// Profile the registered secondary threads.
"threads",
NULL
};
@ -432,15 +418,17 @@ void mozilla_sampler_start(int aProfileEntries, int aInterval,
profiler_stop();
TableTicker* t;
if (sps_version2()) {
t = new BreakpadSampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
aFeatures, aFeatureCount);
} else {
t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
aFeatures, aFeatureCount);
t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
aFeatures, aFeatureCount);
if (t->HasUnwinderThread()) {
int aLocal;
uwt__register_thread_for_profiling( &aLocal );
// Create the unwinder thread. ATM there is only one.
uwt__init();
}
tlsTicker.set(t);
t->Start();
if (t->ProfileJS()) {
@ -475,6 +463,15 @@ void mozilla_sampler_stop()
}
bool disableJS = t->ProfileJS();
bool unwinderThreader = t->HasUnwinderThread();
// Shut down and reap the unwinder thread. We have to do this
// before stopping the sampler, so as to guarantee that the unwinder
// thread doesn't try to access memory that the subsequent call to
// mozilla_sampler_stop causes to be freed.
if (unwinderThreader) {
uwt__stop();
}
t->Stop();
delete t;
@ -485,6 +482,10 @@ void mozilla_sampler_stop()
if (disableJS)
stack->disableJSSampling();
if (unwinderThreader) {
uwt__deinit();
}
sIsProfiling = false;
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();