Index: base/profiler/stack_sampling_profiler_win.cc |
diff --git a/base/profiler/stack_sampling_profiler_win.cc b/base/profiler/stack_sampling_profiler_win.cc |
deleted file mode 100644 |
index 1ccd13412a64496c17fd9d1476e92abb0fff71c0..0000000000000000000000000000000000000000 |
--- 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 |