tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

platform-win32.cpp (10853B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions
      7 // are met:
      8 //  * Redistributions of source code must retain the above copyright
      9 //    notice, this list of conditions and the following disclaimer.
     10 //  * Redistributions in binary form must reproduce the above copyright
     11 //    notice, this list of conditions and the following disclaimer in
     12 //    the documentation and/or other materials provided with the
     13 //    distribution.
     14 //  * Neither the name of Google, Inc. nor the names of its contributors
     15 //    may be used to endorse or promote products derived from this
     16 //    software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     21 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     22 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     23 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     24 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     25 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     26 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     27 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     28 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29 // SUCH DAMAGE.
     30 
     31 #include <windows.h>
     32 #include <mmsystem.h>
     33 #include <process.h>
     34 
     35 namespace mozilla {
     36 namespace baseprofiler {
     37 
     38 static int64_t MicrosecondsSince1970() {
     39  int64_t prt;
     40  FILETIME ft;
     41  SYSTEMTIME st;
     42 
     43  GetSystemTime(&st);
     44  SystemTimeToFileTime(&st, &ft);
     45  static_assert(sizeof(ft) == sizeof(prt), "Expect FILETIME to be 64 bits");
     46  memcpy(&prt, &ft, sizeof(prt));
     47  const int64_t epochBias = 116444736000000000LL;
     48  prt = (prt - epochBias) / 10;
     49 
     50  return prt;
     51 }
     52 
     53 void* GetStackTop(void* aGuess) {
     54  PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
     55  return reinterpret_cast<void*>(pTib->StackBase);
     56 }
     57 
     58 static void PopulateRegsFromContext(Registers& aRegs, CONTEXT* aContext) {
     59 #if defined(GP_ARCH_amd64)
     60  aRegs.mPC = reinterpret_cast<Address>(aContext->Rip);
     61  aRegs.mSP = reinterpret_cast<Address>(aContext->Rsp);
     62  aRegs.mFP = reinterpret_cast<Address>(aContext->Rbp);
     63  aRegs.mR10 = reinterpret_cast<Address>(aContext->R10);
     64  aRegs.mR12 = reinterpret_cast<Address>(aContext->R12);
     65 #elif defined(GP_ARCH_x86)
     66  aRegs.mPC = reinterpret_cast<Address>(aContext->Eip);
     67  aRegs.mSP = reinterpret_cast<Address>(aContext->Esp);
     68  aRegs.mFP = reinterpret_cast<Address>(aContext->Ebp);
     69  aRegs.mEcx = reinterpret_cast<Address>(aContext->Ecx);
     70  aRegs.mEdx = reinterpret_cast<Address>(aContext->Edx);
     71 #elif defined(GP_ARCH_arm64)
     72  aRegs.mPC = reinterpret_cast<Address>(aContext->Pc);
     73  aRegs.mSP = reinterpret_cast<Address>(aContext->Sp);
     74  aRegs.mFP = reinterpret_cast<Address>(aContext->Fp);
     75  aRegs.mLR = reinterpret_cast<Address>(aContext->Lr);
     76  aRegs.mR11 = reinterpret_cast<Address>(aContext->X11);
     77 #else
     78 #  error "bad arch"
     79 #endif
     80 }
     81 
     82 // Gets a real (i.e. not pseudo) handle for the current thread, with the
     83 // permissions needed for profiling.
     84 // @return a real HANDLE for the current thread.
     85 static HANDLE GetRealCurrentThreadHandleForProfiling() {
     86  HANDLE realCurrentThreadHandle;
     87  if (!::DuplicateHandle(
     88          ::GetCurrentProcess(), ::GetCurrentThread(), ::GetCurrentProcess(),
     89          &realCurrentThreadHandle,
     90          THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
     91          FALSE, 0)) {
     92    return nullptr;
     93  }
     94 
     95  return realCurrentThreadHandle;
     96 }
     97 
     98 class PlatformData {
     99 public:
    100  // Get a handle to the calling thread. This is the thread that we are
    101  // going to profile. We need a real handle because we are going to use it in
    102  // the sampler thread.
    103  explicit PlatformData(BaseProfilerThreadId aThreadId)
    104      : mProfiledThread(GetRealCurrentThreadHandleForProfiling()) {
    105    MOZ_ASSERT(DWORD(aThreadId.ToNumber()) == ::GetCurrentThreadId());
    106  }
    107 
    108  ~PlatformData() {
    109    if (mProfiledThread != nullptr) {
    110      CloseHandle(mProfiledThread);
    111      mProfiledThread = nullptr;
    112    }
    113  }
    114 
    115  HANDLE ProfiledThread() { return mProfiledThread; }
    116 
    117 private:
    118  HANDLE mProfiledThread;
    119 };
    120 
    121 #if defined(USE_MOZ_STACK_WALK)
    122 HANDLE
    123 GetThreadHandle(PlatformData* aData) { return aData->ProfiledThread(); }
    124 #endif
    125 
    126 static const HANDLE kNoThread = INVALID_HANDLE_VALUE;
    127 
    128 ////////////////////////////////////////////////////////////////////////
    129 // BEGIN Sampler target specifics
    130 
    131 Sampler::Sampler(PSLockRef aLock) {}
    132 
    133 void Sampler::Disable(PSLockRef aLock) {}
    134 
    135 template <typename Func>
    136 void Sampler::SuspendAndSampleAndResumeThread(
    137    PSLockRef aLock, const RegisteredThread& aRegisteredThread,
    138    const TimeStamp& aNow, const Func& aProcessRegs) {
    139  HANDLE profiled_thread =
    140      aRegisteredThread.GetPlatformData()->ProfiledThread();
    141  if (profiled_thread == nullptr) {
    142    return;
    143  }
    144 
    145  // Context used for sampling the register state of the profiled thread.
    146  CONTEXT context;
    147  memset(&context, 0, sizeof(context));
    148 
    149  //----------------------------------------------------------------//
    150  // Suspend the samplee thread and get its context.
    151 
    152  static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
    153  if (SuspendThread(profiled_thread) == kSuspendFailed) {
    154    return;
    155  }
    156 
    157  // SuspendThread is asynchronous, so the thread may still be running.
    158  // Call GetThreadContext first to ensure the thread is really suspended.
    159  // See https://blogs.msdn.microsoft.com/oldnewthing/20150205-00/?p=44743.
    160 
    161  // Using only CONTEXT_CONTROL is faster but on 64-bit it causes crashes in
    162  // RtlVirtualUnwind (see bug 1120126) so we set all the flags.
    163 #if defined(GP_ARCH_amd64)
    164  context.ContextFlags = CONTEXT_FULL;
    165 #else
    166  context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
    167 #endif
    168  if (!GetThreadContext(profiled_thread, &context)) {
    169    ResumeThread(profiled_thread);
    170    return;
    171  }
    172 
    173  //----------------------------------------------------------------//
    174  // Sample the target thread.
    175 
    176  // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
    177  //
    178  // The profiler's "critical section" begins here.  We must be very careful
    179  // what we do here, or risk deadlock.  See the corresponding comment in
    180  // platform-linux-android.cpp for details.
    181 
    182  Registers regs;
    183  PopulateRegsFromContext(regs, &context);
    184  aProcessRegs(regs, aNow);
    185 
    186  //----------------------------------------------------------------//
    187  // Resume the target thread.
    188 
    189  ResumeThread(profiled_thread);
    190 
    191  // The profiler's critical section ends here.
    192  //
    193  // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
    194 }
    195 
    196 // END Sampler target specifics
    197 ////////////////////////////////////////////////////////////////////////
    198 
    199 ////////////////////////////////////////////////////////////////////////
    200 // BEGIN SamplerThread target specifics
    201 
    202 static unsigned int __stdcall ThreadEntry(void* aArg) {
    203  auto thread = static_cast<SamplerThread*>(aArg);
    204  thread->Run();
    205  return 0;
    206 }
    207 
    208 SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration,
    209                             double aIntervalMilliseconds, uint32_t aFeatures)
    210    : mSampler(aLock),
    211      mActivityGeneration(aActivityGeneration),
    212      mIntervalMicroseconds(
    213          std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))),
    214      mNoTimerResolutionChange(
    215          ProfilerFeature::HasNoTimerResolutionChange(aFeatures)) {
    216  if ((!mNoTimerResolutionChange) && (mIntervalMicroseconds < 10 * 1000)) {
    217    // By default the timer resolution (which tends to be 1/64Hz, around 16ms)
    218    // is not changed. However, if the requested interval is sufficiently low,
    219    // the resolution will be adjusted to match. Note that this affects all
    220    // timers in Firefox, and could therefore hide issues while profiling. This
    221    // change may be prevented with the "notimerresolutionchange" feature.
    222    ::timeBeginPeriod(mIntervalMicroseconds / 1000);
    223  }
    224 
    225  // Create a new thread. It is important to use _beginthreadex() instead of
    226  // the Win32 function CreateThread(), because the CreateThread() does not
    227  // initialize thread-specific structures in the C runtime library.
    228  mThread = reinterpret_cast<HANDLE>(_beginthreadex(nullptr,
    229                                                    /* stack_size */ 0,
    230                                                    ThreadEntry, this,
    231                                                    /* initflag */ 0, nullptr));
    232  if (mThread == 0) {
    233    MOZ_CRASH("_beginthreadex failed");
    234  }
    235 }
    236 
    237 SamplerThread::~SamplerThread() {
    238  WaitForSingleObject(mThread, INFINITE);
    239 
    240  // Close our own handle for the thread.
    241  if (mThread != kNoThread) {
    242    CloseHandle(mThread);
    243  }
    244 }
    245 
    246 void SamplerThread::SleepMicro(uint32_t aMicroseconds) {
    247  // For now, keep the old behaviour of minimum Sleep(1), even for
    248  // smaller-than-usual sleeps after an overshoot, unless the user has
    249  // explicitly opted into a sub-millisecond profiler interval.
    250  if (mIntervalMicroseconds >= 1000) {
    251    ::Sleep(std::max(1u, aMicroseconds / 1000));
    252  } else {
    253    TimeStamp start = TimeStamp::Now();
    254    TimeStamp end = start + TimeDuration::FromMicroseconds(aMicroseconds);
    255 
    256    // First, sleep for as many whole milliseconds as possible.
    257    if (aMicroseconds >= 1000) {
    258      ::Sleep(aMicroseconds / 1000);
    259    }
    260 
    261    // Then, spin until enough time has passed.
    262    while (TimeStamp::Now() < end) {
    263      YieldProcessor();
    264    }
    265  }
    266 }
    267 
    268 void SamplerThread::Stop(PSLockRef aLock) {
    269  if ((!mNoTimerResolutionChange) && (mIntervalMicroseconds < 10 * 1000)) {
    270    // Disable any timer resolution changes we've made. Do it now while
    271    // gPSMutex is locked, i.e. before any other SamplerThread can be created
    272    // and call ::timeBeginPeriod().
    273    //
    274    // It's safe to do this now even though this SamplerThread is still alive,
    275    // because the next time the main loop of Run() iterates it won't get past
    276    // the mActivityGeneration check, and so it won't make any more ::Sleep()
    277    // calls.
    278    ::timeEndPeriod(mIntervalMicroseconds / 1000);
    279  }
    280 
    281  mSampler.Disable(aLock);
    282 }
    283 
    284 // END SamplerThread target specifics
    285 ////////////////////////////////////////////////////////////////////////
    286 
    287 static void PlatformInit(PSLockRef aLock) {}
    288 
    289 #if defined(HAVE_NATIVE_UNWIND)
    290 #  define REGISTERS_SYNC_POPULATE(regs) \
    291    CONTEXT context;                    \
    292    RtlCaptureContext(&context);        \
    293    PopulateRegsFromContext(regs, &context);
    294 #endif
    295 
    296 }  // namespace baseprofiler
    297 }  // namespace mozilla