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 +