From 0d012fdc5741ba935966d77e1e944591df8e8e05 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Fri, 3 Feb 2012 15:19:18 -0500 Subject: [PATCH] Bug 724079. Add back the mac backend. r=ehsan This should improve latency and perhaps fix the crashes we're seeing in bug 721025. --- tools/profiler/Makefile.in | 6 +- tools/profiler/TableTicker.cpp | 21 ++- tools/profiler/platform-macos.cc | 309 +++++++++++++++++++++++++++++++ 3 files changed, 325 insertions(+), 11 deletions(-) create mode 100644 tools/profiler/platform-macos.cc diff --git a/tools/profiler/Makefile.in b/tools/profiler/Makefile.in index dce4e3179da..7def8ac581d 100644 --- a/tools/profiler/Makefile.in +++ b/tools/profiler/Makefile.in @@ -85,16 +85,12 @@ CPPSRCS += \ $(NULL) endif ifeq ($(OS_TARGET),Darwin) -# For now we use platform-linux.cc because we can't unwind -# another thread on mac using backtrace(), the implementation -# for platform-macosx.cc is in the hg history and should be -# used when we can stackwalk using a thread handle. DEFINES += -DMOZ_ENABLE_PROFILER_SPS CPPSRCS += \ shared-libraries-macos.cc \ - platform-linux.cc \ + platform-macos.cc \ TableTicker.cpp \ $(NULL) endif diff --git a/tools/profiler/TableTicker.cpp b/tools/profiler/TableTicker.cpp index 3950c232ad4..714ff7fb243 100644 --- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -45,9 +45,10 @@ #include "prenv.h" #include "shared-libraries.h" #include "mozilla/StringBuilder.h" +#include "mozilla/StackWalk.h" // we eventually want to make this runtime switchable -#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_UNIX)) +#if defined(MOZ_PROFILING) && (defined(XP_UNIX) && !defined(XP_MACOSX)) #ifndef ANDROID #define USE_BACKTRACE #endif @@ -56,7 +57,7 @@ #include #endif -#if defined(MOZ_PROFILING) && defined(XP_WIN) +#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN)) #define USE_NS_STACKWALK #endif #ifdef USE_NS_STACKWALK @@ -353,7 +354,7 @@ class TableTicker: public Sampler { private: // Not implemented on platforms which do not support backtracing - void doBacktrace(Profile &aProfile); + void doBacktrace(Profile &aProfile, Address pc); private: Profile mProfile; @@ -419,8 +420,9 @@ void TableTicker::HandleSaveRequest() NS_DispatchToMainThread(runnable); } + #ifdef USE_BACKTRACE -void TableTicker::doBacktrace(Profile &aProfile) +void TableTicker::doBacktrace(Profile &aProfile, Address pc) { void *array[100]; int count = backtrace (array, 100); @@ -434,6 +436,7 @@ void TableTicker::doBacktrace(Profile &aProfile) } #endif + #ifdef USE_NS_STACKWALK typedef struct { void** array; @@ -452,17 +455,23 @@ void StackWalkCallback(void* aPC, void* aClosure) array->array[array->count++] = aPC; } -void TableTicker::doBacktrace(Profile &aProfile) +void TableTicker::doBacktrace(Profile &aProfile, Address fp) { +#ifndef XP_MACOSX uintptr_t thread = GetThreadHandle(platform_data()); MOZ_ASSERT(thread); +#endif void* pc_array[1000]; PCArray array = { pc_array, mozilla::ArrayLength(pc_array), 0 }; +#ifdef XP_MACOSX + nsresult rv = FramePointerStackWalk(StackWalkCallback, 1, &array, reinterpret_cast(fp)); +#else nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread); +#endif if (NS_SUCCEEDED(rv)) { aProfile.addTag(ProfileEntry('s', "(root)", 0)); @@ -539,7 +548,7 @@ void TableTicker::Tick(TickSample* sample) #if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK) if (mUseStackWalk) { - doBacktrace(mProfile); + doBacktrace(mProfile, sample->fp); } else { doSampleStackTrace(mStack, mProfile, sample); } diff --git a/tools/profiler/platform-macos.cc b/tools/profiler/platform-macos.cc new file mode 100644 index 00000000000..03784a74cf7 --- /dev/null +++ b/tools/profiler/platform-macos.cc @@ -0,0 +1,309 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "platform.h" + +// this port is based off of v8 svn revision 9837 + +// XXX: this is a very stubbed out implementation +// that only supports a single Sampler +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; + +// 0 is never a valid thread id on MacOSX since a ptread_t is +// a pointer. +static const pthread_t kNoThread = (pthread_t) 0; + +class MacOSMutex : public Mutex { + public: + MacOSMutex() { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex_, &attr); + } + + virtual ~MacOSMutex() { pthread_mutex_destroy(&mutex_); } + + virtual int Lock() { return pthread_mutex_lock(&mutex_); } + virtual int Unlock() { return pthread_mutex_unlock(&mutex_); } + + virtual bool TryLock() { + int result = pthread_mutex_trylock(&mutex_); + // Return false if the lock is busy and locking failed. + if (result == EBUSY) { + return false; + } + ASSERT(result == 0); // Verify no other errors. + return true; + } + + private: + pthread_mutex_t mutex_; +}; + + +Mutex* OS::CreateMutex() { + return new MacOSMutex(); +} + +void OS::Sleep(int 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) + : data_(new PlatformData), + stack_size_(0) { + set_name(name); +} + + +Thread::~Thread() { + delete data_; +} + + +static void SetThreadName(const char* name) { + // pthread_setname_np is only available in 10.6 or later, so test + // for it at runtime. + int (*dynamic_pthread_setname_np)(const char*); + *reinterpret_cast(&dynamic_pthread_setname_np) = + dlsym(RTLD_DEFAULT, "pthread_setname_np"); + if (!dynamic_pthread_setname_np) + return; + + // Mac OS X does not expose the length limit of the name, so hardcode it. + static const int kMaxNameLength = 63; + USE(kMaxNameLength); + ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength); + dynamic_pthread_setname_np(name); +} + + +static void* ThreadEntry(void* arg) { + Thread* thread = reinterpret_cast(arg); + // This is also initialized by the first argument to pthread_create() but we + // don't know which thread will run first (the original thread or the new + // one) so we initialize it here too. + thread->data()->thread_ = pthread_self(); + SetThreadName(thread->name()); + ASSERT(thread->data()->thread_ != kNoThread); + thread->Run(); + return NULL; +} + + +void Thread::set_name(const char* name) { + strncpy(name_, name, sizeof(name_)); + name_[sizeof(name_) - 1] = '\0'; +} + + +void Thread::Start() { + pthread_attr_t* attr_ptr = NULL; + pthread_attr_t attr; + if (stack_size_ > 0) { + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, static_cast(stack_size_)); + attr_ptr = &attr; + } + pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this); + ASSERT(data_->thread_ != kNoThread); +} + +void Thread::Join() { + pthread_join(data_->thread_, NULL); +} + +class Sampler::PlatformData : public Malloced { + public: + PlatformData() : profiled_thread_(mach_thread_self()) {} + + ~PlatformData() { + // Deallocate Mach port for thread. + mach_port_deallocate(mach_task_self(), profiled_thread_); + } + + thread_act_t profiled_thread() { return profiled_thread_; } + + private: + // Note: for profiled_thread_ Mach primitives are used instead of PThread's + // because the latter doesn't provide thread manipulation primitives required. + // For details, consult "Mac OS X Internals" book, Section 7.3. + thread_act_t profiled_thread_; +}; + + +class SamplerThread : public Thread { + public: + explicit SamplerThread(int interval) + : Thread("SamplerThread"), + interval_(interval) {} + + static void AddActiveSampler(Sampler* sampler) { + ScopedLock lock(mutex_); + SamplerRegistry::AddActiveSampler(sampler); + if (instance_ == NULL) { + instance_ = new SamplerThread(sampler->interval()); + instance_->Start(); + } else { + ASSERT(instance_->interval_ == sampler->interval()); + } + } + + static void RemoveActiveSampler(Sampler* sampler) { + ScopedLock lock(mutex_); + instance_->Join(); + //XXX: unlike v8 we need to remove the active sampler after doing the Join + // because we drop the sampler immediately + SamplerRegistry::RemoveActiveSampler(sampler); + delete instance_; + instance_ = NULL; + /* + if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) { + RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_); + delete instance_; + instance_ = NULL; + } + */ + } + + // Implement Thread::Run(). + virtual void Run() { + while (SamplerRegistry::sampler->IsActive()) { + SampleContext(SamplerRegistry::sampler); + OS::Sleep(interval_); + } + } + + void SampleContext(Sampler* sampler) { + thread_act_t profiled_thread = sampler->platform_data()->profiled_thread(); + TickSample 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 V8_HOST_ARCH_X64 + thread_state_flavor_t flavor = x86_THREAD_STATE64; + x86_thread_state64_t state; + mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; +#if __DARWIN_UNIX03 +#define REGISTER_FIELD(name) __r ## name +#else +#define REGISTER_FIELD(name) r ## name +#endif // __DARWIN_UNIX03 +#elif V8_HOST_ARCH_IA32 + thread_state_flavor_t flavor = i386_THREAD_STATE; + i386_thread_state_t state; + mach_msg_type_number_t count = i386_THREAD_STATE_COUNT; +#if __DARWIN_UNIX03 +#define REGISTER_FIELD(name) __e ## name +#else +#define REGISTER_FIELD(name) e ## name +#endif // __DARWIN_UNIX03 +#else +#error Unsupported Mac OS X host architecture. +#endif // V8_HOST_ARCH + + if (thread_get_state(profiled_thread, + flavor, + reinterpret_cast(&state), + &count) == KERN_SUCCESS) { + //sample->state = sampler->isolate()->current_vm_state(); + sample->pc = reinterpret_cast
(state.REGISTER_FIELD(ip)); + sample->sp = reinterpret_cast
(state.REGISTER_FIELD(sp)); + sample->fp = reinterpret_cast
(state.REGISTER_FIELD(bp)); + sample->timestamp = mozilla::TimeStamp::Now(); + sampler->SampleStack(sample); + sampler->Tick(sample); + } + thread_resume(profiled_thread); + } + + const int interval_; + //RuntimeProfilerRateLimiter rate_limiter_; + + // Protects the process wide state below. + static Mutex* mutex_; + static SamplerThread* instance_; + + DISALLOW_COPY_AND_ASSIGN(SamplerThread); +}; + +#undef REGISTER_FIELD + + +Mutex* SamplerThread::mutex_ = OS::CreateMutex(); +SamplerThread* SamplerThread::instance_ = NULL; + + +Sampler::Sampler(int interval, bool profiling) + : // isolate_(isolate), + interval_(interval), + profiling_(profiling), + active_(false) /*, + samples_taken_(0)*/ { + data_ = new PlatformData; +} + + +Sampler::~Sampler() { + ASSERT(!IsActive()); + delete data_; +} + + +void Sampler::Start() { + ASSERT(!IsActive()); + SetActive(true); + SamplerThread::AddActiveSampler(this); +} + + +void Sampler::Stop() { + ASSERT(IsActive()); + SetActive(false); + SamplerThread::RemoveActiveSampler(this); +}