tools/profiler/platform-win32.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/profiler/platform-win32.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,365 @@
     1.4 +// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
     1.5 +//
     1.6 +// Redistribution and use in source and binary forms, with or without
     1.7 +// modification, are permitted provided that the following conditions
     1.8 +// are met:
     1.9 +//  * Redistributions of source code must retain the above copyright
    1.10 +//    notice, this list of conditions and the following disclaimer.
    1.11 +//  * Redistributions in binary form must reproduce the above copyright
    1.12 +//    notice, this list of conditions and the following disclaimer in
    1.13 +//    the documentation and/or other materials provided with the
    1.14 +//    distribution.
    1.15 +//  * Neither the name of Google, Inc. nor the names of its contributors
    1.16 +//    may be used to endorse or promote products derived from this
    1.17 +//    software without specific prior written permission.
    1.18 +// 
    1.19 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    1.20 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    1.21 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    1.22 +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    1.23 +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    1.24 +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    1.25 +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
    1.26 +// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
    1.27 +// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    1.28 +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    1.29 +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    1.30 +// SUCH DAMAGE.
    1.31 +
    1.32 +#include <windows.h>
    1.33 +#include <mmsystem.h>
    1.34 +#include <process.h>
    1.35 +#include "platform.h"
    1.36 +#include "TableTicker.h"
    1.37 +#include "ProfileEntry.h"
    1.38 +#include "UnwinderThread2.h"
    1.39 +
    1.40 +class PlatformData : public Malloced {
    1.41 + public:
    1.42 +  // Get a handle to the calling thread. This is the thread that we are
    1.43 +  // going to profile. We need to make a copy of the handle because we are
    1.44 +  // going to use it in the sampler thread. Using GetThreadHandle() will
    1.45 +  // not work in this case. We're using OpenThread because DuplicateHandle
    1.46 +  // for some reason doesn't work in Chrome's sandbox.
    1.47 +  PlatformData(int aThreadId) : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
    1.48 +                                               THREAD_SUSPEND_RESUME |
    1.49 +                                               THREAD_QUERY_INFORMATION,
    1.50 +                                               false,
    1.51 +                                               aThreadId)) {}
    1.52 +
    1.53 +  ~PlatformData() {
    1.54 +    if (profiled_thread_ != NULL) {
    1.55 +      CloseHandle(profiled_thread_);
    1.56 +      profiled_thread_ = NULL;
    1.57 +    }
    1.58 +  }
    1.59 +
    1.60 +  HANDLE profiled_thread() { return profiled_thread_; }
    1.61 +
    1.62 + private:
    1.63 +  HANDLE profiled_thread_;
    1.64 +};
    1.65 +
    1.66 +/* static */ PlatformData*
    1.67 +Sampler::AllocPlatformData(int aThreadId)
    1.68 +{
    1.69 +  return new PlatformData(aThreadId);
    1.70 +}
    1.71 +
    1.72 +/* static */ void
    1.73 +Sampler::FreePlatformData(PlatformData* aData)
    1.74 +{
    1.75 +  delete aData;
    1.76 +}
    1.77 +
    1.78 +uintptr_t
    1.79 +Sampler::GetThreadHandle(PlatformData* aData)
    1.80 +{
    1.81 +  return (uintptr_t) aData->profiled_thread();
    1.82 +}
    1.83 +
    1.84 +class SamplerThread : public Thread {
    1.85 + public:
    1.86 +  SamplerThread(double interval, Sampler* sampler)
    1.87 +      : Thread("SamplerThread")
    1.88 +      , interval_(interval)
    1.89 +      , sampler_(sampler)
    1.90 +  {
    1.91 +    interval_ = floor(interval + 0.5);
    1.92 +    if (interval_ <= 0) {
    1.93 +      interval_ = 1;
    1.94 +    }
    1.95 +  }
    1.96 +
    1.97 +  static void StartSampler(Sampler* sampler) {
    1.98 +    if (instance_ == NULL) {
    1.99 +      instance_ = new SamplerThread(sampler->interval(), sampler);
   1.100 +      instance_->Start();
   1.101 +    } else {
   1.102 +      ASSERT(instance_->interval_ == sampler->interval());
   1.103 +    }
   1.104 +  } 
   1.105 +
   1.106 +  static void StopSampler() {
   1.107 +    instance_->Join();
   1.108 +    delete instance_;
   1.109 +    instance_ = NULL;
   1.110 +  }
   1.111 +
   1.112 +  // Implement Thread::Run().
   1.113 +  virtual void Run() {
   1.114 +
   1.115 +    // By default we'll not adjust the timer resolution which tends to be around
   1.116 +    // 16ms. However, if the requested interval is sufficiently low we'll try to
   1.117 +    // adjust the resolution to match.
   1.118 +    if (interval_ < 10)
   1.119 +        ::timeBeginPeriod(interval_);
   1.120 +
   1.121 +    while (sampler_->IsActive()) {
   1.122 +      if (!sampler_->IsPaused()) {
   1.123 +        mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   1.124 +        std::vector<ThreadInfo*> threads =
   1.125 +          sampler_->GetRegisteredThreads();
   1.126 +        for (uint32_t i = 0; i < threads.size(); i++) {
   1.127 +          ThreadInfo* info = threads[i];
   1.128 +
   1.129 +          // This will be null if we're not interested in profiling this thread.
   1.130 +          if (!info->Profile())
   1.131 +            continue;
   1.132 +
   1.133 +          PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
   1.134 +          if (sleeping == PseudoStack::SLEEPING_AGAIN) {
   1.135 +            info->Profile()->DuplicateLastSample();
   1.136 +            //XXX: This causes flushes regardless of jank-only mode
   1.137 +            info->Profile()->flush();
   1.138 +            continue;
   1.139 +          }
   1.140 +
   1.141 +          ThreadProfile* thread_profile = info->Profile();
   1.142 +
   1.143 +          SampleContext(sampler_, thread_profile);
   1.144 +        }
   1.145 +      }
   1.146 +      OS::Sleep(interval_);
   1.147 +    }
   1.148 +
   1.149 +    // disable any timer resolution changes we've made
   1.150 +    if (interval_ < 10)
   1.151 +        ::timeEndPeriod(interval_);
   1.152 +  }
   1.153 +
   1.154 +  void SampleContext(Sampler* sampler, ThreadProfile* thread_profile) {
   1.155 +    uintptr_t thread = Sampler::GetThreadHandle(
   1.156 +                               thread_profile->GetPlatformData());
   1.157 +    HANDLE profiled_thread = reinterpret_cast<HANDLE>(thread);
   1.158 +    if (profiled_thread == NULL)
   1.159 +      return;
   1.160 +
   1.161 +    // Context used for sampling the register state of the profiled thread.
   1.162 +    CONTEXT context;
   1.163 +    memset(&context, 0, sizeof(context));
   1.164 +
   1.165 +    TickSample sample_obj;
   1.166 +    TickSample* sample = &sample_obj;
   1.167 +
   1.168 +    // Grab the timestamp before pausing the thread, to avoid deadlocks.
   1.169 +    sample->timestamp = mozilla::TimeStamp::Now();
   1.170 +    sample->threadProfile = thread_profile;
   1.171 +
   1.172 +    static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
   1.173 +    if (SuspendThread(profiled_thread) == kSuspendFailed)
   1.174 +      return;
   1.175 +
   1.176 +    context.ContextFlags = CONTEXT_CONTROL;
   1.177 +    if (GetThreadContext(profiled_thread, &context) != 0) {
   1.178 +#if V8_HOST_ARCH_X64
   1.179 +      sample->pc = reinterpret_cast<Address>(context.Rip);
   1.180 +      sample->sp = reinterpret_cast<Address>(context.Rsp);
   1.181 +      sample->fp = reinterpret_cast<Address>(context.Rbp);
   1.182 +#else
   1.183 +      sample->pc = reinterpret_cast<Address>(context.Eip);
   1.184 +      sample->sp = reinterpret_cast<Address>(context.Esp);
   1.185 +      sample->fp = reinterpret_cast<Address>(context.Ebp);
   1.186 +#endif
   1.187 +      sample->context = &context;
   1.188 +      sampler->Tick(sample);
   1.189 +    }
   1.190 +    ResumeThread(profiled_thread);
   1.191 +  }
   1.192 +
   1.193 +  Sampler* sampler_;
   1.194 +  int interval_; // units: ms
   1.195 +
   1.196 +  // Protects the process wide state below.
   1.197 +  static SamplerThread* instance_;
   1.198 +
   1.199 +  DISALLOW_COPY_AND_ASSIGN(SamplerThread);
   1.200 +};
   1.201 +
   1.202 +SamplerThread* SamplerThread::instance_ = NULL;
   1.203 +
   1.204 +
   1.205 +Sampler::Sampler(double interval, bool profiling, int entrySize)
   1.206 +    : interval_(interval),
   1.207 +      profiling_(profiling),
   1.208 +      paused_(false),
   1.209 +      active_(false),
   1.210 +      entrySize_(entrySize) {
   1.211 +}
   1.212 +
   1.213 +Sampler::~Sampler() {
   1.214 +  ASSERT(!IsActive());
   1.215 +}
   1.216 +
   1.217 +void Sampler::Start() {
   1.218 +  ASSERT(!IsActive());
   1.219 +  SetActive(true);
   1.220 +  SamplerThread::StartSampler(this);
   1.221 +}
   1.222 +
   1.223 +void Sampler::Stop() {
   1.224 +  ASSERT(IsActive());
   1.225 +  SetActive(false);
   1.226 +  SamplerThread::StopSampler();
   1.227 +}
   1.228 +
   1.229 +
   1.230 +static const HANDLE kNoThread = INVALID_HANDLE_VALUE;
   1.231 +
   1.232 +static unsigned int __stdcall ThreadEntry(void* arg) {
   1.233 +  Thread* thread = reinterpret_cast<Thread*>(arg);
   1.234 +  thread->Run();
   1.235 +  return 0;
   1.236 +}
   1.237 +
   1.238 +// Initialize a Win32 thread object. The thread has an invalid thread
   1.239 +// handle until it is started.
   1.240 +Thread::Thread(const char* name)
   1.241 +    : stack_size_(0) {
   1.242 +  thread_ = kNoThread;
   1.243 +  set_name(name);
   1.244 +}
   1.245 +
   1.246 +void Thread::set_name(const char* name) {
   1.247 +  strncpy(name_, name, sizeof(name_));
   1.248 +  name_[sizeof(name_) - 1] = '\0';
   1.249 +}
   1.250 +
   1.251 +// Close our own handle for the thread.
   1.252 +Thread::~Thread() {
   1.253 +  if (thread_ != kNoThread) CloseHandle(thread_);
   1.254 +}
   1.255 +
   1.256 +// Create a new thread. It is important to use _beginthreadex() instead of
   1.257 +// the Win32 function CreateThread(), because the CreateThread() does not
   1.258 +// initialize thread specific structures in the C runtime library.
   1.259 +void Thread::Start() {
   1.260 +  thread_ = reinterpret_cast<HANDLE>(
   1.261 +      _beginthreadex(NULL,
   1.262 +                     static_cast<unsigned>(stack_size_),
   1.263 +                     ThreadEntry,
   1.264 +                     this,
   1.265 +                     0,
   1.266 +                     (unsigned int*) &thread_id_));
   1.267 +}
   1.268 +
   1.269 +// Wait for thread to terminate.
   1.270 +void Thread::Join() {
   1.271 +  if (thread_id_ != GetCurrentId()) {
   1.272 +    WaitForSingleObject(thread_, INFINITE);
   1.273 +  }
   1.274 +}
   1.275 +
   1.276 +/* static */ Thread::tid_t
   1.277 +Thread::GetCurrentId()
   1.278 +{
   1.279 +  return GetCurrentThreadId();
   1.280 +}
   1.281 +
   1.282 +void OS::Startup() {
   1.283 +}
   1.284 +
   1.285 +void OS::Sleep(int milliseconds) {
   1.286 +  ::Sleep(milliseconds);
   1.287 +}
   1.288 +
   1.289 +bool Sampler::RegisterCurrentThread(const char* aName,
   1.290 +                                    PseudoStack* aPseudoStack,
   1.291 +                                    bool aIsMainThread, void* stackTop)
   1.292 +{
   1.293 +  if (!Sampler::sRegisteredThreadsMutex)
   1.294 +    return false;
   1.295 +
   1.296 +
   1.297 +  mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   1.298 +
   1.299 +  int id = GetCurrentThreadId();
   1.300 +
   1.301 +  for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
   1.302 +    ThreadInfo* info = sRegisteredThreads->at(i);
   1.303 +    if (info->ThreadId() == id) {
   1.304 +      // Thread already registered. This means the first unregister will be
   1.305 +      // too early.
   1.306 +      ASSERT(false);
   1.307 +      return false;
   1.308 +    }
   1.309 +  }
   1.310 +
   1.311 +  set_tls_stack_top(stackTop);
   1.312 +
   1.313 +  ThreadInfo* info = new ThreadInfo(aName, id,
   1.314 +    aIsMainThread, aPseudoStack, stackTop);
   1.315 +
   1.316 +  if (sActiveSampler) {
   1.317 +    sActiveSampler->RegisterThread(info);
   1.318 +  }
   1.319 +
   1.320 +  sRegisteredThreads->push_back(info);
   1.321 +
   1.322 +  uwt__register_thread_for_profiling(stackTop);
   1.323 +  return true;
   1.324 +}
   1.325 +
   1.326 +void Sampler::UnregisterCurrentThread()
   1.327 +{
   1.328 +  if (!Sampler::sRegisteredThreadsMutex)
   1.329 +    return;
   1.330 +
   1.331 +  tlsStackTop.set(nullptr);
   1.332 +
   1.333 +  mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   1.334 +
   1.335 +  int id = GetCurrentThreadId();
   1.336 +
   1.337 +  for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
   1.338 +    ThreadInfo* info = sRegisteredThreads->at(i);
   1.339 +    if (info->ThreadId() == id) {
   1.340 +      delete info;
   1.341 +      sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
   1.342 +      break;
   1.343 +    }
   1.344 +  }
   1.345 +}
   1.346 +
   1.347 +void TickSample::PopulateContext(void* aContext)
   1.348 +{
   1.349 +  MOZ_ASSERT(aContext);
   1.350 +  CONTEXT* pContext = reinterpret_cast<CONTEXT*>(aContext);
   1.351 +  context = pContext;
   1.352 +  RtlCaptureContext(pContext);
   1.353 +
   1.354 +#if defined(SPS_PLAT_amd64_windows)
   1.355 +
   1.356 +  pc = reinterpret_cast<Address>(pContext->Rip);
   1.357 +  sp = reinterpret_cast<Address>(pContext->Rsp);
   1.358 +  fp = reinterpret_cast<Address>(pContext->Rbp);
   1.359 +
   1.360 +#elif defined(SPS_PLAT_x86_windows)
   1.361 +
   1.362 +  pc = reinterpret_cast<Address>(pContext->Eip);
   1.363 +  sp = reinterpret_cast<Address>(pContext->Esp);
   1.364 +  fp = reinterpret_cast<Address>(pContext->Ebp);
   1.365 +
   1.366 +#endif
   1.367 +}
   1.368 +

mercurial