| // Copyright 2015 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/profiler/win32_stack_frame_unwinder.h" | 
 |  | 
 | #include <windows.h> | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/macros.h" | 
 | #include "base/memory/ptr_util.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | // Win32UnwindFunctions ------------------------------------------------------- | 
 |  | 
 | const HMODULE ModuleHandleTraits::kNonNullModuleForTesting = | 
 |     reinterpret_cast<HMODULE>(static_cast<uintptr_t>(-1)); | 
 |  | 
 | // static | 
 | bool ModuleHandleTraits::CloseHandle(HMODULE handle) { | 
 |   if (handle == kNonNullModuleForTesting) | 
 |     return true; | 
 |  | 
 |   return ::FreeLibrary(handle) != 0; | 
 | } | 
 |  | 
 | // static | 
 | bool ModuleHandleTraits::IsHandleValid(HMODULE handle) { | 
 |   return handle != nullptr; | 
 | } | 
 |  | 
 | // static | 
 | HMODULE ModuleHandleTraits::NullHandle() { | 
 |   return nullptr; | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | // Implements the UnwindFunctions interface for the corresponding Win32 | 
 | // functions. | 
 | class Win32UnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions { | 
 | public: | 
 |   Win32UnwindFunctions(); | 
 |   ~Win32UnwindFunctions() override; | 
 |  | 
 |   PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter, | 
 |                                         PDWORD64 image_base) override; | 
 |  | 
 |   void VirtualUnwind(DWORD64 image_base, | 
 |                      DWORD64 program_counter, | 
 |                      PRUNTIME_FUNCTION runtime_function, | 
 |                      CONTEXT* context) override; | 
 |  | 
 |   ScopedModuleHandle GetModuleForProgramCounter( | 
 |       DWORD64 program_counter) override; | 
 |  | 
 | private: | 
 |   DISALLOW_COPY_AND_ASSIGN(Win32UnwindFunctions); | 
 | }; | 
 |  | 
 | Win32UnwindFunctions::Win32UnwindFunctions() {} | 
 | Win32UnwindFunctions::~Win32UnwindFunctions() {} | 
 |  | 
 | PRUNTIME_FUNCTION Win32UnwindFunctions::LookupFunctionEntry( | 
 |     DWORD64 program_counter, | 
 |     PDWORD64 image_base) { | 
 | #ifdef _WIN64 | 
 |   return RtlLookupFunctionEntry(program_counter, image_base, nullptr); | 
 | #else | 
 |   NOTREACHED(); | 
 |   return nullptr; | 
 | #endif | 
 | } | 
 |  | 
 | void Win32UnwindFunctions::VirtualUnwind(DWORD64 image_base, | 
 |                                          DWORD64 program_counter, | 
 |                                          PRUNTIME_FUNCTION runtime_function, | 
 |                                          CONTEXT* context) { | 
 | #ifdef _WIN64 | 
 |   void* handler_data; | 
 |   ULONG64 establisher_frame; | 
 |   KNONVOLATILE_CONTEXT_POINTERS nvcontext = {}; | 
 |   RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, program_counter, | 
 |                    runtime_function, context, &handler_data, | 
 |                    &establisher_frame, &nvcontext); | 
 | #else | 
 |   NOTREACHED(); | 
 | #endif | 
 | } | 
 |  | 
 | ScopedModuleHandle Win32UnwindFunctions::GetModuleForProgramCounter( | 
 |     DWORD64 program_counter) { | 
 |   HMODULE module_handle = nullptr; | 
 |   // GetModuleHandleEx() increments the module reference count, which is then | 
 |   // managed and ultimately decremented by ScopedModuleHandle. | 
 |   if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | 
 |                            reinterpret_cast<LPCTSTR>(program_counter), | 
 |                            &module_handle)) { | 
 |     const DWORD error = ::GetLastError(); | 
 |     DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast<int>(error)); | 
 |   } | 
 |   return ScopedModuleHandle(module_handle); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // Win32StackFrameUnwinder ---------------------------------------------------- | 
 |  | 
 | Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {} | 
 | Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {} | 
 |  | 
 | Win32StackFrameUnwinder::Win32StackFrameUnwinder() | 
 |     : Win32StackFrameUnwinder(WrapUnique(new Win32UnwindFunctions)) {} | 
 |  | 
 | Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {} | 
 |  | 
 | bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context, | 
 |                                         ScopedModuleHandle* module) { | 
 | #ifdef _WIN64 | 
 |   ScopedModuleHandle frame_module = | 
 |       unwind_functions_->GetModuleForProgramCounter(context->Rip); | 
 |   if (!frame_module.IsValid()) { | 
 |     // There's no loaded module containing the instruction pointer. This can be | 
 |     // due to executing code that is not in a module. In particular, | 
 |     // runtime-generated code associated with third-party injected DLLs | 
 |     // typically is not in a module. It can also be due to the the module having | 
 |     // been unloaded since we recorded the stack.  In the latter case the | 
 |     // function unwind information was part of the unloaded module, so it's not | 
 |     // possible to unwind further. | 
 |     // | 
 |     // If a module was found, it's still theoretically possible for the detected | 
 |     // module module to be different than the one that was loaded when the stack | 
 |     // was copied (i.e. if the module was unloaded and a different module loaded | 
 |     // in overlapping memory). This likely would cause a crash, but has not been | 
 |     // observed in practice. | 
 |     return false; | 
 |   } | 
 |  | 
 |   ULONG64 image_base; | 
 |   // Try to look up unwind metadata for the current function. | 
 |   PRUNTIME_FUNCTION runtime_function = | 
 |       unwind_functions_->LookupFunctionEntry(context->Rip, &image_base); | 
 |  | 
 |   if (runtime_function) { | 
 |     unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function, | 
 |                                      context); | 
 |     at_top_frame_ = false; | 
 |   } else { | 
 |     if (at_top_frame_) { | 
 |       at_top_frame_ = false; | 
 |  | 
 |       // This is a leaf function (i.e. a function that neither calls a function, | 
 |       // nor allocates any stack space itself) so the return address is at RSP. | 
 |       context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp); | 
 |       context->Rsp += 8; | 
 |     } else { | 
 |       // In theory we shouldn't get here, as it means we've encountered a | 
 |       // function without unwind information below the top of the stack, which | 
 |       // is forbidden by the Microsoft x64 calling convention. | 
 |       // | 
 |       // The one known case in Chrome code that executes this path occurs | 
 |       // because of BoringSSL unwind information inconsistent with the actual | 
 |       // function code. See https://crbug.com/542919. | 
 |       // | 
 |       // Note that dodgy third-party generated code that otherwise would enter | 
 |       // this path should be caught by the module check above, since the code | 
 |       // typically is located outside of a module. | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   module->Set(frame_module.Take()); | 
 |   return true; | 
 | #else | 
 |   NOTREACHED(); | 
 |   return false; | 
 | #endif | 
 | } | 
 |  | 
 | Win32StackFrameUnwinder::Win32StackFrameUnwinder( | 
 |     std::unique_ptr<UnwindFunctions> unwind_functions) | 
 |     : at_top_frame_(true), unwind_functions_(std::move(unwind_functions)) {} | 
 |  | 
 | }  // namespace base |