diff options
Diffstat (limited to 'base/profiler/stack_sampling_profiler_win.cc')
-rw-r--r-- | base/profiler/stack_sampling_profiler_win.cc | 357 |
1 files changed, 0 insertions, 357 deletions
diff --git a/base/profiler/stack_sampling_profiler_win.cc b/base/profiler/stack_sampling_profiler_win.cc deleted file mode 100644 index 1ccd13412a..0000000000 --- a/base/profiler/stack_sampling_profiler_win.cc +++ /dev/null @@ -1,357 +0,0 @@ -// 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 <objbase.h> -#include <windows.h> - -#include <map> -#include <utility> - -#include "base/logging.h" -#include "base/profiler/native_stack_sampler.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/time/time.h" -#include "base/win/pe_image.h" -#include "base/win/scoped_handle.h" - -namespace base { - -namespace { - -// Walks the stack represented by |context| from the current frame downwards, -// recording the instruction pointers for each frame in |instruction_pointers|. -int RecordStack(CONTEXT* context, - int max_stack_size, - const void* instruction_pointers[], - bool* last_frame_is_unknown_function) { -#ifdef _WIN64 - *last_frame_is_unknown_function = false; - - int i = 0; - for (; (i < max_stack_size) && context->Rip; ++i) { - // Try to look up unwind metadata for the current function. - ULONG64 image_base; - PRUNTIME_FUNCTION runtime_function = - RtlLookupFunctionEntry(context->Rip, &image_base, nullptr); - - instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip); - - if (runtime_function) { - KNONVOLATILE_CONTEXT_POINTERS nvcontext = {0}; - void* handler_data; - ULONG64 establisher_frame; - RtlVirtualUnwind(0, image_base, context->Rip, runtime_function, context, - &handler_data, &establisher_frame, &nvcontext); - } else { - // If we don't have a RUNTIME_FUNCTION, then in theory this should be a - // leaf function whose frame contains only a return address, at - // RSP. However, crash data also indicates that some third party libraries - // do not provide RUNTIME_FUNCTION information for non-leaf functions. We - // could manually unwind the stack in the former case, but attempting to - // do so in the latter case would produce wrong results and likely crash, - // so just bail out. - // - // Ad hoc runs with instrumentation show that ~5% of stack traces end with - // a valid leaf function. To avoid selectively omitting these traces it - // makes sense to ultimately try to distinguish these two cases and - // selectively unwind the stack for legitimate leaf functions. For the - // purposes of avoiding crashes though, just ignore them all for now. - return i; - } - } - return i; -#else - return 0; -#endif -} - -// Fills in |module_handles| corresponding to the pointers to code in -// |addresses|. The module handles are returned with reference counts -// incremented and should be freed with FreeModuleHandles. See note in -// SuspendThreadAndRecordStack for why |addresses| and |module_handles| are -// arrays. -void FindModuleHandlesForAddresses(const void* const addresses[], - HMODULE module_handles[], int stack_depth, - bool last_frame_is_unknown_function) { - const int module_frames = - last_frame_is_unknown_function ? stack_depth - 1 : stack_depth; - for (int i = 0; i < module_frames; ++i) { - HMODULE module_handle = NULL; - if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - reinterpret_cast<LPCTSTR>(addresses[i]), - &module_handle)) { - // HMODULE actually represents the base address of the module, so we can - // use it directly as an address. - DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]); - module_handles[i] = module_handle; - } - } -} - -// Frees the modules handles returned by FindModuleHandlesForAddresses. See note -// in SuspendThreadAndRecordStack for why |module_handles| is an array. -void FreeModuleHandles(int stack_depth, HMODULE module_handles[]) { - for (int i = 0; i < stack_depth; ++i) { - if (module_handles[i]) - ::FreeLibrary(module_handles[i]); - } -} - -// Gets the unique build ID for a module. Windows build IDs are created by a -// concatenation of a GUID and AGE fields found in the headers of a module. The -// GUID is stored in the first 16 bytes and the AGE is stored in the last 4 -// bytes. Returns the empty string if the function fails to get the build ID. -// -// Example: -// dumpbin chrome.exe /headers | find "Format:" -// ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ... -// -// The resulting buildID string of this instance of chrome.exe is -// "16B2A4281DED442E9A36FCE8CBD2972610". -// -// Note that the AGE field is encoded in decimal, not hex. -std::string GetBuildIDForModule(HMODULE module_handle) { - GUID guid; - DWORD age; - win::PEImage(module_handle).GetDebugId(&guid, &age); - const int kGUIDSize = 39; - std::wstring build_id; - int result = - ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize); - if (result != kGUIDSize) - return std::string(); - RemoveChars(build_id, L"{}-", &build_id); - build_id += StringPrintf(L"%d", age); - return WideToUTF8(build_id); -} - -// Disables priority boost on a thread for the lifetime of the object. -class ScopedDisablePriorityBoost { - public: - ScopedDisablePriorityBoost(HANDLE thread_handle); - ~ScopedDisablePriorityBoost(); - - private: - HANDLE thread_handle_; - BOOL got_previous_boost_state_; - BOOL boost_state_was_disabled_; - - DISALLOW_COPY_AND_ASSIGN(ScopedDisablePriorityBoost); -}; - -ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle) - : thread_handle_(thread_handle), - got_previous_boost_state_(false), - boost_state_was_disabled_(false) { - got_previous_boost_state_ = - ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_); - if (got_previous_boost_state_) { - // Confusingly, TRUE disables priority boost. - ::SetThreadPriorityBoost(thread_handle_, TRUE); - } -} - -ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() { - if (got_previous_boost_state_) - ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_); -} - -// Suspends the thread with |thread_handle|, records the stack into -// |instruction_pointers|, then resumes the thread. Returns the size of the -// stack. -// -// IMPORTANT NOTE: No heap allocations may occur between SuspendThread and -// ResumeThread. Otherwise this code can deadlock on heap locks acquired by the -// target thread before it was suspended. This is why we pass instruction -// pointers and module handles as preallocated arrays rather than vectors, since -// vectors make it too easy to subtly allocate memory. -int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size, - const void* instruction_pointers[], - bool* last_frame_is_unknown_function) { - if (::SuspendThread(thread_handle) == -1) - return 0; - - int stack_depth = 0; - CONTEXT thread_context = {0}; - thread_context.ContextFlags = CONTEXT_FULL; - if (::GetThreadContext(thread_handle, &thread_context)) { - stack_depth = RecordStack(&thread_context, max_stack_size, - instruction_pointers, - last_frame_is_unknown_function); - } - - // Disable the priority boost that the thread would otherwise receive on - // resume. We do this to avoid artificially altering the dynamics of the - // executing application any more than we already are by suspending and - // resuming the thread. - // - // Note that this can racily disable a priority boost that otherwise would - // have been given to the thread, if the thread is waiting on other wait - // conditions at the time of SuspendThread and those conditions are satisfied - // before priority boost is reenabled. The measured length of this window is - // ~100us, so this should occur fairly rarely. - ScopedDisablePriorityBoost disable_priority_boost(thread_handle); - bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1; - CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError(); - - return stack_depth; -} - -class NativeStackSamplerWin : public NativeStackSampler { - public: - explicit NativeStackSamplerWin(win::ScopedHandle thread_handle); - ~NativeStackSamplerWin() override; - - // StackSamplingProfiler::NativeStackSampler: - void ProfileRecordingStarting( - std::vector<StackSamplingProfiler::Module>* modules) override; - void RecordStackSample(StackSamplingProfiler::Sample* sample) override; - void ProfileRecordingStopped() override; - - private: - // Attempts to query the module filename, base address, and id for - // |module_handle|, and store them in |module|. Returns true if it succeeded. - static bool GetModuleForHandle(HMODULE module_handle, - StackSamplingProfiler::Module* module); - - // Gets the index for the Module corresponding to |module_handle| in - // |modules|, adding it if it's not already present. Returns - // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be - // determined for |module|. - size_t GetModuleIndex(HMODULE module_handle, - std::vector<StackSamplingProfiler::Module>* modules); - - // Copies the stack information represented by |instruction_pointers| into - // |sample| and |modules|. - void CopyToSample(const void* const instruction_pointers[], - const HMODULE module_handles[], - int stack_depth, - StackSamplingProfiler::Sample* sample, - std::vector<StackSamplingProfiler::Module>* modules); - - win::ScopedHandle thread_handle_; - // Weak. Points to the modules associated with the profile being recorded - // between ProfileRecordingStarting() and ProfileRecordingStopped(). - std::vector<StackSamplingProfiler::Module>* current_modules_; - // Maps a module handle to the corresponding Module's index within - // current_modules_. - std::map<HMODULE, size_t> profile_module_index_; - - DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin); -}; - -NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle) - : thread_handle_(thread_handle.Take()) { -} - -NativeStackSamplerWin::~NativeStackSamplerWin() { -} - -void NativeStackSamplerWin::ProfileRecordingStarting( - std::vector<StackSamplingProfiler::Module>* modules) { - current_modules_ = modules; - profile_module_index_.clear(); -} - -void NativeStackSamplerWin::RecordStackSample( - StackSamplingProfiler::Sample* sample) { - DCHECK(current_modules_); - - const int max_stack_size = 64; - const void* instruction_pointers[max_stack_size] = {0}; - HMODULE module_handles[max_stack_size] = {0}; - - bool last_frame_is_unknown_function = false; - int stack_depth = SuspendThreadAndRecordStack( - thread_handle_.Get(), max_stack_size, instruction_pointers, - &last_frame_is_unknown_function); - FindModuleHandlesForAddresses(instruction_pointers, module_handles, - stack_depth, last_frame_is_unknown_function); - CopyToSample(instruction_pointers, module_handles, stack_depth, sample, - current_modules_); - FreeModuleHandles(stack_depth, module_handles); -} - -void NativeStackSamplerWin::ProfileRecordingStopped() { - current_modules_ = nullptr; -} - -// static -bool NativeStackSamplerWin::GetModuleForHandle( - HMODULE module_handle, - StackSamplingProfiler::Module* module) { - wchar_t module_name[MAX_PATH]; - DWORD result_length = - GetModuleFileName(module_handle, module_name, arraysize(module_name)); - if (result_length == 0) - return false; - - module->filename = base::FilePath(module_name); - - module->base_address = reinterpret_cast<const void*>(module_handle); - - module->id = GetBuildIDForModule(module_handle); - if (module->id.empty()) - return false; - - return true; -} - -size_t NativeStackSamplerWin::GetModuleIndex( - HMODULE module_handle, - std::vector<StackSamplingProfiler::Module>* modules) { - if (!module_handle) - return StackSamplingProfiler::Frame::kUnknownModuleIndex; - - auto loc = profile_module_index_.find(module_handle); - if (loc == profile_module_index_.end()) { - StackSamplingProfiler::Module module; - if (!GetModuleForHandle(module_handle, &module)) - return StackSamplingProfiler::Frame::kUnknownModuleIndex; - modules->push_back(module); - loc = profile_module_index_.insert(std::make_pair( - module_handle, modules->size() - 1)).first; - } - - return loc->second; -} - -void NativeStackSamplerWin::CopyToSample( - const void* const instruction_pointers[], - const HMODULE module_handles[], - int stack_depth, - StackSamplingProfiler::Sample* sample, - std::vector<StackSamplingProfiler::Module>* module) { - sample->clear(); - sample->reserve(stack_depth); - - for (int i = 0; i < stack_depth; ++i) { - sample->push_back(StackSamplingProfiler::Frame( - instruction_pointers[i], - GetModuleIndex(module_handles[i], module))); - } -} - -} // namespace - -scoped_ptr<NativeStackSampler> NativeStackSampler::Create( - PlatformThreadId thread_id) { -#if _WIN64 - // Get the thread's handle. - HANDLE thread_handle = ::OpenThread( - THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, - FALSE, - thread_id); - - if (thread_handle) { - return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin( - win::ScopedHandle(thread_handle))); - } -#endif - return scoped_ptr<NativeStackSampler>(); -} - -} // namespace base |