Bug 717059 - Profiler: add 'stackwalk' optional feature for Mac/Linux on profiling builds. r=ehsan
authorBenoit Girard <b56girard@gmail.com>
Tue, 10 Jan 2012 18:02:00 -0500
changeset 85602 ac78d7a4e8197cd8bc433fd8241e8ef3f5e3dbbf
parent 85601 d44e07a5db122adc171faa6fba4fc216807d24e7
child 85603 07cfb8019fcd4779754e9b6182c5ed53d915723c
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs717059
milestone12.0a1
Bug 717059 - Profiler: add 'stackwalk' optional feature for Mac/Linux on profiling builds. r=ehsan
tools/profiler/Makefile.in
tools/profiler/sps/TableTicker.cpp
tools/profiler/sps/platform-linux.cc
tools/profiler/sps/platform-macos.cc
--- a/tools/profiler/Makefile.in
+++ b/tools/profiler/Makefile.in
@@ -84,22 +84,26 @@ 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-macos.cc \
+  platform-linux.cc \
   TableTicker.cpp \
   $(NULL)
 endif
 
 ifeq ($(OS_TARGET),WINNT)
 
 DEFINES += -DMOZ_ENABLE_PROFILER_SPS
 
--- a/tools/profiler/sps/TableTicker.cpp
+++ b/tools/profiler/sps/TableTicker.cpp
@@ -42,17 +42,19 @@
 #include "platform.h"
 #include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "shared-libraries.h"
 #include "mozilla/StringBuilder.h"
 
 // we eventually want to make this runtime switchable
-//#define USE_BACKTRACE
+#if defined(XP_MACOSX) || defined(XP_UNIX)
+#define USE_BACKTRACE
+#endif
 #ifdef USE_BACKTRACE
 #include <execinfo.h>
 #endif
 
 using std::string;
 using namespace mozilla;
 
 #ifdef XP_WIN
@@ -117,42 +119,46 @@ public:
   { }
 
   ProfileEntry(char aTagName, double aTagFloat)
     : mTagFloat(aTagFloat)
     , mLeafAddress(0)
     , mTagName(aTagName)
   { }
 
