tools/profiler/platform-macos.cc

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include <dlfcn.h>
     6 #include <unistd.h>
     7 #include <sys/mman.h>
     8 #include <mach/mach_init.h>
     9 #include <mach-o/dyld.h>
    10 #include <mach-o/getsect.h>
    12 #include <AvailabilityMacros.h>
    14 #include <pthread.h>
    15 #include <semaphore.h>
    16 #include <signal.h>
    17 #include <libkern/OSAtomic.h>
    18 #include <mach/mach.h>
    19 #include <mach/semaphore.h>
    20 #include <mach/task.h>
    21 #include <mach/vm_statistics.h>
    22 #include <sys/time.h>
    23 #include <sys/resource.h>
    24 #include <sys/types.h>
    25 #include <sys/sysctl.h>
    26 #include <stdarg.h>
    27 #include <stdlib.h>
    28 #include <string.h>
    29 #include <errno.h>
    30 #include <math.h>
    32 #include "nsThreadUtils.h"
    34 #include "platform.h"
    35 #include "TableTicker.h"
    36 #include "UnwinderThread2.h"  /* uwt__register_thread_for_profiling */
    38 // this port is based off of v8 svn revision 9837
    40 // XXX: this is a very stubbed out implementation
    41 // that only supports a single Sampler
    42 struct SamplerRegistry {
    43   static void AddActiveSampler(Sampler *sampler) {
    44     ASSERT(!SamplerRegistry::sampler);
    45     SamplerRegistry::sampler = sampler;
    46   }
    47   static void RemoveActiveSampler(Sampler *sampler) {
    48     SamplerRegistry::sampler = NULL;
    49   }
    50   static Sampler *sampler;
    51 };
    53 Sampler *SamplerRegistry::sampler = NULL;
    55 // 0 is never a valid thread id on MacOSX since a ptread_t is
    56 // a pointer.
    57 static const pthread_t kNoThread = (pthread_t) 0;
    59 void OS::Startup() {
    60 }
    62 void OS::Sleep(int milliseconds) {
    63   usleep(1000 * milliseconds);
    64 }
    66 void OS::SleepMicro(int microseconds) {
    67   usleep(microseconds);
    68 }
    70 Thread::Thread(const char* name)
    71     : stack_size_(0) {
    72   set_name(name);
    73 }
    76 Thread::~Thread() {
    77 }
    80 static void SetThreadName(const char* name) {
    81   // pthread_setname_np is only available in 10.6 or later, so test
    82   // for it at runtime.
    83   int (*dynamic_pthread_setname_np)(const char*);
    84   *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
    85     dlsym(RTLD_DEFAULT, "pthread_setname_np");
    86   if (!dynamic_pthread_setname_np)
    87     return;
    89   // Mac OS X does not expose the length limit of the name, so hardcode it.
    90   static const int kMaxNameLength = 63;
    91   USE(kMaxNameLength);
    92   ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
    93   dynamic_pthread_setname_np(name);
    94 }
    97 static void* ThreadEntry(void* arg) {
    98   Thread* thread = reinterpret_cast<Thread*>(arg);
   100   thread->thread_ = pthread_self();
   101   SetThreadName(thread->name());
   102   ASSERT(thread->thread_ != kNoThread);
   103   thread->Run();
   104   return NULL;
   105 }
   108 void Thread::set_name(const char* name) {
   109   strncpy(name_, name, sizeof(name_));
   110   name_[sizeof(name_) - 1] = '\0';
   111 }
   114 void Thread::Start() {
   115   pthread_attr_t* attr_ptr = NULL;
   116   pthread_attr_t attr;
   117   if (stack_size_ > 0) {
   118     pthread_attr_init(&attr);
   119     pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
   120     attr_ptr = &attr;
   121   }
   122   pthread_create(&thread_, attr_ptr, ThreadEntry, this);
   123   ASSERT(thread_ != kNoThread);
   124 }
   126 void Thread::Join() {
   127   pthread_join(thread_, NULL);
   128 }
   130 class PlatformData : public Malloced {
   131  public:
   132   PlatformData() : profiled_thread_(mach_thread_self())
   133   {
   134     profiled_pthread_ = pthread_from_mach_thread_np(profiled_thread_);
   135   }
   137   ~PlatformData() {
   138     // Deallocate Mach port for thread.
   139     mach_port_deallocate(mach_task_self(), profiled_thread_);
   140   }
   142   thread_act_t profiled_thread() { return profiled_thread_; }
   143   pthread_t profiled_pthread() { return profiled_pthread_; }
   145  private:
   146   // Note: for profiled_thread_ Mach primitives are used instead of PThread's
   147   // because the latter doesn't provide thread manipulation primitives required.
   148   // For details, consult "Mac OS X Internals" book, Section 7.3.
   149   thread_act_t profiled_thread_;
   150   // we also store the pthread because Mach threads have no concept of stack
   151   // and we want to be able to get the stack size when we need to unwind the
   152   // stack using frame pointers.
   153   pthread_t profiled_pthread_;
   154 };
   156 /* static */ PlatformData*
   157 Sampler::AllocPlatformData(int aThreadId)
   158 {
   159   return new PlatformData;
   160 }
   162 /* static */ void
   163 Sampler::FreePlatformData(PlatformData* aData)
   164 {
   165   delete aData;
   166 }
   168 class SamplerThread : public Thread {
   169  public:
   170   explicit SamplerThread(double interval)
   171       : Thread("SamplerThread")
   172       , intervalMicro_(floor(interval * 1000 + 0.5))
   173   {
   174     if (intervalMicro_ <= 0) {
   175       intervalMicro_ = 1;
   176     }
   177   }
   179   static void AddActiveSampler(Sampler* sampler) {
   180     mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   181     SamplerRegistry::AddActiveSampler(sampler);
   182     if (instance_ == NULL) {
   183       instance_ = new SamplerThread(sampler->interval());
   184       instance_->Start();
   185     }
   186   }
   188   static void RemoveActiveSampler(Sampler* sampler) {
   189     mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   190     instance_->Join();
   191     //XXX: unlike v8 we need to remove the active sampler after doing the Join
   192     // because we drop the sampler immediately
   193     SamplerRegistry::RemoveActiveSampler(sampler);
   194     delete instance_;
   195     instance_ = NULL;
   196   }
   198   // Implement Thread::Run().
   199   virtual void Run() {
   200     while (SamplerRegistry::sampler->IsActive()) {
   201       if (!SamplerRegistry::sampler->IsPaused()) {
   202         mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   203         std::vector<ThreadInfo*> threads =
   204           SamplerRegistry::sampler->GetRegisteredThreads();
   205         for (uint32_t i = 0; i < threads.size(); i++) {
   206           ThreadInfo* info = threads[i];
   208           // This will be null if we're not interested in profiling this thread.
   209           if (!info->Profile())
   210             continue;
   212           PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
   213           if (sleeping == PseudoStack::SLEEPING_AGAIN) {
   214             info->Profile()->DuplicateLastSample();
   215             //XXX: This causes flushes regardless of jank-only mode
   216             info->Profile()->flush();
   217             continue;
   218           }
   220           ThreadProfile* thread_profile = info->Profile();
   222           SampleContext(SamplerRegistry::sampler, thread_profile);
   223         }
   224       }
   225       OS::SleepMicro(intervalMicro_);
   226     }
   227   }
   229   void SampleContext(Sampler* sampler, ThreadProfile* thread_profile) {
   230     thread_act_t profiled_thread =
   231       thread_profile->GetPlatformData()->profiled_thread();
   233     TickSample sample_obj;
   234     TickSample* sample = &sample_obj;
   236     if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
   238 #if V8_HOST_ARCH_X64
   239     thread_state_flavor_t flavor = x86_THREAD_STATE64;
   240     x86_thread_state64_t state;
   241     mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
   242 #if __DARWIN_UNIX03
   243 #define REGISTER_FIELD(name) __r ## name
   244 #else
   245 #define REGISTER_FIELD(name) r ## name
   246 #endif  // __DARWIN_UNIX03
   247 #elif V8_HOST_ARCH_IA32
   248     thread_state_flavor_t flavor = i386_THREAD_STATE;
   249     i386_thread_state_t state;
   250     mach_msg_type_number_t count = i386_THREAD_STATE_COUNT;
   251 #if __DARWIN_UNIX03
   252 #define REGISTER_FIELD(name) __e ## name
   253 #else
   254 #define REGISTER_FIELD(name) e ## name
   255 #endif  // __DARWIN_UNIX03
   256 #else
   257 #error Unsupported Mac OS X host architecture.
   258 #endif  // V8_HOST_ARCH
   260     if (thread_get_state(profiled_thread,
   261                          flavor,
   262                          reinterpret_cast<natural_t*>(&state),
   263                          &count) == KERN_SUCCESS) {
   264       sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
   265       sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
   266       sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
   267       sample->timestamp = mozilla::TimeStamp::Now();
   268       sample->threadProfile = thread_profile;
   269       sampler->Tick(sample);
   270     }
   271     thread_resume(profiled_thread);
   272   }
   274   int intervalMicro_;
   275   //RuntimeProfilerRateLimiter rate_limiter_;
   277   static SamplerThread* instance_;
   279   DISALLOW_COPY_AND_ASSIGN(SamplerThread);
   280 };
   282 #undef REGISTER_FIELD
   284 SamplerThread* SamplerThread::instance_ = NULL;
   286 Sampler::Sampler(double interval, bool profiling, int entrySize)
   287     : // isolate_(isolate),
   288       interval_(interval),
   289       profiling_(profiling),
   290       paused_(false),
   291       active_(false),
   292       entrySize_(entrySize) /*,
   293       samples_taken_(0)*/ {
   294 }
   297 Sampler::~Sampler() {
   298   ASSERT(!IsActive());
   299 }
   302 void Sampler::Start() {
   303   ASSERT(!IsActive());
   304   SetActive(true);
   305   SamplerThread::AddActiveSampler(this);
   306 }
   309 void Sampler::Stop() {
   310   ASSERT(IsActive());
   311   SetActive(false);
   312   SamplerThread::RemoveActiveSampler(this);
   313 }
   315 pthread_t
   316 Sampler::GetProfiledThread(PlatformData* aData)
   317 {
   318   return aData->profiled_pthread();
   319 }
   321 #include <sys/syscall.h>
   322 pid_t gettid()
   323 {
   324   return (pid_t) syscall(SYS_thread_selfid);
   325 }
   327 /* static */ Thread::tid_t
   328 Thread::GetCurrentId()
   329 {
   330   return gettid();
   331 }
   333 bool Sampler::RegisterCurrentThread(const char* aName,
   334                                     PseudoStack* aPseudoStack,
   335                                     bool aIsMainThread, void* stackTop)
   336 {
   337   if (!Sampler::sRegisteredThreadsMutex)
   338     return false;
   341   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   343   int id = gettid();
   344   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
   345     ThreadInfo* info = sRegisteredThreads->at(i);
   346     if (info->ThreadId() == id) {
   347       // Thread already registered. This means the first unregister will be
   348       // too early.
   349       ASSERT(false);
   350       return false;
   351     }
   352   }
   354   set_tls_stack_top(stackTop);
   356   ThreadInfo* info = new ThreadInfo(aName, id,
   357     aIsMainThread, aPseudoStack, stackTop);
   359   if (sActiveSampler) {
   360     sActiveSampler->RegisterThread(info);
   361   }
   363   sRegisteredThreads->push_back(info);
   365   uwt__register_thread_for_profiling(stackTop);
   366   return true;
   367 }
   369 void Sampler::UnregisterCurrentThread()
   370 {
   371   if (!Sampler::sRegisteredThreadsMutex)
   372     return;
   374   tlsStackTop.set(nullptr);
   376   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   378   int id = gettid();
   380   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
   381     ThreadInfo* info = sRegisteredThreads->at(i);
   382     if (info->ThreadId() == id) {
   383       delete info;
   384       sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
   385       break;
   386     }
   387   }
   388 }
   390 void TickSample::PopulateContext(void* aContext)
   391 {
   392   // Note that this asm changes if PopulateContext's parameter list is altered
   393 #if defined(SPS_PLAT_amd64_darwin)
   394   asm (
   395       // Compute caller's %rsp by adding to %rbp:
   396       // 8 bytes for previous %rbp, 8 bytes for return address
   397       "leaq 0x10(%%rbp), %0\n\t"
   398       // Dereference %rbp to get previous %rbp
   399       "movq (%%rbp), %1\n\t"
   400       :
   401       "=r"(sp),
   402       "=r"(fp)
   403   );
   404 #elif defined(SPS_PLAT_x86_darwin)
   405   asm (
   406       // Compute caller's %esp by adding to %ebp:
   407       // 4 bytes for aContext + 4 bytes for return address +
   408       // 4 bytes for previous %ebp
   409       "leal 0xc(%%ebp), %0\n\t"
   410       // Dereference %ebp to get previous %ebp
   411       "movl (%%ebp), %1\n\t"
   412       :
   413       "=r"(sp),
   414       "=r"(fp)
   415   );
   416 #else
   417 # error "Unsupported architecture"
   418 #endif
   419   pc = reinterpret_cast<Address>(__builtin_extract_return_addr(
   420                                     __builtin_return_address(0)));
   421 }

mercurial