Bug 724079. Add back the mac backend. r=ehsan
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Fri, 03 Feb 2012 15:19:18 -0500
changeset 89959 deb7adeda4ebfac83b77f62a11e00373be600b31
parent 89958 c5574e55a558aa4279536fd8fb78e522a5131658
child 89960 8a820e4ee7f68f753938baf259af97a0f6902f3f
push id136
push userlsblakk@mozilla.com
push dateFri, 01 Jun 2012 02:39:32 +0000
treeherdermozilla-release@7ebf7352c959 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs724079, 721025
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
tools/profiler/TableTicker.cpp
tools/profiler/platform-macos.cc
--- a/tools/profiler/Makefile.in
+++ b/tools/profiler/Makefile.in
@@ -80,26 +80,22 @@ DEFINES += -DMOZ_ENABLE_PROFILER_SPS
 
 CPPSRCS += \
   shared-libraries-linux.cc \
   platform-linux.cc \
   TableTicker.cpp \
   $(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
 
 ifeq ($(OS_TARGET),WINNT)
 
 DEFINES += -DMOZ_ENABLE_PROFILER_SPS
 
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -40,28 +40,29 @@
 #include <stdio.h>
 #include "sps_sampler.h"
 #include "platform.h"
 #include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 #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
 #endif
 #ifdef USE_BACKTRACE
  #include <execinfo.h>
 #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
  #include "nsStackWalk.h"
 #endif
 
 using std::string;
 using namespace mozilla;
@@ -348,17 +349,17 @@ class TableTicker: public Sampler {
 
   Profile* GetProfile()
   {
     return &mProfile;
   }
 
 private:
   // Not implemented on platforms which do not support backtracing
-  void doBacktrace(Profile &aProfile);
+  void doBacktrace(Profile &aProfile, Address pc);
 
 private:
   Profile mProfile;
   Stack *mStack;
   bool mSaveRequested;
   bool mUseStackWalk;
   bool mJankOnly;
 };
@@ -414,31 +415,33 @@ void TableTicker::HandleSaveRequest()
   mSaveRequested = false;
 
   // TODO: Use use the ipc/chromium Tasks here to support processes
   // without XPCOM.
   nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
   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);
 
   aProfile.addTag(ProfileEntry('s', "(root)", 0));
 
   for (int i = 0; i < count; i++) {
     if( (intptr_t)array[i] == -1 ) break;
     aProfile.addTag(ProfileEntry('l', (const char*)array[i]));
   }
 }
 #endif
 
+
 #ifdef USE_NS_STACKWALK
 typedef struct {
   void** array;
   size_t size;
   size_t count;
 } PCArray;
 
 static
@@ -447,27 +450,33 @@ void StackWalkCallback(void* aPC, void* 
   PCArray* array = static_cast<PCArray*>(aClosure);
   if (array->count >= array->size) {
     // too many frames, ignore
     return;
   }
   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<void**>(fp));
+#else
   nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
+#endif
   if (NS_SUCCEEDED(rv)) {
     aProfile.addTag(ProfileEntry('s', "(root)", 0));
 
     for (size_t i = array.count; i > 0; --i) {
       aProfile.addTag(ProfileEntry('l', (const char*)array.array[i - 1]));
     }
   }
 }
@@ -534,17 +543,17 @@ void TableTicker::Tick(TickSample* sampl
       if (delta.ToMilliseconds() > 100.0) {
           recordSample = true;
       }
     }
   }
 
 #if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK)
   if (mUseStackWalk) {
-    doBacktrace(mProfile);
+    doBacktrace(mProfile, sample->fp);
   } else {
     doSampleStackTrace(mStack, mProfile, sample);
   }
 #else
   doSampleStackTrace(mStack, mProfile, sample);
 #endif
 
   if (recordSample)
new file mode 100644
--- /dev/null
+++ b/tools/profiler/platform-macos.cc
@@ -0,0 +1,309 @@
+#include <dlfcn.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <mach/mach_init.h>
+#include <mach-o/dyld.h>
+#include <mach-o/getsect.h>
+
+#include <AvailabilityMacros.h>
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <libkern/OSAtomic.h>
+#include <mach/mach.h>
+#include <mach/semaphore.h>
+#include <mach/task.h>
+#include <mach/vm_statistics.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+
+#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<void**>(&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<Thread*>(arg);
+  // This is also initialized by the first argument to pthread_create() but we
+  // don't know which thread will run first (the original thread or the new
+  // one) so we initialize it here too.
+  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<size_t>(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<natural_t*>(&state),
+                         &count) == KERN_SUCCESS) {
+      //sample->state = sampler->isolate()->current_vm_state();
+      sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
+      sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
+      sample->fp = reinterpret_cast<Address>(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);
+}