Bug 734691 - Port multi-thread support to win/mac. r=snorp,smaug

--HG--
extra : rebase_source : ce0d9f94d2b4deb249e09198f9315f69ad6ee7d4
This commit is contained in:
Benoit Girard 2013-04-03 18:59:17 -04:00
parent 07acfb74dc
commit d8168f5e54
14 changed files with 438 additions and 276 deletions

View File

@ -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);

View File

@ -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"

View File

@ -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

View File

@ -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 \

View File

@ -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];
} }

View File

@ -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);

View File

@ -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)

View File

@ -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();

View File

@ -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

View File

@ -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,50 +205,60 @@ 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 /* static */ PlatformData*
vm_tid_(gettid()), Sampler::AllocPlatformData(int aThreadId)
#endif
signal_sender_launched_(false)
#ifdef XP_MACOSX
, signal_receiver_(pthread_self())
#endif
{ {
return new PlatformData;
} }
void SignalSender() { /* static */ void
while (sampler_->IsActive()) { Sampler::FreePlatformData(PlatformData* aData)
sampler_->HandleSaveRequest(); {
delete aData;
}
if (!sampler_->IsPaused()) { static void* SignalSender(void* arg) {
#ifdef XP_MACOSX # if defined(ANDROID)
pthread_kill(signal_receiver_, SIGPROF); // pthread_atfork isn't available on Android.
void* initialize_atfork = NULL;
# else # else
// This call is done just once, at the first call to SenderEntry.
// It returns NULL.
static void* initialize_atfork = setup_atfork();
# endif
std::vector<ThreadInfo*> threads = GetRegisteredThreads(); int vm_tgid_ = getpid();
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++) { for (uint32_t i = 0; i < threads.size(); i++) {
ThreadInfo* info = threads[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 // We use sCurrentThreadProfile the ThreadProfile for the
// thread we're profiling to the signal handler // thread we're profiling to the signal handler
sCurrentThreadProfile = info->Profile(); sCurrentThreadProfile = info->Profile();
int threadId = info->ThreadId(); int threadId = info->ThreadId();
if (threadId == 0) {
threadId = vm_tid_;
}
if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) { if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) {
printf_stderr("profiler failed to signal tid=%d\n", threadId); printf_stderr("profiler failed to signal tid=%d\n", threadId);
@ -248,73 +272,44 @@ class Sampler::PlatformData : public Malloced {
while (sCurrentThreadProfile) while (sCurrentThreadProfile)
sched_yield(); sched_yield();
} }
#endif
} }
// Convert ms to us and subtract 100 us to compensate delays // Convert ms to us and subtract 100 us to compensate delays
// occuring during signal delivery. // occuring during signal delivery.
// TODO measure and confirm this. // TODO measure and confirm this.
const useconds_t interval = sampler_->interval_ * 1000 - 100; const useconds_t interval =
SamplerRegistry::sampler->interval() * 1000 - 100;
//int result = usleep(interval); //int result = usleep(interval);
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 void* SenderEntry(void* arg) {
# if defined(ANDROID)
// pthread_atfork isn't available on Android.
void* initialize_atfork = NULL;
# else
// This call is done just once, at the first call to SenderEntry.
// It returns NULL.
static void* initialize_atfork = setup_atfork();
# endif
Sampler::PlatformData* data =
reinterpret_cast<Sampler::PlatformData*>(arg);
data->SignalSender();
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();

View File

@ -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()) {
{
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()) if (!SamplerRegistry::sampler->IsPaused())
SampleContext(SamplerRegistry::sampler); 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;
}
}
} }

View File

@ -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;
}
}
} }

View File

@ -31,6 +31,7 @@ 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];

View File

@ -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_ */