+  ProfileEntry(char aTagName, uintptr_t aTagOffset)
+    : mTagOffset(aTagOffset)
+    , mLeafAddress(0)
+    , mTagName(aTagName)
+  { }
+
   string TagToString(Profile *profile);
 
 private:
   union {
     const char* mTagData;
     double mTagFloat;
     Address mTagAddress;
+    uintptr_t mTagOffset;
   };
   Address mLeafAddress;
   char mTagName;
 };
 
 #define PROFILE_MAX_ENTRY 100000
 class Profile
 {
 public:
   Profile(int aEntrySize)
     : mWritePos(0)
     , mReadPos(0)
     , mEntrySize(aEntrySize)
   {
     mEntries = new ProfileEntry[mEntrySize];
-    mNeedsSharedLibraryInfo = false;
-#if defined(ENABLE_SPS_LEAF_DATA) || defined(USE_BACKTRACE)
     mNeedsSharedLibraryInfo = true;
-#endif
   }
 
   ~Profile()
   {
     delete[] mEntries;
   }
 
   void addTag(ProfileEntry aTag)
@@ -230,16 +236,17 @@ class TableTicker: public Sampler {
  public:
   TableTicker(int aInterval, int aEntrySize, Stack *aStack,
               const char** aFeatures, uint32_t aFeatureCount)
     : Sampler(aInterval, true)
     , mProfile(aEntrySize)
     , mStack(aStack)
     , mSaveRequested(false)
   {
+    mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
     mProfile.addTag(ProfileEntry('m', "Start"));
   }
 
   ~TableTicker() { if (IsActive()) Stop(); }
 
   virtual void SampleStack(TickSample* sample) {}
 
   // Called within a signal. This function must be reentrant
@@ -261,16 +268,17 @@ class TableTicker: public Sampler {
   Profile* GetProfile()
   {
     return &mProfile;
   }
  private:
   Profile mProfile;
   Stack *mStack;
   bool mSaveRequested;
+  bool mUseStackWalk;
 };
 
 /**
  * This is an event used to save the profile on the main thread
  * to be sure that it is not being modified while saving.
  */
 class SaveProfileTask : public nsRunnable {
 public:
@@ -326,31 +334,22 @@ void TableTicker::HandleSaveRequest()
 
 #ifdef USE_BACKTRACE
 static
 void doBacktrace(Profile &aProfile)
 {
   void *array[100];
   int count = backtrace (array, 100);
 
-  bool isSignal = true;
-#ifndef __i386__
-  // the test doesn't work for 64bit
-  isSignal = false;
-#endif
-  for (int i = count-1; i >= 0; i--) {
-    if( isSignal ) {
-      if( (intptr_t)array[i] == -1 ) { // signal frames have addresses of -1?
-        isSignal = false;
-      }
-      continue;
-    }
+  aProfile.addTag(ProfileEntry('s', "XRE_Main", 0));
+
+  for (int i = 0; i < count; i++) {
+    if( (intptr_t)array[i] == -1 ) break;
     aProfile.addTag(ProfileEntry('l', (const char*)array[i]));
   }
-  aProfile.addTag(ProfileEntry('s', "XRE_Main", 0));
 }
 #endif
 
 static
 void doSampleStackTrace(Stack *aStack, Profile &aProfile, TickSample *sample)
 {
   // Sample
   // 's' tag denotes the start of a sample block
@@ -375,17 +374,21 @@ void TableTicker::Tick(TickSample* sampl
   const char *marker = mStack->getMarker(i++);
   for (int i = 0; marker != NULL; i++) {
     mProfile.addTag(ProfileEntry('m', marker));
     marker = mStack->getMarker(i++);
   }
   mStack->mQueueClearMarker = true;
 
 #ifdef USE_BACKTRACE
-  doBacktrace(mProfile);
+  if (mUseStackWalk) {
+    doBacktrace(mProfile);
+  } else {
+    doSampleStackTrace(mStack, mProfile, sample);
+  }
 #else
   doSampleStackTrace(mStack, mProfile, sample);
 #endif
 
   if (!sLastTracerEvent.IsNull() && sample) {
     TimeDuration delta = sample->timestamp - sLastTracerEvent;
     mProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
   }
@@ -404,18 +407,20 @@ string ProfileEntry::TagToString(Profile
     SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo();
     Address pc = mTagAddress;
     // TODO Use binary sort (STL)
     for (size_t i = 0; i < shlibInfo.GetSize(); i++) {
       SharedLibrary &e = shlibInfo.GetEntry(i);
       if (pc > (Address)e.GetStart() && pc < (Address)e.GetEnd()) {
         if (e.GetName()) {
           found = true;
+
           snprintf(tagBuff, 1024, "l-%s@%p\n", e.GetName(), pc - e.GetStart());
           tag += string(tagBuff);
+
           break;
         }
       }
     }
     if (!found) {
       snprintf(tagBuff, 1024, "l-???@%p\n", pc);
       tag += string(tagBuff);
     }
@@ -513,17 +518,22 @@ char* mozilla_sampler_get_profile()
 
   char *rtn = (char*)malloc( (profile.Length()+1) * sizeof(char) );
   strcpy(rtn, profile.Buffer());
   return rtn;
 }
 
 const char** mozilla_sampler_get_features()
 {
-  static const char* features[] = {""};
+  static const char* features[] = {
+#ifdef MOZ_PROFILING && USE_BACKTRACE
+    "stackwalk",
+#endif
+    NULL
+  };
 
   return features;
 }
 
 // Values are only honored on the first start
 void mozilla_sampler_start(int aProfileEntries, int aInterval,
                            const char** aFeatures, uint32_t aFeatureCount)
 {
--- a/tools/profiler/sps/platform-linux.cc
+++ b/tools/profiler/sps/platform-linux.cc
@@ -88,22 +88,21 @@ static void ProfilerSaveSignalHandler(in
 #define SYS_tgkill __NR_tgkill
 #else
 #define V8_HOST_ARCH_X64 1
 #endif
 static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
   if (!sActiveSampler)
     return;
 
-#ifndef ENABLE_SPS_LEAF_DATA
-  TickSample* sample = NULL;
-#else
   TickSample sample_obj;
   TickSample* sample = &sample_obj;
+  sample->pc = 0;
 
+#ifdef ENABLE_SPS_LEAF_DATA
   // If profiling, we extract the current pc and sp.
   if (sActiveSampler->IsProfiling()) {
     // Extracting the sample from the context is extremely machine dependent.
     ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
     mcontext_t& mcontext = ucontext->uc_mcontext;
 #if V8_HOST_ARCH_IA32
     sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
     sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
@@ -122,19 +121,20 @@ static void ProfilerSignalHandler(int si
     sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
     sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
     sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
 #endif
 #elif V8_HOST_ARCH_MIPS
     // Implement this on MIPS.
     UNIMPLEMENTED();
 #endif
-    sample->timestamp = mozilla::TimeStamp::Now();
   }
 #endif
+  sample->timestamp = mozilla::TimeStamp::Now();
+
   sActiveSampler->Tick(sample);
 }
 
 #ifndef XP_MACOSX
 void tgkill(pid_t tgid, pid_t tid, int signalno) {
   syscall(SYS_tgkill, tgid, tid, signalno);
 }
 #endif
deleted file mode 100644
--- a/tools/profiler/sps/platform-macos.cc
+++ /dev/null
@@ -1,306 +0,0 @@
-#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) {
-  }
-  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_);
-    SamplerRegistry::RemoveActiveSampler(sampler);
-    instance_->Join();
-    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);
-}