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 84377 ac78d7a4e8197cd8bc433fd8241e8ef3f5e3dbbf
parent 84376 d44e07a5db122adc171faa6fba4fc216807d24e7
child 84378 07cfb8019fcd4779754e9b6182c5ed53d915723c
push id21842
push usermak77@bonardo.net
push dateFri, 13 Jan 2012 08:56:37 +0000
treeherdermozilla-central@8d4638feec54 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs717059
milestone12.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 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);
-}