зеркало из https://github.com/mozilla/gecko-dev.git
Bug 734691 - Port multi-thread support to win/mac. r=snorp,smaug
--HG-- extra : rebase_source : ce0d9f94d2b4deb249e09198f9315f69ad6ee7d4
This commit is contained in:
Родитель
034fae0ccc
Коммит
c01c3468e2
|
@ -266,6 +266,10 @@ JSBool
|
||||||
OperationCallback(JSContext* aCx)
|
OperationCallback(JSContext* aCx)
|
||||||
{
|
{
|
||||||
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
||||||
|
|
||||||
|
// Now is a good time to turn on profiling if it's pending.
|
||||||
|
profiler_js_operation_callback();
|
||||||
|
|
||||||
return worker->OperationCallback(aCx);
|
return worker->OperationCallback(aCx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,15 +522,19 @@ public:
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSRuntime* rt = JS_GetRuntime(cx);
|
||||||
|
|
||||||
profiler_register_thread("WebWorker");
|
profiler_register_thread("WebWorker");
|
||||||
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||||
|
if (PseudoStack* stack = mozilla_get_pseudo_stack())
|
||||||
|
stack->sampleRuntime(rt);
|
||||||
|
#endif
|
||||||
|
|
||||||
{
|
{
|
||||||
JSAutoRequest ar(cx);
|
JSAutoRequest ar(cx);
|
||||||
workerPrivate->DoRunLoop(cx);
|
workerPrivate->DoRunLoop(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSRuntime* rt = JS_GetRuntime(cx);
|
|
||||||
|
|
||||||
// XXX Bug 666963 - CTypes can create another JSContext for use with
|
// XXX Bug 666963 - CTypes can create another JSContext for use with
|
||||||
// closures, and then it holds that context in a reserved slot on the CType
|
// closures, and then it holds that context in a reserved slot on the CType
|
||||||
// prototype object. We have to destroy that context before we can destroy
|
// prototype object. We have to destroy that context before we can destroy
|
||||||
|
@ -545,6 +553,10 @@ public:
|
||||||
JS_DestroyContext(cx);
|
JS_DestroyContext(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||||
|
if (PseudoStack* stack = mozilla_get_pseudo_stack())
|
||||||
|
stack->sampleRuntime(nullptr);
|
||||||
|
#endif
|
||||||
JS_DestroyRuntime(rt);
|
JS_DestroyRuntime(rt);
|
||||||
|
|
||||||
workerPrivate->ScheduleDeletion(false);
|
workerPrivate->ScheduleDeletion(false);
|
||||||
|
|
|
@ -138,6 +138,10 @@ static inline void profiler_unlock() {}
|
||||||
static inline void profiler_register_thread(const char* name) {}
|
static inline void profiler_register_thread(const char* name) {}
|
||||||
static inline void profiler_unregister_thread() {}
|
static inline void profiler_unregister_thread() {}
|
||||||
|
|
||||||
|
// Call by the JSRuntime's operation callback. This is used to enable
|
||||||
|
// profiling on auxilerary threads.
|
||||||
|
static inline void profiler_js_operation_callback() {}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#include "GeckoProfilerImpl.h"
|
#include "GeckoProfilerImpl.h"
|
||||||
|
|
|
@ -152,6 +152,17 @@ void profiler_unregister_thread()
|
||||||
mozilla_sampler_unregister_thread();
|
mozilla_sampler_unregister_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void profiler_js_operation_callback()
|
||||||
|
{
|
||||||
|
PseudoStack *stack = tlsPseudoStack.get();
|
||||||
|
if (!stack) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack->jsOperationCallback();
|
||||||
|
}
|
||||||
|
|
||||||
// we want the class and function name but can't easily get that using preprocessor macros
|
// 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
|
// __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,9 @@ ifndef _MSC_VER
|
||||||
FAIL_ON_WARNINGS = 1
|
FAIL_ON_WARNINGS = 1
|
||||||
endif # !_MSC_VER
|
endif # !_MSC_VER
|
||||||
|
|
||||||
|
# Uncomment for better debugging in opt builds
|
||||||
|
#MOZ_OPTIMIZE_FLAGS += -O0 -g
|
||||||
|
|
||||||
CPPSRCS = \
|
CPPSRCS = \
|
||||||
platform.cpp \
|
platform.cpp \
|
||||||
nsProfilerFactory.cpp \
|
nsProfilerFactory.cpp \
|
||||||
|
|
|
@ -134,7 +134,10 @@ std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry)
|
||||||
|
|
||||||
#define DYNAMIC_MAX_STRING 512
|
#define DYNAMIC_MAX_STRING 512
|
||||||
|
|
||||||
ThreadProfile::ThreadProfile(const char* aName, int aEntrySize, PseudoStack *aStack, int aThreadId, bool aIsMainThread)
|
ThreadProfile::ThreadProfile(const char* aName, int aEntrySize,
|
||||||
|
PseudoStack *aStack, int aThreadId,
|
||||||
|
PlatformData* aPlatform,
|
||||||
|
bool aIsMainThread)
|
||||||
: mWritePos(0)
|
: mWritePos(0)
|
||||||
, mLastFlushPos(0)
|
, mLastFlushPos(0)
|
||||||
, mReadPos(0)
|
, mReadPos(0)
|
||||||
|
@ -144,6 +147,7 @@ ThreadProfile::ThreadProfile(const char* aName, int aEntrySize, PseudoStack *aSt
|
||||||
, mName(strdup(aName))
|
, mName(strdup(aName))
|
||||||
, mThreadId(aThreadId)
|
, mThreadId(aThreadId)
|
||||||
, mIsMainThread(aIsMainThread)
|
, mIsMainThread(aIsMainThread)
|
||||||
|
, mPlatformData(aPlatform)
|
||||||
{
|
{
|
||||||
mEntries = new ProfileEntry[mEntrySize];
|
mEntries = new ProfileEntry[mEntrySize];
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "mozilla/Mutex.h"
|
#include "mozilla/Mutex.h"
|
||||||
|
|
||||||
class ThreadProfile;
|
|
||||||
class ThreadProfile;
|
class ThreadProfile;
|
||||||
|
|
||||||
class ProfileEntry
|
class ProfileEntry
|
||||||
|
@ -57,7 +56,9 @@ typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagSt
|
||||||
class ThreadProfile
|
class ThreadProfile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ThreadProfile(const char* aName, int aEntrySize, PseudoStack *aStack, int aThreadId, bool aIsMainThread);
|
ThreadProfile(const char* aName, int aEntrySize, PseudoStack *aStack,
|
||||||
|
int aThreadId, PlatformData* aPlatformData,
|
||||||
|
bool aIsMainThread);
|
||||||
~ThreadProfile();
|
~ThreadProfile();
|
||||||
void addTag(ProfileEntry aTag);
|
void addTag(ProfileEntry aTag);
|
||||||
void flush();
|
void flush();
|
||||||
|
@ -76,6 +77,7 @@ public:
|
||||||
const char* Name() const { return mName; }
|
const char* Name() const { return mName; }
|
||||||
int ThreadId() const { return mThreadId; }
|
int ThreadId() const { return mThreadId; }
|
||||||
|
|
||||||
|
PlatformData* GetPlatformData() { return mPlatformData; }
|
||||||
private:
|
private:
|
||||||
// Circular buffer 'Keep One Slot Open' implementation
|
// Circular buffer 'Keep One Slot Open' implementation
|
||||||
// for simplicity
|
// for simplicity
|
||||||
|
@ -89,6 +91,7 @@ private:
|
||||||
char* mName;
|
char* mName;
|
||||||
int mThreadId;
|
int mThreadId;
|
||||||
bool mIsMainThread;
|
bool mIsMainThread;
|
||||||
|
PlatformData* mPlatformData; // Platform specific data.
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile);
|
std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile);
|
||||||
|
|
|
@ -220,6 +220,10 @@ public:
|
||||||
mStartJSSampling = true;
|
mStartJSSampling = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void jsOperationCallback() {
|
||||||
|
if (mStartJSSampling)
|
||||||
|
enableJSSampling();
|
||||||
|
}
|
||||||
void disableJSSampling() {
|
void disableJSSampling() {
|
||||||
mStartJSSampling = false;
|
mStartJSSampling = false;
|
||||||
if (mRuntime)
|
if (mRuntime)
|
||||||
|
|
|
@ -179,6 +179,10 @@ void TableTicker::BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile)
|
||||||
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
|
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
|
||||||
|
|
||||||
for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
|
for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
|
||||||
|
// Thread not being profiled, skip it
|
||||||
|
if (!sRegisteredThreads->at(i)->Profile())
|
||||||
|
continue;
|
||||||
|
|
||||||
MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
|
MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
|
||||||
|
|
||||||
JSCustomObject* threadSamples = b.CreateObject();
|
JSCustomObject* threadSamples = b.CreateObject();
|
||||||
|
@ -291,7 +295,7 @@ void StackWalkCallback(void* aPC, void* aSP, void* aClosure)
|
||||||
void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
|
void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
|
||||||
{
|
{
|
||||||
#ifndef XP_MACOSX
|
#ifndef XP_MACOSX
|
||||||
uintptr_t thread = GetThreadHandle(platform_data());
|
uintptr_t thread = GetThreadHandle(aSample->threadProfile->GetPlatformData());
|
||||||
MOZ_ASSERT(thread);
|
MOZ_ASSERT(thread);
|
||||||
#endif
|
#endif
|
||||||
void* pc_array[1000];
|
void* pc_array[1000];
|
||||||
|
@ -308,7 +312,7 @@ void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample
|
||||||
|
|
||||||
uint32_t maxFrames = uint32_t(array.size - array.count);
|
uint32_t maxFrames = uint32_t(array.size - array.count);
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
pthread_t pt = GetProfiledThread(platform_data());
|
pthread_t pt = GetProfiledThread(aSample->threadProfile->GetPlatformData());
|
||||||
void *stackEnd = reinterpret_cast<void*>(-1);
|
void *stackEnd = reinterpret_cast<void*>(-1);
|
||||||
if (pt)
|
if (pt)
|
||||||
stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
|
stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
|
||||||
|
@ -389,11 +393,6 @@ void doSampleStackTrace(PseudoStack *aStack, ThreadProfile &aProfile, TickSample
|
||||||
|
|
||||||
void TableTicker::Tick(TickSample* sample)
|
void TableTicker::Tick(TickSample* sample)
|
||||||
{
|
{
|
||||||
if (!sample->threadProfile) {
|
|
||||||
// Platform doesn't support multithread, so use the main thread profile we created
|
|
||||||
sample->threadProfile = GetPrimaryThreadProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadProfile& currThreadProfile = *sample->threadProfile;
|
ThreadProfile& currThreadProfile = *sample->threadProfile;
|
||||||
|
|
||||||
// Marker(s) come before the sample
|
// Marker(s) come before the sample
|
||||||
|
@ -474,7 +473,7 @@ void mozilla_sampler_print_location1()
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadProfile threadProfile("Temp", PROFILE_DEFAULT_ENTRY, stack,
|
ThreadProfile threadProfile("Temp", PROFILE_DEFAULT_ENTRY, stack,
|
||||||
0, false);
|
0, Sampler::AllocPlatformData(0), false);
|
||||||
doSampleStackTrace(stack, threadProfile, NULL);
|
doSampleStackTrace(stack, threadProfile, NULL);
|
||||||
|
|
||||||
threadProfile.flush();
|
threadProfile.flush();
|
||||||
|
|
|
@ -26,7 +26,7 @@ class BreakpadSampler;
|
||||||
|
|
||||||
class TableTicker: public Sampler {
|
class TableTicker: public Sampler {
|
||||||
public:
|
public:
|
||||||
TableTicker(int aInterval, int aEntrySize, PseudoStack *aStack,
|
TableTicker(int aInterval, int aEntrySize,
|
||||||
const char** aFeatures, uint32_t aFeatureCount)
|
const char** aFeatures, uint32_t aFeatureCount)
|
||||||
: Sampler(aInterval, true, aEntrySize)
|
: Sampler(aInterval, true, aEntrySize)
|
||||||
, mPrimaryThreadProfile(nullptr)
|
, mPrimaryThreadProfile(nullptr)
|
||||||
|
@ -38,6 +38,7 @@ class TableTicker: public Sampler {
|
||||||
//XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
|
//XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
|
||||||
mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
|
mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
|
||||||
mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
|
mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
|
||||||
|
mProfileThreads = true || hasFeature(aFeatures, aFeatureCount, "threads");
|
||||||
mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
|
mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -46,10 +47,15 @@ class TableTicker: public Sampler {
|
||||||
// Create ThreadProfile for each registered thread
|
// Create ThreadProfile for each registered thread
|
||||||
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
|
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
|
||||||
ThreadInfo* info = sRegisteredThreads->at(i);
|
ThreadInfo* info = sRegisteredThreads->at(i);
|
||||||
|
|
||||||
|
if (!info->IsMainThread() && !mProfileThreads)
|
||||||
|
continue;
|
||||||
|
|
||||||
ThreadProfile* profile = new ThreadProfile(info->Name(),
|
ThreadProfile* profile = new ThreadProfile(info->Name(),
|
||||||
aEntrySize,
|
aEntrySize,
|
||||||
info->Stack(),
|
info->Stack(),
|
||||||
info->ThreadId(),
|
info->ThreadId(),
|
||||||
|
info->GetPlatformData(),
|
||||||
info->IsMainThread());
|
info->IsMainThread());
|
||||||
profile->addTag(ProfileEntry('m', "Start"));
|
profile->addTag(ProfileEntry('m', "Start"));
|
||||||
|
|
||||||
|
@ -81,8 +87,6 @@ class TableTicker: public Sampler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void SampleStack(TickSample* sample) {}
|
|
||||||
|
|
||||||
// Called within a signal. This function must be reentrant
|
// Called within a signal. This function must be reentrant
|
||||||
virtual void Tick(TickSample* sample);
|
virtual void Tick(TickSample* sample);
|
||||||
|
|
||||||
|
@ -115,7 +119,8 @@ class TableTicker: public Sampler {
|
||||||
virtual JSObject *ToJSObject(JSContext *aCx);
|
virtual JSObject *ToJSObject(JSContext *aCx);
|
||||||
JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b);
|
JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b);
|
||||||
|
|
||||||
const bool ProfileJS() { return mProfileJS; }
|
bool ProfileJS() const { return mProfileJS; }
|
||||||
|
bool ProfileThreads() const { return mProfileThreads; }
|
||||||
|
|
||||||
virtual BreakpadSampler* AsBreakpadSampler() { return nullptr; }
|
virtual BreakpadSampler* AsBreakpadSampler() { return nullptr; }
|
||||||
|
|
||||||
|
@ -133,13 +138,14 @@ protected:
|
||||||
bool mUseStackWalk;
|
bool mUseStackWalk;
|
||||||
bool mJankOnly;
|
bool mJankOnly;
|
||||||
bool mProfileJS;
|
bool mProfileJS;
|
||||||
|
bool mProfileThreads;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BreakpadSampler: public TableTicker {
|
class BreakpadSampler: public TableTicker {
|
||||||
public:
|
public:
|
||||||
BreakpadSampler(int aInterval, int aEntrySize, PseudoStack *aStack,
|
BreakpadSampler(int aInterval, int aEntrySize,
|
||||||
const char** aFeatures, uint32_t aFeatureCount)
|
const char** aFeatures, uint32_t aFeatureCount)
|
||||||
: TableTicker(aInterval, aEntrySize, aStack, aFeatures, aFeatureCount)
|
: TableTicker(aInterval, aEntrySize, aFeatures, aFeatureCount)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// Called within a signal. This function must be reentrant
|
// Called within a signal. This function must be reentrant
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
#include "mozilla/Mutex.h"
|
#include "mozilla/Mutex.h"
|
||||||
#include "ProfileEntry.h"
|
#include "ProfileEntry.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
#include "TableTicker.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -125,6 +126,19 @@ static void* setup_atfork() {
|
||||||
#include "android-signal-defs.h"
|
#include "android-signal-defs.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct SamplerRegistry {
|
||||||
|
static void AddActiveSampler(Sampler *sampler) {
|
||||||
|
ASSERT(!SamplerRegistry::sampler);
|
||||||
|
SamplerRegistry::sampler = sampler;
|
||||||
|
}
|
||||||
|
static void RemoveActiveSampler(Sampler *sampler) {
|
||||||
|
SamplerRegistry::sampler = NULL;
|
||||||
|
}
|
||||||
|
static Sampler *sampler;
|
||||||
|
};
|
||||||
|
|
||||||
|
Sampler *SamplerRegistry::sampler = NULL;
|
||||||
|
|
||||||
static ThreadProfile* sCurrentThreadProfile = NULL;
|
static ThreadProfile* sCurrentThreadProfile = NULL;
|
||||||
|
|
||||||
static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) {
|
static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) {
|
||||||
|
@ -191,90 +205,29 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
|
||||||
sCurrentThreadProfile = NULL;
|
sCurrentThreadProfile = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef XP_MACOSX
|
|
||||||
int tgkill(pid_t tgid, pid_t tid, int signalno) {
|
int tgkill(pid_t tgid, pid_t tid, int signalno) {
|
||||||
return syscall(SYS_tgkill, tgid, tid, signalno);
|
return syscall(SYS_tgkill, tgid, tid, signalno);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
class Sampler::PlatformData : public Malloced {
|
class PlatformData : public Malloced {
|
||||||
public:
|
public:
|
||||||
explicit PlatformData(Sampler* sampler)
|
PlatformData()
|
||||||
: sampler_(sampler),
|
{}
|
||||||
signal_handler_installed_(false),
|
|
||||||
vm_tgid_(getpid()),
|
|
||||||
#ifndef XP_MACOSX
|
|
||||||
vm_tid_(gettid()),
|
|
||||||
#endif
|
|
||||||
signal_sender_launched_(false)
|
|
||||||
#ifdef XP_MACOSX
|
|
||||||
, signal_receiver_(pthread_self())
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SignalSender() {
|
|
||||||
while (sampler_->IsActive()) {
|
|
||||||
sampler_->HandleSaveRequest();
|
|
||||||
|
|
||||||
if (!sampler_->IsPaused()) {
|
|
||||||
#ifdef XP_MACOSX
|
|
||||||
pthread_kill(signal_receiver_, SIGPROF);
|
|
||||||
#else
|
|
||||||
|
|
||||||
std::vector<ThreadInfo*> threads = GetRegisteredThreads();
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < threads.size(); i++) {
|
|
||||||
ThreadInfo* info = threads[i];
|
|
||||||
|
|
||||||
// We use sCurrentThreadProfile the ThreadProfile for the
|
|
||||||
// thread we're profiling to the signal handler
|
|
||||||
sCurrentThreadProfile = info->Profile();
|
|
||||||
|
|
||||||
int threadId = info->ThreadId();
|
|
||||||
if (threadId == 0) {
|
|
||||||
threadId = vm_tid_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) {
|
|
||||||
printf_stderr("profiler failed to signal tid=%d\n", threadId);
|
|
||||||
#ifdef DEBUG
|
|
||||||
abort();
|
|
||||||
#endif
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the signal handler to run before moving on to the next one
|
|
||||||
while (sCurrentThreadProfile)
|
|
||||||
sched_yield();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert ms to us and subtract 100 us to compensate delays
|
|
||||||
// occuring during signal delivery.
|
|
||||||
// TODO measure and confirm this.
|
|
||||||
const useconds_t interval = sampler_->interval_ * 1000 - 100;
|
|
||||||
//int result = usleep(interval);
|
|
||||||
usleep(interval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Sampler* sampler_;
|
|
||||||
bool signal_handler_installed_;
|
|
||||||
struct sigaction old_sigprof_signal_handler_;
|
|
||||||
struct sigaction old_sigsave_signal_handler_;
|
|
||||||
pid_t vm_tgid_;
|
|
||||||
pid_t vm_tid_;
|
|
||||||
bool signal_sender_launched_;
|
|
||||||
pthread_t signal_sender_thread_;
|
|
||||||
#ifdef XP_MACOSX
|
|
||||||
pthread_t signal_receiver_;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* static */ PlatformData*
|
||||||
|
Sampler::AllocPlatformData(int aThreadId)
|
||||||
|
{
|
||||||
|
return new PlatformData;
|
||||||
|
}
|
||||||
|
|
||||||
static void* SenderEntry(void* arg) {
|
/* static */ void
|
||||||
|
Sampler::FreePlatformData(PlatformData* aData)
|
||||||
|
{
|
||||||
|
delete aData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* SignalSender(void* arg) {
|
||||||
# if defined(ANDROID)
|
# if defined(ANDROID)
|
||||||
// pthread_atfork isn't available on Android.
|
// pthread_atfork isn't available on Android.
|
||||||
void* initialize_atfork = NULL;
|
void* initialize_atfork = NULL;
|
||||||
|
@ -283,38 +236,80 @@ static void* SenderEntry(void* arg) {
|
||||||
// It returns NULL.
|
// It returns NULL.
|
||||||
static void* initialize_atfork = setup_atfork();
|
static void* initialize_atfork = setup_atfork();
|
||||||
# endif
|
# endif
|
||||||
Sampler::PlatformData* data =
|
|
||||||
reinterpret_cast<Sampler::PlatformData*>(arg);
|
int vm_tgid_ = getpid();
|
||||||
data->SignalSender();
|
|
||||||
|
while (SamplerRegistry::sampler->IsActive()) {
|
||||||
|
SamplerRegistry::sampler->HandleSaveRequest();
|
||||||
|
|
||||||
|
if (!SamplerRegistry::sampler->IsPaused()) {
|
||||||
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
|
std::vector<ThreadInfo*> threads =
|
||||||
|
SamplerRegistry::sampler->GetRegisteredThreads();
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < threads.size(); i++) {
|
||||||
|
ThreadInfo* info = threads[i];
|
||||||
|
|
||||||
|
// This will be null if we're not interested in profiling this thread.
|
||||||
|
if (!info->Profile())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// We use sCurrentThreadProfile the ThreadProfile for the
|
||||||
|
// thread we're profiling to the signal handler
|
||||||
|
sCurrentThreadProfile = info->Profile();
|
||||||
|
|
||||||
|
int threadId = info->ThreadId();
|
||||||
|
|
||||||
|
if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) {
|
||||||
|
printf_stderr("profiler failed to signal tid=%d\n", threadId);
|
||||||
|
#ifdef DEBUG
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the signal handler to run before moving on to the next one
|
||||||
|
while (sCurrentThreadProfile)
|
||||||
|
sched_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert ms to us and subtract 100 us to compensate delays
|
||||||
|
// occuring during signal delivery.
|
||||||
|
// TODO measure and confirm this.
|
||||||
|
const useconds_t interval =
|
||||||
|
SamplerRegistry::sampler->interval() * 1000 - 100;
|
||||||
|
//int result = usleep(interval);
|
||||||
|
usleep(interval);
|
||||||
|
}
|
||||||
return initialize_atfork; // which is guaranteed to be NULL
|
return initialize_atfork; // which is guaranteed to be NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Sampler::Sampler(int interval, bool profiling, int entrySize)
|
Sampler::Sampler(int interval, bool profiling, int entrySize)
|
||||||
: interval_(interval),
|
: interval_(interval),
|
||||||
profiling_(profiling),
|
profiling_(profiling),
|
||||||
paused_(false),
|
paused_(false),
|
||||||
active_(false),
|
active_(false),
|
||||||
entrySize_(entrySize) {
|
entrySize_(entrySize) {
|
||||||
data_ = new PlatformData(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sampler::~Sampler() {
|
Sampler::~Sampler() {
|
||||||
ASSERT(!data_->signal_sender_launched_);
|
ASSERT(!signal_sender_launched_);
|
||||||
delete data_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sampler::Start() {
|
void Sampler::Start() {
|
||||||
LOG("Sampler started");
|
LOG("Sampler started");
|
||||||
|
|
||||||
|
SamplerRegistry::AddActiveSampler(this);
|
||||||
|
|
||||||
// Request profiling signals.
|
// Request profiling signals.
|
||||||
LOG("Request signal");
|
LOG("Request signal");
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
sa.sa_sigaction = ProfilerSignalHandler;
|
sa.sa_sigaction = ProfilerSignalHandler;
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||||
if (sigaction(SIGPROF, &sa, &data_->old_sigprof_signal_handler_) != 0) {
|
if (sigaction(SIGPROF, &sa, &old_sigprof_signal_handler_) != 0) {
|
||||||
LOG("Error installing signal");
|
LOG("Error installing signal");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -324,20 +319,20 @@ void Sampler::Start() {
|
||||||
sa2.sa_sigaction = ProfilerSaveSignalHandler;
|
sa2.sa_sigaction = ProfilerSaveSignalHandler;
|
||||||
sigemptyset(&sa2.sa_mask);
|
sigemptyset(&sa2.sa_mask);
|
||||||
sa2.sa_flags = SA_RESTART | SA_SIGINFO;
|
sa2.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||||
if (sigaction(SIGNAL_SAVE_PROFILE, &sa2, &data_->old_sigsave_signal_handler_) != 0) {
|
if (sigaction(SIGNAL_SAVE_PROFILE, &sa2, &old_sigsave_signal_handler_) != 0) {
|
||||||
LOG("Error installing start signal");
|
LOG("Error installing start signal");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG("Signal installed");
|
LOG("Signal installed");
|
||||||
data_->signal_handler_installed_ = true;
|
signal_handler_installed_ = true;
|
||||||
|
|
||||||
// Start a thread that sends SIGPROF signal to VM thread.
|
// Start a thread that sends SIGPROF signal to VM thread.
|
||||||
// Sending the signal ourselves instead of relying on itimer provides
|
// Sending the signal ourselves instead of relying on itimer provides
|
||||||
// much better accuracy.
|
// much better accuracy.
|
||||||
SetActive(true);
|
SetActive(true);
|
||||||
if (pthread_create(
|
if (pthread_create(
|
||||||
&data_->signal_sender_thread_, NULL, SenderEntry, data_) == 0) {
|
&signal_sender_thread_, NULL, SignalSender, NULL) == 0) {
|
||||||
data_->signal_sender_launched_ = true;
|
signal_sender_launched_ = true;
|
||||||
}
|
}
|
||||||
LOG("Profiler thread started");
|
LOG("Profiler thread started");
|
||||||
}
|
}
|
||||||
|
@ -348,32 +343,42 @@ void Sampler::Stop() {
|
||||||
|
|
||||||
// Wait for signal sender termination (it will exit after setting
|
// Wait for signal sender termination (it will exit after setting
|
||||||
// active_ to false).
|
// active_ to false).
|
||||||
if (data_->signal_sender_launched_) {
|
if (signal_sender_launched_) {
|
||||||
pthread_join(data_->signal_sender_thread_, NULL);
|
pthread_join(signal_sender_thread_, NULL);
|
||||||
data_->signal_sender_launched_ = false;
|
signal_sender_launched_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SamplerRegistry::RemoveActiveSampler(this);
|
||||||
|
|
||||||
// Restore old signal handler
|
// Restore old signal handler
|
||||||
if (data_->signal_handler_installed_) {
|
if (signal_handler_installed_) {
|
||||||
sigaction(SIGNAL_SAVE_PROFILE, &data_->old_sigsave_signal_handler_, 0);
|
sigaction(SIGNAL_SAVE_PROFILE, &old_sigsave_signal_handler_, 0);
|
||||||
sigaction(SIGPROF, &data_->old_sigprof_signal_handler_, 0);
|
sigaction(SIGPROF, &old_sigprof_signal_handler_, 0);
|
||||||
data_->signal_handler_installed_ = false;
|
signal_handler_installed_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
|
bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
|
|
||||||
ThreadInfo* info = new ThreadInfo(aName, gettid(), aIsMainThread, aPseudoStack);
|
ThreadInfo* info = new ThreadInfo(aName, gettid(),
|
||||||
|
aIsMainThread, aPseudoStack);
|
||||||
|
|
||||||
if (sActiveSampler) {
|
bool profileThread = sActiveSampler &&
|
||||||
|
(aIsMainThread || sActiveSampler->ProfileThreads());
|
||||||
|
|
||||||
|
if (profileThread) {
|
||||||
// We need to create the ThreadProfile now
|
// We need to create the ThreadProfile now
|
||||||
info->SetProfile(new ThreadProfile(info->Name(),
|
info->SetProfile(new ThreadProfile(info->Name(),
|
||||||
sActiveSampler->EntrySize(),
|
sActiveSampler->EntrySize(),
|
||||||
info->Stack(),
|
info->Stack(),
|
||||||
info->ThreadId(),
|
info->ThreadId(),
|
||||||
|
info->GetPlatformData(),
|
||||||
aIsMainThread));
|
aIsMainThread));
|
||||||
|
if (sActiveSampler->ProfileJS()) {
|
||||||
|
info->Profile()->GetPseudoStack()->enableJSSampling();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sRegisteredThreads->push_back(info);
|
sRegisteredThreads->push_back(info);
|
||||||
|
@ -382,7 +387,7 @@ bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack
|
||||||
|
|
||||||
void Sampler::UnregisterCurrentThread()
|
void Sampler::UnregisterCurrentThread()
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
|
|
||||||
int id = gettid();
|
int id = gettid();
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
#include "TableTicker.h"
|
||||||
#include "UnwinderThread2.h" /* uwt__register_thread_for_profiling */
|
#include "UnwinderThread2.h" /* uwt__register_thread_for_profiling */
|
||||||
|
|
||||||
// this port is based off of v8 svn revision 9837
|
// this port is based off of v8 svn revision 9837
|
||||||
|
@ -91,21 +92,13 @@ void OS::Sleep(int milliseconds) {
|
||||||
usleep(1000 * milliseconds);
|
usleep(1000 * milliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Thread::PlatformData : public Malloced {
|
|
||||||
public:
|
|
||||||
PlatformData() : thread_(kNoThread) {}
|
|
||||||
pthread_t thread_; // Thread handle for pthread.
|
|
||||||
};
|
|
||||||
|
|
||||||
Thread::Thread(const char* name)
|
Thread::Thread(const char* name)
|
||||||
: data_(new PlatformData),
|
: stack_size_(0) {
|
||||||
stack_size_(0) {
|
|
||||||
set_name(name);
|
set_name(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Thread::~Thread() {
|
Thread::~Thread() {
|
||||||
delete data_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -141,9 +134,9 @@ static void* ThreadEntry(void* arg) {
|
||||||
}
|
}
|
||||||
// END temp hack for SPS v1-vs-v2
|
// END temp hack for SPS v1-vs-v2
|
||||||
|
|
||||||
thread->data()->thread_ = pthread_self();
|
thread->thread_ = pthread_self();
|
||||||
SetThreadName(thread->name());
|
SetThreadName(thread->name());
|
||||||
ASSERT(thread->data()->thread_ != kNoThread);
|
ASSERT(thread->thread_ != kNoThread);
|
||||||
thread->Run();
|
thread->Run();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -163,15 +156,15 @@ void Thread::Start() {
|
||||||
pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
|
pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
|
||||||
attr_ptr = &attr;
|
attr_ptr = &attr;
|
||||||
}
|
}
|
||||||
pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
|
pthread_create(&thread_, attr_ptr, ThreadEntry, this);
|
||||||
ASSERT(data_->thread_ != kNoThread);
|
ASSERT(thread_ != kNoThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::Join() {
|
void Thread::Join() {
|
||||||
pthread_join(data_->thread_, NULL);
|
pthread_join(thread_, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Sampler::PlatformData : public Malloced {
|
class PlatformData : public Malloced {
|
||||||
public:
|
public:
|
||||||
PlatformData() : profiled_thread_(mach_thread_self())
|
PlatformData() : profiled_thread_(mach_thread_self())
|
||||||
{
|
{
|
||||||
|
@ -197,6 +190,17 @@ class Sampler::PlatformData : public Malloced {
|
||||||
pthread_t profiled_pthread_;
|
pthread_t profiled_pthread_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* static */ PlatformData*
|
||||||
|
Sampler::AllocPlatformData(int aThreadId)
|
||||||
|
{
|
||||||
|
return new PlatformData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
Sampler::FreePlatformData(PlatformData* aData)
|
||||||
|
{
|
||||||
|
delete aData;
|
||||||
|
}
|
||||||
|
|
||||||
class SamplerThread : public Thread {
|
class SamplerThread : public Thread {
|
||||||
public:
|
public:
|
||||||
|
@ -205,7 +209,7 @@ class SamplerThread : public Thread {
|
||||||
, interval_(interval) {}
|
, interval_(interval) {}
|
||||||
|
|
||||||
static void AddActiveSampler(Sampler* sampler) {
|
static void AddActiveSampler(Sampler* sampler) {
|
||||||
ScopedLock lock(mutex_);
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
SamplerRegistry::AddActiveSampler(sampler);
|
SamplerRegistry::AddActiveSampler(sampler);
|
||||||
if (instance_ == NULL) {
|
if (instance_ == NULL) {
|
||||||
instance_ = new SamplerThread(sampler->interval());
|
instance_ = new SamplerThread(sampler->interval());
|
||||||
|
@ -216,37 +220,45 @@ class SamplerThread : public Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RemoveActiveSampler(Sampler* sampler) {
|
static void RemoveActiveSampler(Sampler* sampler) {
|
||||||
ScopedLock lock(mutex_);
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
instance_->Join();
|
instance_->Join();
|
||||||
//XXX: unlike v8 we need to remove the active sampler after doing the Join
|
//XXX: unlike v8 we need to remove the active sampler after doing the Join
|
||||||
// because we drop the sampler immediately
|
// because we drop the sampler immediately
|
||||||
SamplerRegistry::RemoveActiveSampler(sampler);
|
SamplerRegistry::RemoveActiveSampler(sampler);
|
||||||
delete instance_;
|
delete instance_;
|
||||||
instance_ = NULL;
|
instance_ = NULL;
|
||||||
/*
|
|
||||||
if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
|
|
||||||
RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
|
|
||||||
delete instance_;
|
|
||||||
instance_ = NULL;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Thread::Run().
|
// Implement Thread::Run().
|
||||||
virtual void Run() {
|
virtual void Run() {
|
||||||
while (SamplerRegistry::sampler->IsActive()) {
|
while (SamplerRegistry::sampler->IsActive()) {
|
||||||
if (!SamplerRegistry::sampler->IsPaused())
|
{
|
||||||
SampleContext(SamplerRegistry::sampler);
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
|
std::vector<ThreadInfo*> threads =
|
||||||
|
SamplerRegistry::sampler->GetRegisteredThreads();
|
||||||
|
for (uint32_t i = 0; i < threads.size(); i++) {
|
||||||
|
ThreadInfo* info = threads[i];
|
||||||
|
|
||||||
|
// This will be null if we're not interested in profiling this thread.
|
||||||
|
if (!info->Profile())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ThreadProfile* thread_profile = info->Profile();
|
||||||
|
|
||||||
|
if (!SamplerRegistry::sampler->IsPaused())
|
||||||
|
SampleContext(SamplerRegistry::sampler, thread_profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
OS::Sleep(interval_);
|
OS::Sleep(interval_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleContext(Sampler* sampler) {
|
void SampleContext(Sampler* sampler, ThreadProfile* thread_profile) {
|
||||||
thread_act_t profiled_thread = sampler->platform_data()->profiled_thread();
|
thread_act_t profiled_thread =
|
||||||
|
thread_profile->GetPlatformData()->profiled_thread();
|
||||||
|
|
||||||
TickSample sample_obj;
|
TickSample sample_obj;
|
||||||
TickSample* sample = &sample_obj;
|
TickSample* sample = &sample_obj;
|
||||||
//TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate());
|
|
||||||
//if (sample == NULL) sample = &sample_obj;
|
|
||||||
|
|
||||||
if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
|
if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
|
||||||
|
|
||||||
|
@ -276,13 +288,11 @@ class SamplerThread : public Thread {
|
||||||
flavor,
|
flavor,
|
||||||
reinterpret_cast<natural_t*>(&state),
|
reinterpret_cast<natural_t*>(&state),
|
||||||
&count) == KERN_SUCCESS) {
|
&count) == KERN_SUCCESS) {
|
||||||
//sample->state = sampler->isolate()->current_vm_state();
|
|
||||||
sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
|
sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
|
||||||
sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
|
sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
|
||||||
sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
|
sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
|
||||||
sample->timestamp = mozilla::TimeStamp::Now();
|
sample->timestamp = mozilla::TimeStamp::Now();
|
||||||
sample->threadProfile = NULL;
|
sample->threadProfile = thread_profile;
|
||||||
sampler->SampleStack(sample);
|
|
||||||
sampler->Tick(sample);
|
sampler->Tick(sample);
|
||||||
}
|
}
|
||||||
thread_resume(profiled_thread);
|
thread_resume(profiled_thread);
|
||||||
|
@ -300,11 +310,8 @@ class SamplerThread : public Thread {
|
||||||
|
|
||||||
#undef REGISTER_FIELD
|
#undef REGISTER_FIELD
|
||||||
|
|
||||||
|
|
||||||
Mutex* SamplerThread::mutex_ = OS::CreateMutex();
|
|
||||||
SamplerThread* SamplerThread::instance_ = NULL;
|
SamplerThread* SamplerThread::instance_ = NULL;
|
||||||
|
|
||||||
|
|
||||||
Sampler::Sampler(int interval, bool profiling, int entrySize)
|
Sampler::Sampler(int interval, bool profiling, int entrySize)
|
||||||
: // isolate_(isolate),
|
: // isolate_(isolate),
|
||||||
interval_(interval),
|
interval_(interval),
|
||||||
|
@ -313,13 +320,11 @@ Sampler::Sampler(int interval, bool profiling, int entrySize)
|
||||||
active_(false),
|
active_(false),
|
||||||
entrySize_(entrySize) /*,
|
entrySize_(entrySize) /*,
|
||||||
samples_taken_(0)*/ {
|
samples_taken_(0)*/ {
|
||||||
data_ = new PlatformData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Sampler::~Sampler() {
|
Sampler::~Sampler() {
|
||||||
ASSERT(!IsActive());
|
ASSERT(!IsActive());
|
||||||
delete data_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -337,27 +342,38 @@ void Sampler::Stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_t
|
pthread_t
|
||||||
Sampler::GetProfiledThread(Sampler::PlatformData* aData)
|
Sampler::GetProfiledThread(PlatformData* aData)
|
||||||
{
|
{
|
||||||
return aData->profiled_pthread();
|
return aData->profiled_pthread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
pid_t gettid()
|
||||||
|
{
|
||||||
|
return (pid_t) syscall(SYS_thread_selfid);
|
||||||
|
}
|
||||||
|
|
||||||
bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
|
bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
|
|
||||||
if (!aIsMainThread)
|
ThreadInfo* info = new ThreadInfo(aName, gettid(),
|
||||||
return false;
|
aIsMainThread, aPseudoStack);
|
||||||
|
|
||||||
ThreadInfo* info = new ThreadInfo(aName, 0, true, aPseudoStack);
|
bool profileThread = sActiveSampler &&
|
||||||
|
(aIsMainThread || sActiveSampler->ProfileThreads());
|
||||||
|
|
||||||
if (sActiveSampler) {
|
if (profileThread) {
|
||||||
// We need to create the ThreadProfile now
|
// We need to create the ThreadProfile now
|
||||||
info->SetProfile(new ThreadProfile(info->Name(),
|
info->SetProfile(new ThreadProfile(info->Name(),
|
||||||
sActiveSampler->EntrySize(),
|
sActiveSampler->EntrySize(),
|
||||||
info->Stack(),
|
info->Stack(),
|
||||||
info->ThreadId(),
|
info->ThreadId(),
|
||||||
true));
|
info->GetPlatformData(),
|
||||||
|
aIsMainThread));
|
||||||
|
if (sActiveSampler->ProfileJS()) {
|
||||||
|
info->Profile()->GetPseudoStack()->enableJSSampling();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sRegisteredThreads->push_back(info);
|
sRegisteredThreads->push_back(info);
|
||||||
|
@ -366,5 +382,16 @@ bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack
|
||||||
|
|
||||||
void Sampler::UnregisterCurrentThread()
|
void Sampler::UnregisterCurrentThread()
|
||||||
{
|
{
|
||||||
// We only have the main thread currently and that will never be unregistered
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
}
|
|
||||||
|
int id = gettid();
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
|
||||||
|
ThreadInfo* info = sRegisteredThreads->at(i);
|
||||||
|
if (info->ThreadId() == id) {
|
||||||
|
delete info;
|
||||||
|
sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,21 +30,21 @@
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
#include "TableTicker.h"
|
||||||
#include "ProfileEntry.h"
|
#include "ProfileEntry.h"
|
||||||
|
|
||||||
class Sampler::PlatformData : public Malloced {
|
class PlatformData : public Malloced {
|
||||||
public:
|
public:
|
||||||
// Get a handle to the calling thread. This is the thread that we are
|
// Get a handle to the calling thread. This is the thread that we are
|
||||||
// going to profile. We need to make a copy of the handle because we are
|
// going to profile. We need to make a copy of the handle because we are
|
||||||
// going to use it in the sampler thread. Using GetThreadHandle() will
|
// going to use it in the sampler thread. Using GetThreadHandle() will
|
||||||
// not work in this case. We're using OpenThread because DuplicateHandle
|
// not work in this case. We're using OpenThread because DuplicateHandle
|
||||||
// for some reason doesn't work in Chrome's sandbox.
|
// for some reason doesn't work in Chrome's sandbox.
|
||||||
PlatformData() : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
|
PlatformData(int aThreadId) : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
|
||||||
THREAD_SUSPEND_RESUME |
|
THREAD_SUSPEND_RESUME |
|
||||||
THREAD_QUERY_INFORMATION,
|
THREAD_QUERY_INFORMATION,
|
||||||
false,
|
false,
|
||||||
GetCurrentThreadId())) {}
|
aThreadId)) {}
|
||||||
|
|
||||||
~PlatformData() {
|
~PlatformData() {
|
||||||
if (profiled_thread_ != NULL) {
|
if (profiled_thread_ != NULL) {
|
||||||
|
@ -59,8 +59,20 @@ class Sampler::PlatformData : public Malloced {
|
||||||
HANDLE profiled_thread_;
|
HANDLE profiled_thread_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* static */ PlatformData*
|
||||||
|
Sampler::AllocPlatformData(int aThreadId)
|
||||||
|
{
|
||||||
|
return new PlatformData(aThreadId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
Sampler::FreePlatformData(PlatformData* aData)
|
||||||
|
{
|
||||||
|
delete aData;
|
||||||
|
}
|
||||||
|
|
||||||
uintptr_t
|
uintptr_t
|
||||||
Sampler::GetThreadHandle(Sampler::PlatformData* aData)
|
Sampler::GetThreadHandle(PlatformData* aData)
|
||||||
{
|
{
|
||||||
return (uintptr_t) aData->profiled_thread();
|
return (uintptr_t) aData->profiled_thread();
|
||||||
}
|
}
|
||||||
|
@ -97,8 +109,24 @@ class SamplerThread : public Thread {
|
||||||
::timeBeginPeriod(interval_);
|
::timeBeginPeriod(interval_);
|
||||||
|
|
||||||
while (sampler_->IsActive()) {
|
while (sampler_->IsActive()) {
|
||||||
if (!sampler_->IsPaused())
|
{
|
||||||
SampleContext(sampler_);
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
|
std::vector<ThreadInfo*> threads =
|
||||||
|
sampler_->GetRegisteredThreads();
|
||||||
|
for (uint32_t i = 0; i < threads.size(); i++) {
|
||||||
|
ThreadInfo* info = threads[i];
|
||||||
|
|
||||||
|
// This will be null if we're not interested in profiling this thread.
|
||||||
|
if (!info->Profile())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ThreadProfile* thread_profile = info->Profile();
|
||||||
|
|
||||||
|
if (!sampler_->IsPaused()) {
|
||||||
|
SampleContext(sampler_, thread_profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
OS::Sleep(interval_);
|
OS::Sleep(interval_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,8 +135,10 @@ class SamplerThread : public Thread {
|
||||||
::timeEndPeriod(interval_);
|
::timeEndPeriod(interval_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleContext(Sampler* sampler) {
|
void SampleContext(Sampler* sampler, ThreadProfile* thread_profile) {
|
||||||
HANDLE profiled_thread = sampler->platform_data()->profiled_thread();
|
uintptr_t thread = Sampler::GetThreadHandle(
|
||||||
|
thread_profile->GetPlatformData());
|
||||||
|
HANDLE profiled_thread = reinterpret_cast<HANDLE>(thread);
|
||||||
if (profiled_thread == NULL)
|
if (profiled_thread == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -121,7 +151,7 @@ class SamplerThread : public Thread {
|
||||||
|
|
||||||
// Grab the timestamp before pausing the thread, to avoid deadlocks.
|
// Grab the timestamp before pausing the thread, to avoid deadlocks.
|
||||||
sample->timestamp = mozilla::TimeStamp::Now();
|
sample->timestamp = mozilla::TimeStamp::Now();
|
||||||
sample->threadProfile = NULL;
|
sample->threadProfile = thread_profile;
|
||||||
|
|
||||||
static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
|
static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
|
||||||
if (SuspendThread(profiled_thread) == kSuspendFailed)
|
if (SuspendThread(profiled_thread) == kSuspendFailed)
|
||||||
|
@ -139,7 +169,6 @@ class SamplerThread : public Thread {
|
||||||
sample->fp = reinterpret_cast<Address>(context.Ebp);
|
sample->fp = reinterpret_cast<Address>(context.Ebp);
|
||||||
#endif
|
#endif
|
||||||
sample->context = &context;
|
sample->context = &context;
|
||||||
sampler->SampleStack(sample);
|
|
||||||
sampler->Tick(sample);
|
sampler->Tick(sample);
|
||||||
}
|
}
|
||||||
ResumeThread(profiled_thread);
|
ResumeThread(profiled_thread);
|
||||||
|
@ -162,13 +191,11 @@ Sampler::Sampler(int interval, bool profiling, int entrySize)
|
||||||
profiling_(profiling),
|
profiling_(profiling),
|
||||||
paused_(false),
|
paused_(false),
|
||||||
active_(false),
|
active_(false),
|
||||||
entrySize_(entrySize),
|
entrySize_(entrySize) {
|
||||||
data_(new PlatformData) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sampler::~Sampler() {
|
Sampler::~Sampler() {
|
||||||
ASSERT(!IsActive());
|
ASSERT(!IsActive());
|
||||||
delete data_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sampler::Start() {
|
void Sampler::Start() {
|
||||||
|
@ -192,18 +219,11 @@ static unsigned int __stdcall ThreadEntry(void* arg) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Thread::PlatformData : public Malloced {
|
|
||||||
public:
|
|
||||||
explicit PlatformData(HANDLE thread) : thread_(thread) {}
|
|
||||||
HANDLE thread_;
|
|
||||||
unsigned thread_id_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize a Win32 thread object. The thread has an invalid thread
|
// Initialize a Win32 thread object. The thread has an invalid thread
|
||||||
// handle until it is started.
|
// handle until it is started.
|
||||||
Thread::Thread(const char* name)
|
Thread::Thread(const char* name)
|
||||||
: stack_size_(0) {
|
: stack_size_(0) {
|
||||||
data_ = new PlatformData(kNoThread);
|
thread_ = kNoThread;
|
||||||
set_name(name);
|
set_name(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,27 +234,26 @@ void Thread::set_name(const char* name) {
|
||||||
|
|
||||||
// Close our own handle for the thread.
|
// Close our own handle for the thread.
|
||||||
Thread::~Thread() {
|
Thread::~Thread() {
|
||||||
if (data_->thread_ != kNoThread) CloseHandle(data_->thread_);
|
if (thread_ != kNoThread) CloseHandle(thread_);
|
||||||
delete data_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new thread. It is important to use _beginthreadex() instead of
|
// Create a new thread. It is important to use _beginthreadex() instead of
|
||||||
// the Win32 function CreateThread(), because the CreateThread() does not
|
// the Win32 function CreateThread(), because the CreateThread() does not
|
||||||
// initialize thread specific structures in the C runtime library.
|
// initialize thread specific structures in the C runtime library.
|
||||||
void Thread::Start() {
|
void Thread::Start() {
|
||||||
data_->thread_ = reinterpret_cast<HANDLE>(
|
thread_ = reinterpret_cast<HANDLE>(
|
||||||
_beginthreadex(NULL,
|
_beginthreadex(NULL,
|
||||||
static_cast<unsigned>(stack_size_),
|
static_cast<unsigned>(stack_size_),
|
||||||
ThreadEntry,
|
ThreadEntry,
|
||||||
this,
|
this,
|
||||||
0,
|
0,
|
||||||
&data_->thread_id_));
|
&thread_id_));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for thread to terminate.
|
// Wait for thread to terminate.
|
||||||
void Thread::Join() {
|
void Thread::Join() {
|
||||||
if (data_->thread_id_ != GetCurrentThreadId()) {
|
if (thread_id_ != GetCurrentThreadId()) {
|
||||||
WaitForSingleObject(data_->thread_, INFINITE);
|
WaitForSingleObject(thread_, INFINITE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,20 +263,25 @@ void OS::Sleep(int milliseconds) {
|
||||||
|
|
||||||
bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
|
bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread)
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
|
|
||||||
if (!aIsMainThread)
|
ThreadInfo* info = new ThreadInfo(aName, GetCurrentThreadId(),
|
||||||
return false;
|
aIsMainThread, aPseudoStack);
|
||||||
|
|
||||||
ThreadInfo* info = new ThreadInfo(aName, 0, true, aPseudoStack);
|
bool profileThread = sActiveSampler &&
|
||||||
|
(aIsMainThread || sActiveSampler->ProfileThreads());
|
||||||
|
|
||||||
if (sActiveSampler) {
|
if (profileThread) {
|
||||||
// We need to create the ThreadProfile now
|
// We need to create the ThreadProfile now
|
||||||
info->SetProfile(new ThreadProfile(info->Name(),
|
info->SetProfile(new ThreadProfile(info->Name(),
|
||||||
sActiveSampler->EntrySize(),
|
sActiveSampler->EntrySize(),
|
||||||
info->Stack(),
|
info->Stack(),
|
||||||
info->ThreadId(),
|
GetCurrentThreadId(),
|
||||||
true));
|
info->GetPlatformData(),
|
||||||
|
aIsMainThread));
|
||||||
|
if (sActiveSampler->ProfileJS()) {
|
||||||
|
info->Profile()->GetPseudoStack()->enableJSSampling();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sRegisteredThreads->push_back(info);
|
sRegisteredThreads->push_back(info);
|
||||||
|
@ -266,5 +290,16 @@ bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack
|
||||||
|
|
||||||
void Sampler::UnregisterCurrentThread()
|
void Sampler::UnregisterCurrentThread()
|
||||||
{
|
{
|
||||||
// We only have the main thread currently and that will never be unregistered
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
}
|
|
||||||
|
int id = GetCurrentThreadId();
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
|
||||||
|
ThreadInfo* info = sRegisteredThreads->at(i);
|
||||||
|
if (info->ThreadId() == id) {
|
||||||
|
delete info;
|
||||||
|
sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -28,9 +28,10 @@ mozilla::ThreadLocal<TableTicker *> tlsTicker;
|
||||||
// it as the flag itself.
|
// it as the flag itself.
|
||||||
bool stack_key_initialized;
|
bool stack_key_initialized;
|
||||||
|
|
||||||
TimeStamp sLastTracerEvent; // is raced on
|
TimeStamp sLastTracerEvent; // is raced on
|
||||||
int sFrameNumber = 0;
|
int sFrameNumber = 0;
|
||||||
int sLastFrameNumber = 0;
|
int sLastFrameNumber = 0;
|
||||||
|
static bool sIsProfiling = false; // is raced on
|
||||||
|
|
||||||
/* used to keep track of the last event that we sampled during */
|
/* used to keep track of the last event that we sampled during */
|
||||||
unsigned int sLastSampledEventGeneration = 0;
|
unsigned int sLastSampledEventGeneration = 0;
|
||||||
|
@ -44,16 +45,33 @@ unsigned int sCurrentEventGeneration = 0;
|
||||||
* a problem if 2^32 events happen between samples that we need
|
* a problem if 2^32 events happen between samples that we need
|
||||||
* to know are associated with different events */
|
* to know are associated with different events */
|
||||||
|
|
||||||
std::vector<ThreadInfo*>* Sampler::sRegisteredThreads = new std::vector<ThreadInfo*>();
|
std::vector<ThreadInfo*>* Sampler::sRegisteredThreads = nullptr;
|
||||||
mozilla::Mutex* Sampler::sRegisteredThreadsMutex = new mozilla::Mutex("sRegisteredThreads mutex");
|
mozilla::Mutex* Sampler::sRegisteredThreadsMutex = nullptr;
|
||||||
|
|
||||||
Sampler* Sampler::sActiveSampler;
|
TableTicker* Sampler::sActiveSampler;
|
||||||
|
|
||||||
|
void Sampler::Startup() {
|
||||||
|
sRegisteredThreads = new std::vector<ThreadInfo*>();
|
||||||
|
sRegisteredThreadsMutex = new mozilla::Mutex("sRegisteredThreads mutex");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sampler::Shutdown() {
|
||||||
|
while (sRegisteredThreads->size() > 0) {
|
||||||
|
delete sRegisteredThreads->back();
|
||||||
|
sRegisteredThreads->pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete sRegisteredThreadsMutex;
|
||||||
|
delete sRegisteredThreads;
|
||||||
|
}
|
||||||
|
|
||||||
ThreadInfo::~ThreadInfo() {
|
ThreadInfo::~ThreadInfo() {
|
||||||
free(mName);
|
free(mName);
|
||||||
|
|
||||||
if (mProfile)
|
if (mProfile)
|
||||||
delete mProfile;
|
delete mProfile;
|
||||||
|
|
||||||
|
Sampler::FreePlatformData(mPlatformData);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sps_version2()
|
bool sps_version2()
|
||||||
|
@ -235,9 +253,13 @@ void mozilla_sampler_init()
|
||||||
}
|
}
|
||||||
stack_key_initialized = true;
|
stack_key_initialized = true;
|
||||||
|
|
||||||
|
Sampler::Startup();
|
||||||
|
|
||||||
PseudoStack *stack = new PseudoStack();
|
PseudoStack *stack = new PseudoStack();
|
||||||
tlsPseudoStack.set(stack);
|
tlsPseudoStack.set(stack);
|
||||||
|
|
||||||
|
Sampler::RegisterCurrentThread("Gecko", stack, true);
|
||||||
|
|
||||||
if (sps_version2()) {
|
if (sps_version2()) {
|
||||||
// Read mode settings from MOZ_PROFILER_MODE and interval
|
// Read mode settings from MOZ_PROFILER_MODE and interval
|
||||||
// settings from MOZ_PROFILER_INTERVAL and stack-scan threshhold
|
// settings from MOZ_PROFILER_INTERVAL and stack-scan threshhold
|
||||||
|
@ -306,9 +328,10 @@ void mozilla_sampler_shutdown()
|
||||||
uwt__deinit();
|
uwt__deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
Sampler::FreeRegisteredThreads();
|
|
||||||
|
|
||||||
profiler_stop();
|
profiler_stop();
|
||||||
|
|
||||||
|
Sampler::Shutdown();
|
||||||
|
|
||||||
// We can't delete the Stack because we can be between a
|
// We can't delete the Stack because we can be between a
|
||||||
// sampler call_enter/call_exit point.
|
// sampler call_enter/call_exit point.
|
||||||
// TODO Need to find a safe time to delete Stack
|
// TODO Need to find a safe time to delete Stack
|
||||||
|
@ -367,6 +390,7 @@ const char** mozilla_sampler_get_features()
|
||||||
#endif
|
#endif
|
||||||
"jank",
|
"jank",
|
||||||
"js",
|
"js",
|
||||||
|
"threads",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -398,16 +422,29 @@ void mozilla_sampler_start(int aProfileEntries, int aInterval,
|
||||||
if (sps_version2()) {
|
if (sps_version2()) {
|
||||||
t = new BreakpadSampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
|
t = new BreakpadSampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
|
||||||
aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
|
aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
|
||||||
stack, aFeatures, aFeatureCount);
|
aFeatures, aFeatureCount);
|
||||||
} else {
|
} else {
|
||||||
t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
|
t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
|
||||||
aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
|
aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
|
||||||
stack, aFeatures, aFeatureCount);
|
aFeatures, aFeatureCount);
|
||||||
}
|
}
|
||||||
tlsTicker.set(t);
|
tlsTicker.set(t);
|
||||||
t->Start();
|
t->Start();
|
||||||
if (t->ProfileJS())
|
if (t->ProfileJS()) {
|
||||||
stack->enableJSSampling();
|
mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
|
||||||
|
std::vector<ThreadInfo*> threads = t->GetRegisteredThreads();
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < threads.size(); i++) {
|
||||||
|
ThreadInfo* info = threads[i];
|
||||||
|
ThreadProfile* thread_profile = info->Profile();
|
||||||
|
if (!thread_profile) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
thread_profile->GetPseudoStack()->enableJSSampling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sIsProfiling = true;
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||||
if (os)
|
if (os)
|
||||||
|
@ -435,6 +472,8 @@ void mozilla_sampler_stop()
|
||||||
if (disableJS)
|
if (disableJS)
|
||||||
stack->disableJSSampling();
|
stack->disableJSSampling();
|
||||||
|
|
||||||
|
sIsProfiling = false;
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||||
if (os)
|
if (os)
|
||||||
os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
|
os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
|
||||||
|
@ -442,15 +481,7 @@ void mozilla_sampler_stop()
|
||||||
|
|
||||||
bool mozilla_sampler_is_active()
|
bool mozilla_sampler_is_active()
|
||||||
{
|
{
|
||||||
if (!stack_key_initialized)
|
return sIsProfiling;
|
||||||
profiler_init();
|
|
||||||
|
|
||||||
TableTicker *t = tlsTicker.get();
|
|
||||||
if (!t) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return t->IsActive();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static double sResponsivenessTimes[100];
|
static double sResponsivenessTimes[100];
|
||||||
|
|
|
@ -35,14 +35,23 @@
|
||||||
#define __android_log_print(a, ...)
|
#define __android_log_print(a, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef XP_UNIX
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "mozilla/StandardInteger.h"
|
#include "mozilla/StandardInteger.h"
|
||||||
#include "mozilla/Util.h"
|
#include "mozilla/Util.h"
|
||||||
#include "mozilla/unused.h"
|
#include "mozilla/unused.h"
|
||||||
#include "mozilla/TimeStamp.h"
|
#include "mozilla/TimeStamp.h"
|
||||||
|
#include "mozilla/Mutex.h"
|
||||||
#include "PlatformMacros.h"
|
#include "PlatformMacros.h"
|
||||||
#include "v8-support.h"
|
#include "v8-support.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef XP_WIN
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define ASSERT(a) MOZ_ASSERT(a)
|
#define ASSERT(a) MOZ_ASSERT(a)
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
|
@ -183,14 +192,17 @@ class Thread {
|
||||||
// prctl().
|
// prctl().
|
||||||
static const int kMaxThreadNameLength = 16;
|
static const int kMaxThreadNameLength = 16;
|
||||||
|
|
||||||
class PlatformData;
|
#ifdef XP_WIN
|
||||||
PlatformData* data() { return data_; }
|
HANDLE thread_;
|
||||||
|
unsigned thread_id_;
|
||||||
|
#endif
|
||||||
|
#if defined(XP_MACOSX)
|
||||||
|
pthread_t thread_;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void set_name(const char *name);
|
void set_name(const char *name);
|
||||||
|
|
||||||
PlatformData* data_;
|
|
||||||
|
|
||||||
char name_[kMaxThreadNameLength];
|
char name_[kMaxThreadNameLength];
|
||||||
int stack_size_;
|
int stack_size_;
|
||||||
|
|
||||||
|
@ -264,34 +276,9 @@ class TickSample {
|
||||||
mozilla::TimeStamp timestamp;
|
mozilla::TimeStamp timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ThreadInfo {
|
class ThreadInfo;
|
||||||
public:
|
class PlatformData;
|
||||||
ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack)
|
class TableTicker;
|
||||||
: mName(strdup(aName))
|
|
||||||
, mThreadId(aThreadId)
|
|
||||||
, mIsMainThread(aIsMainThread)
|
|
||||||
, mPseudoStack(aPseudoStack)
|
|
||||||
, mProfile(NULL) {}
|
|
||||||
|
|
||||||
virtual ~ThreadInfo();
|
|
||||||
|
|
||||||
const char* Name() const { return mName; }
|
|
||||||
int ThreadId() const { return mThreadId; }
|
|
||||||
|
|
||||||
bool IsMainThread() const { return mIsMainThread; }
|
|
||||||
PseudoStack* Stack() const { return mPseudoStack; }
|
|
||||||
|
|
||||||
void SetProfile(ThreadProfile* aProfile) { mProfile = aProfile; }
|
|
||||||
ThreadProfile* Profile() const { return mProfile; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
char* mName;
|
|
||||||
int mThreadId;
|
|
||||||
const bool mIsMainThread;
|
|
||||||
PseudoStack* mPseudoStack;
|
|
||||||
ThreadProfile* mProfile;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Sampler {
|
class Sampler {
|
||||||
public:
|
public:
|
||||||
// Initialize sampler.
|
// Initialize sampler.
|
||||||
|
@ -300,9 +287,6 @@ class Sampler {
|
||||||
|
|
||||||
int interval() const { return interval_; }
|
int interval() const { return interval_; }
|
||||||
|
|
||||||
// Performs stack sampling.
|
|
||||||
virtual void SampleStack(TickSample* sample) = 0;
|
|
||||||
|
|
||||||
// This method is called for each sampling period with the current
|
// This method is called for each sampling period with the current
|
||||||
// program counter.
|
// program counter.
|
||||||
virtual void Tick(TickSample* sample) = 0;
|
virtual void Tick(TickSample* sample) = 0;
|
||||||
|
@ -326,11 +310,14 @@ class Sampler {
|
||||||
bool IsPaused() const { return paused_; }
|
bool IsPaused() const { return paused_; }
|
||||||
void SetPaused(bool value) { NoBarrier_Store(&paused_, value); }
|
void SetPaused(bool value) { NoBarrier_Store(&paused_, value); }
|
||||||
|
|
||||||
|
virtual bool ProfileThreads() const = 0;
|
||||||
|
|
||||||
int EntrySize() { return entrySize_; }
|
int EntrySize() { return entrySize_; }
|
||||||
|
|
||||||
class PlatformData;
|
// We can't new/delete the type safely without defining it
|
||||||
|
// (-Wdelete-incomplete). Use these Alloc/Free functions instead.
|
||||||
PlatformData* platform_data() { return data_; }
|
static PlatformData* AllocPlatformData(int aThreadId);
|
||||||
|
static void FreePlatformData(PlatformData*);
|
||||||
|
|
||||||
// If we move the backtracing code into the platform files we won't
|
// If we move the backtracing code into the platform files we won't
|
||||||
// need to have these hacks
|
// need to have these hacks
|
||||||
|
@ -343,31 +330,23 @@ class Sampler {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static std::vector<ThreadInfo*> GetRegisteredThreads() {
|
static std::vector<ThreadInfo*> GetRegisteredThreads() {
|
||||||
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
|
|
||||||
|
|
||||||
return *sRegisteredThreads;
|
return *sRegisteredThreads;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread);
|
static bool RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread);
|
||||||
static void UnregisterCurrentThread();
|
static void UnregisterCurrentThread();
|
||||||
|
|
||||||
|
static void Startup();
|
||||||
// Should only be called on shutdown
|
// Should only be called on shutdown
|
||||||
static void FreeRegisteredThreads() {
|
static void Shutdown();
|
||||||
while (sRegisteredThreads->size() > 0) {
|
|
||||||
sRegisteredThreads->pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete sRegisteredThreadsMutex;
|
static TableTicker* GetActiveSampler() { return sActiveSampler; }
|
||||||
delete sRegisteredThreads;
|
static void SetActiveSampler(TableTicker* sampler) { sActiveSampler = sampler; }
|
||||||
}
|
|
||||||
|
|
||||||
static Sampler* GetActiveSampler() { return sActiveSampler; }
|
|
||||||
static void SetActiveSampler(Sampler* sampler) { sActiveSampler = sampler; }
|
|
||||||
|
|
||||||
|
static mozilla::Mutex* sRegisteredThreadsMutex;
|
||||||
protected:
|
protected:
|
||||||
static std::vector<ThreadInfo*>* sRegisteredThreads;
|
static std::vector<ThreadInfo*>* sRegisteredThreads;
|
||||||
static mozilla::Mutex* sRegisteredThreadsMutex;
|
static TableTicker* sActiveSampler;
|
||||||
static Sampler* sActiveSampler;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetActive(bool value) { NoBarrier_Store(&active_, value); }
|
void SetActive(bool value) { NoBarrier_Store(&active_, value); }
|
||||||
|
@ -377,7 +356,46 @@ class Sampler {
|
||||||
Atomic32 paused_;
|
Atomic32 paused_;
|
||||||
Atomic32 active_;
|
Atomic32 active_;
|
||||||
const int entrySize_;
|
const int entrySize_;
|
||||||
PlatformData* data_; // Platform specific data.
|
|
||||||
|
// Refactor me!
|
||||||
|
#if defined(SPS_OS_linux) || defined(SPS_OS_android)
|
||||||
|
bool signal_handler_installed_;
|
||||||
|
struct sigaction old_sigprof_signal_handler_;
|
||||||
|
struct sigaction old_sigsave_signal_handler_;
|
||||||
|
bool signal_sender_launched_;
|
||||||
|
pthread_t signal_sender_thread_;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThreadInfo {
|
||||||
|
public:
|
||||||
|
ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack)
|
||||||
|
: mName(strdup(aName))
|
||||||
|
, mThreadId(aThreadId)
|
||||||
|
, mIsMainThread(aIsMainThread)
|
||||||
|
, mPseudoStack(aPseudoStack)
|
||||||
|
, mPlatformData(Sampler::AllocPlatformData(aThreadId))
|
||||||
|
, mProfile(NULL) {}
|
||||||
|
|
||||||
|
virtual ~ThreadInfo();
|
||||||
|
|
||||||
|
const char* Name() const { return mName; }
|
||||||
|
int ThreadId() const { return mThreadId; }
|
||||||
|
|
||||||
|
bool IsMainThread() const { return mIsMainThread; }
|
||||||
|
PseudoStack* Stack() const { return mPseudoStack; }
|
||||||
|
|
||||||
|
void SetProfile(ThreadProfile* aProfile) { mProfile = aProfile; }
|
||||||
|
ThreadProfile* Profile() const { return mProfile; }
|
||||||
|
|
||||||
|
PlatformData* GetPlatformData() const { return mPlatformData; }
|
||||||
|
private:
|
||||||
|
char* mName;
|
||||||
|
int mThreadId;
|
||||||
|
const bool mIsMainThread;
|
||||||
|
PseudoStack* mPseudoStack;
|
||||||
|
PlatformData* mPlatformData;
|
||||||
|
ThreadProfile* mProfile;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* ndef TOOLS_PLATFORM_H_ */
|
#endif /* ndef TOOLS_PLATFORM_H_ */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче