tools/profiler/platform-win32.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.

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

mercurial