|  | // 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 |