tools/profiler/platform-win32.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
     2 //
     3 // Redistribution and use in source and binary forms, with or without
     4 // modification, are permitted provided that the following conditions
     5 // are met:
     6 //  * Redistributions of source code must retain the above copyright
     7 //    notice, this list of conditions and the following disclaimer.
     8 //  * Redistributions in binary form must reproduce the above copyright
     9 //    notice, this list of conditions and the following disclaimer in
    10 //    the documentation and/or other materials provided with the
    11 //    distribution.
    12 //  * Neither the name of Google, Inc. nor the names of its contributors
    13 //    may be used to endorse or promote products derived from this
    14 //    software without specific prior written permission.
    15 // 
    16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    19 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    20 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    21 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    22 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
    23 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
    24 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    26 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    27 // SUCH DAMAGE.
    29 #include <windows.h>
    30 #include <mmsystem.h>
    31 #include <process.h>
    32 #include "platform.h"
    33 #include "TableTicker.h"
    34 #include "ProfileEntry.h"
    35 #include "UnwinderThread2.h"
    37 class PlatformData : public Malloced {
    38  public:
    39   // Get a handle to the calling thread. This is the thread that we are
    40   // going to profile. We need to make a copy of the handle because we are
    41   // going to use it in the sampler thread. Using GetThreadHandle() will
    42   // not work in this case. We're using OpenThread because DuplicateHandle
    43   // for some reason doesn't work in Chrome's sandbox.
    44   PlatformData(int aThreadId) : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
    45                                                THREAD_SUSPEND_RESUME |
    46                                                THREAD_QUERY_INFORMATION,
    47                                                false,
    48                                                aThreadId)) {}
    50   ~PlatformData() {
    51     if (profiled_thread_ != NULL) {
    52       CloseHandle(profiled_thread_);
    53       profiled_thread_ = NULL;
    54     }
    55   }
    57   HANDLE profiled_thread() { return profiled_thread_; }
    59  private:
    60   HANDLE profiled_thread_;
    61 };
    63 /* static */ PlatformData*
    64 Sampler::AllocPlatformData(int aThreadId)
    65 {
    66   return new PlatformData(aThreadId);
    67 }
    69 /* static */ void
    70 Sampler::FreePlatformData(PlatformData* aData)
    71 {
    72   delete aData;
    73 }
    75 uintptr_t
    76 Sampler::GetThreadHandle(PlatformData* aData)
    77 {
    78   return (uintptr_t) aData->profiled_thread();
    79 }
    81 class SamplerThread : public Thread {
    82  public:
    83   SamplerThread(double interval, Sampler* sampler)
    84       : Thread("SamplerThread")
    85       , interval_(interval)
    86       , sampler_(sampler)
    87   {
    88     interval_ = floor(interval + 0.5);
    89     if (interval_ <= 0) {
    90       interval_ = 1;
    91     }
    92   }
    94   static void StartSampler(Sampler* sampler) {
    95     if (instance_ == NULL) {
    96       instance_ = new SamplerThread(sampler->interval(), sampler);
    97       instance_->Start();
    98     } else {
    99       ASSERT(instance_->interval_ == sampler->interval());
   100     }
   101   } 
   103   static void StopSampler() {
   104     instance_->Join();
   105     delete instance_;
   106     instance_ = NULL;
   107   }
   109   // Implement Thread::Run().
   110   virtual void Run() {
   112     // By default we'll not adjust the timer resolution which tends to be around
   113     // 16ms. However, if the requested interval is sufficiently low we'll try to
   114     // adjust the resolution to match.
   115     if (interval_ < 10)
   116         ::timeBeginPeriod(interval_);
   118     while (sampler_->IsActive()) {
   119       if (!sampler_->IsPaused()) {
   120         mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   121         std::vector<ThreadInfo*> threads =
   122           sampler_->GetRegisteredThreads();
   123         for (uint32_t i = 0; i < threads.size(); i++) {
   124           ThreadInfo* info = threads[i];
   126           // This will be null if we're not interested in profiling this thread.
   127           if (!info->Profile())
   128             continue;
   130           PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
   131           if (sleeping == PseudoStack::SLEEPING_AGAIN) {
   132             info->Profile()->DuplicateLastSample();
   133             //XXX: This causes flushes regardless of jank-only mode
   134             info->Profile()->flush();
   135             continue;
   136           }
   138           ThreadProfile* thread_profile = info->Profile();
   140           SampleContext(sampler_, thread_profile);
   141         }
   142       }
   143       OS::Sleep(interval_);
   144     }
   146     // disable any timer resolution changes we've made
   147     if (interval_ < 10)
   148         ::timeEndPeriod(interval_);
   149   }
   151   void SampleContext(Sampler* sampler, ThreadProfile* thread_profile) {
   152     uintptr_t thread = Sampler::GetThreadHandle(
   153                                thread_profile->GetPlatformData());
   154     HANDLE profiled_thread = reinterpret_cast<HANDLE>(thread);
   155     if (profiled_thread == NULL)
   156       return;
   158     // Context used for sampling the register state of the profiled thread.
   159     CONTEXT context;
   160     memset(&context, 0, sizeof(context));
   162     TickSample sample_obj;
   163     TickSample* sample = &sample_obj;
   165     // Grab the timestamp before pausing the thread, to avoid deadlocks.
   166     sample->timestamp = mozilla::TimeStamp::Now();
   167     sample->threadProfile = thread_profile;
   169     static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
   170     if (SuspendThread(profiled_thread) == kSuspendFailed)
   171       return;
   173     context.ContextFlags = CONTEXT_CONTROL;
   174     if (GetThreadContext(profiled_thread, &context) != 0) {
   175 #if V8_HOST_ARCH_X64
   176       sample->pc = reinterpret_cast<Address>(context.Rip);
   177       sample->sp = reinterpret_cast<Address>(context.Rsp);
   178       sample->fp = reinterpret_cast<Address>(context.Rbp);
   179 #else
   180       sample->pc = reinterpret_cast<Address>(context.Eip);
   181       sample->sp = reinterpret_cast<Address>(context.Esp);
   182       sample->fp = reinterpret_cast<Address>(context.Ebp);
   183 #endif
   184       sample->context = &context;
   185       sampler->Tick(sample);
   186     }
   187     ResumeThread(profiled_thread);
   188   }
   190   Sampler* sampler_;
   191   int interval_; // units: ms
   193   // Protects the process wide state below.
   194   static SamplerThread* instance_;
   196   DISALLOW_COPY_AND_ASSIGN(SamplerThread);
   197 };
   199 SamplerThread* SamplerThread::instance_ = NULL;
   202 Sampler::Sampler(double interval, bool profiling, int entrySize)
   203     : interval_(interval),
   204       profiling_(profiling),
   205       paused_(false),
   206       active_(false),
   207       entrySize_(entrySize) {
   208 }
   210 Sampler::~Sampler() {
   211   ASSERT(!IsActive());
   212 }
   214 void Sampler::Start() {
   215   ASSERT(!IsActive());
   216   SetActive(true);
   217   SamplerThread::StartSampler(this);
   218 }
   220 void Sampler::Stop() {
   221   ASSERT(IsActive());
   222   SetActive(false);
   223   SamplerThread::StopSampler();
   224 }
   227 static const HANDLE kNoThread = INVALID_HANDLE_VALUE;
   229 static unsigned int __stdcall ThreadEntry(void* arg) {
   230   Thread* thread = reinterpret_cast<Thread*>(arg);
   231   thread->Run();
   232   return 0;
   233 }
   235 // Initialize a Win32 thread object. The thread has an invalid thread
   236 // handle until it is started.
   237 Thread::Thread(const char* name)
   238     : stack_size_(0) {
   239   thread_ = kNoThread;
   240   set_name(name);
   241 }
   243 void Thread::set_name(const char* name) {
   244   strncpy(name_, name, sizeof(name_));
   245   name_[sizeof(name_) - 1] = '\0';
   246 }
   248 // Close our own handle for the thread.
   249 Thread::~Thread() {
   250   if (thread_ != kNoThread) CloseHandle(thread_);
   251 }
   253 // Create a new thread. It is important to use _beginthreadex() instead of
   254 // the Win32 function CreateThread(), because the CreateThread() does not
   255 // initialize thread specific structures in the C runtime library.
   256 void Thread::Start() {
   257   thread_ = reinterpret_cast<HANDLE>(
   258       _beginthreadex(NULL,
   259                      static_cast<unsigned>(stack_size_),
   260                      ThreadEntry,
   261                      this,
   262                      0,
   263                      (unsigned int*) &thread_id_));
   264 }
   266 // Wait for thread to terminate.
   267 void Thread::Join() {
   268   if (thread_id_ != GetCurrentId()) {
   269     WaitForSingleObject(thread_, INFINITE);
   270   }
   271 }
   273 /* static */ Thread::tid_t
   274 Thread::GetCurrentId()
   275 {
   276   return GetCurrentThreadId();
   277 }
   279 void OS::Startup() {
   280 }
   282 void OS::Sleep(int milliseconds) {
   283   ::Sleep(milliseconds);
   284 }
   286 bool Sampler::RegisterCurrentThread(const char* aName,
   287                                     PseudoStack* aPseudoStack,
   288                                     bool aIsMainThread, void* stackTop)
   289 {
   290   if (!Sampler::sRegisteredThreadsMutex)
   291     return false;
   294   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   296   int id = GetCurrentThreadId();
   298   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
   299     ThreadInfo* info = sRegisteredThreads->at(i);
   300     if (info->ThreadId() == id) {
   301       // Thread already registered. This means the first unregister will be
   302       // too early.
   303       ASSERT(false);
   304       return false;
   305     }
   306   }
   308   set_tls_stack_top(stackTop);
   310   ThreadInfo* info = new ThreadInfo(aName, id,
   311     aIsMainThread, aPseudoStack, stackTop);
   313   if (sActiveSampler) {
   314     sActiveSampler->RegisterThread(info);
   315   }
   317   sRegisteredThreads->push_back(info);
   319   uwt__register_thread_for_profiling(stackTop);
   320   return true;
   321 }
   323 void Sampler::UnregisterCurrentThread()
   324 {
   325   if (!Sampler::sRegisteredThreadsMutex)
   326     return;
   328   tlsStackTop.set(nullptr);
   330   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   332   int id = GetCurrentThreadId();
   334   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
   335     ThreadInfo* info = sRegisteredThreads->at(i);
   336     if (info->ThreadId() == id) {
   337       delete info;
   338       sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
   339       break;
   340     }
   341   }
   342 }
   344 void TickSample::PopulateContext(void* aContext)
   345 {
   346   MOZ_ASSERT(aContext);
   347   CONTEXT* pContext = reinterpret_cast<CONTEXT*>(aContext);
   348   context = pContext;
   349   RtlCaptureContext(pContext);
   351 #if defined(SPS_PLAT_amd64_windows)
   353   pc = reinterpret_cast<Address>(pContext->Rip);
   354   sp = reinterpret_cast<Address>(pContext->Rsp);
   355   fp = reinterpret_cast<Address>(pContext->Rbp);
   357 #elif defined(SPS_PLAT_x86_windows)
   359   pc = reinterpret_cast<Address>(pContext->Eip);
   360   sp = reinterpret_cast<Address>(pContext->Esp);
   361   fp = reinterpret_cast<Address>(pContext->Ebp);
   363 #endif
   364 }

mercurial