Refactored not to expose raw pointers on ProxyList class.
[chromium-blink-merge.git] / base / profiler / native_stack_sampler_win.cc
bloba39502d378ac87e4876df5d28b640bc428ff6514
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <objbase.h>
6 #include <windows.h>
8 #include <map>
9 #include <utility>
11 #include "base/logging.h"
12 #include "base/profiler/native_stack_sampler.h"
13 #include "base/profiler/win32_stack_frame_unwinder.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/time/time.h"
18 #include "base/win/pe_image.h"
19 #include "base/win/scoped_handle.h"
21 namespace base {
23 // Stack recording functions --------------------------------------------------
25 namespace {
27 // Walks the stack represented by |context| from the current frame downwards,
28 // recording the instruction pointers for each frame in |instruction_pointers|.
29 int RecordStack(CONTEXT* context,
30 int max_stack_size,
31 const void* instruction_pointers[],
32 Win32StackFrameUnwinder* frame_unwinder) {
33 #ifdef _WIN64
34 int i = 0;
35 for (; (i < max_stack_size) && context->Rip; ++i) {
36 instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip);
37 if (!frame_unwinder->TryUnwind(context))
38 return i + 1;
40 return i;
41 #else
42 return 0;
43 #endif
46 // Fills in |module_handles| corresponding to the pointers to code in
47 // |addresses|. The module handles are returned with reference counts
48 // incremented and should be freed with FreeModuleHandles. See note in
49 // SuspendThreadAndRecordStack for why |addresses| and |module_handles| are
50 // arrays.
51 void FindModuleHandlesForAddresses(const void* const addresses[],
52 HMODULE module_handles[], int stack_depth) {
53 for (int i = 0; i < stack_depth; ++i) {
54 HMODULE module_handle = NULL;
55 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
56 reinterpret_cast<LPCTSTR>(addresses[i]),
57 &module_handle)) {
58 // HMODULE actually represents the base address of the module, so we can
59 // use it directly as an address.
60 DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]);
61 module_handles[i] = module_handle;
66 // Frees the modules handles returned by FindModuleHandlesForAddresses. See note
67 // in SuspendThreadAndRecordStack for why |module_handles| is an array.
68 void FreeModuleHandles(int stack_depth, HMODULE module_handles[]) {
69 for (int i = 0; i < stack_depth; ++i) {
70 if (module_handles[i])
71 ::FreeLibrary(module_handles[i]);
75 // Gets the unique build ID for a module. Windows build IDs are created by a
76 // concatenation of a GUID and AGE fields found in the headers of a module. The
77 // GUID is stored in the first 16 bytes and the AGE is stored in the last 4
78 // bytes. Returns the empty string if the function fails to get the build ID.
80 // Example:
81 // dumpbin chrome.exe /headers | find "Format:"
82 // ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ...
84 // The resulting buildID string of this instance of chrome.exe is
85 // "16B2A4281DED442E9A36FCE8CBD2972610".
87 // Note that the AGE field is encoded in decimal, not hex.
88 std::string GetBuildIDForModule(HMODULE module_handle) {
89 GUID guid;
90 DWORD age;
91 win::PEImage(module_handle).GetDebugId(&guid, &age);
92 const int kGUIDSize = 39;
93 std::wstring build_id;
94 int result =
95 ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize);
96 if (result != kGUIDSize)
97 return std::string();
98 RemoveChars(build_id, L"{}-", &build_id);
99 build_id += StringPrintf(L"%d", age);
100 return WideToUTF8(build_id);
103 // ScopedDisablePriorityBoost -------------------------------------------------
105 // Disables priority boost on a thread for the lifetime of the object.
106 class ScopedDisablePriorityBoost {
107 public:
108 ScopedDisablePriorityBoost(HANDLE thread_handle);
109 ~ScopedDisablePriorityBoost();
111 private:
112 HANDLE thread_handle_;
113 BOOL got_previous_boost_state_;
114 BOOL boost_state_was_disabled_;
116 DISALLOW_COPY_AND_ASSIGN(ScopedDisablePriorityBoost);
119 ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle)
120 : thread_handle_(thread_handle),
121 got_previous_boost_state_(false),
122 boost_state_was_disabled_(false) {
123 got_previous_boost_state_ =
124 ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_);
125 if (got_previous_boost_state_) {
126 // Confusingly, TRUE disables priority boost.
127 ::SetThreadPriorityBoost(thread_handle_, TRUE);
131 ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
132 if (got_previous_boost_state_)
133 ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_);
136 // Suspends the thread with |thread_handle|, records the stack into
137 // |instruction_pointers|, then resumes the thread. Returns the size of the
138 // stack.
140 // IMPORTANT NOTE: No heap allocations may occur between SuspendThread and
141 // ResumeThread. Otherwise this code can deadlock on heap locks acquired by the
142 // target thread before it was suspended. This is why we pass instruction
143 // pointers and module handles as preallocated arrays rather than vectors, since
144 // vectors make it too easy to subtly allocate memory.
145 int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size,
146 const void* instruction_pointers[]) {
147 Win32StackFrameUnwinder frame_unwinder;
149 if (::SuspendThread(thread_handle) == -1)
150 return 0;
152 int stack_depth = 0;
153 CONTEXT thread_context = {0};
154 thread_context.ContextFlags = CONTEXT_FULL;
155 if (::GetThreadContext(thread_handle, &thread_context)) {
156 stack_depth = RecordStack(&thread_context, max_stack_size,
157 instruction_pointers, &frame_unwinder);
160 // Disable the priority boost that the thread would otherwise receive on
161 // resume. We do this to avoid artificially altering the dynamics of the
162 // executing application any more than we already are by suspending and
163 // resuming the thread.
165 // Note that this can racily disable a priority boost that otherwise would
166 // have been given to the thread, if the thread is waiting on other wait
167 // conditions at the time of SuspendThread and those conditions are satisfied
168 // before priority boost is reenabled. The measured length of this window is
169 // ~100us, so this should occur fairly rarely.
170 ScopedDisablePriorityBoost disable_priority_boost(thread_handle);
171 bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1;
172 CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError();
174 return stack_depth;
177 // NativeStackSamplerWin ------------------------------------------------------
179 class NativeStackSamplerWin : public NativeStackSampler {
180 public:
181 explicit NativeStackSamplerWin(win::ScopedHandle thread_handle);
182 ~NativeStackSamplerWin() override;
184 // StackSamplingProfiler::NativeStackSampler:
185 void ProfileRecordingStarting(
186 std::vector<StackSamplingProfiler::Module>* modules) override;
187 void RecordStackSample(StackSamplingProfiler::Sample* sample) override;
188 void ProfileRecordingStopped() override;
190 private:
191 // Attempts to query the module filename, base address, and id for
192 // |module_handle|, and store them in |module|. Returns true if it succeeded.
193 static bool GetModuleForHandle(HMODULE module_handle,
194 StackSamplingProfiler::Module* module);
196 // Gets the index for the Module corresponding to |module_handle| in
197 // |modules|, adding it if it's not already present. Returns
198 // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be
199 // determined for |module|.
200 size_t GetModuleIndex(HMODULE module_handle,
201 std::vector<StackSamplingProfiler::Module>* modules);
203 // Copies the stack information represented by |instruction_pointers| into
204 // |sample| and |modules|.
205 void CopyToSample(const void* const instruction_pointers[],
206 const HMODULE module_handles[],
207 int stack_depth,
208 StackSamplingProfiler::Sample* sample,
209 std::vector<StackSamplingProfiler::Module>* modules);
211 win::ScopedHandle thread_handle_;
212 // Weak. Points to the modules associated with the profile being recorded
213 // between ProfileRecordingStarting() and ProfileRecordingStopped().
214 std::vector<StackSamplingProfiler::Module>* current_modules_;
215 // Maps a module handle to the corresponding Module's index within
216 // current_modules_.
217 std::map<HMODULE, size_t> profile_module_index_;
219 DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin);
222 NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle)
223 : thread_handle_(thread_handle.Take()) {
226 NativeStackSamplerWin::~NativeStackSamplerWin() {
229 void NativeStackSamplerWin::ProfileRecordingStarting(
230 std::vector<StackSamplingProfiler::Module>* modules) {
231 current_modules_ = modules;
232 profile_module_index_.clear();
235 void NativeStackSamplerWin::RecordStackSample(
236 StackSamplingProfiler::Sample* sample) {
237 DCHECK(current_modules_);
239 const int max_stack_size = 64;
240 const void* instruction_pointers[max_stack_size] = {0};
241 HMODULE module_handles[max_stack_size] = {0};
243 int stack_depth = SuspendThreadAndRecordStack(thread_handle_.Get(),
244 max_stack_size,
245 instruction_pointers);
246 FindModuleHandlesForAddresses(instruction_pointers, module_handles,
247 stack_depth);
248 CopyToSample(instruction_pointers, module_handles, stack_depth, sample,
249 current_modules_);
250 FreeModuleHandles(stack_depth, module_handles);
253 void NativeStackSamplerWin::ProfileRecordingStopped() {
254 current_modules_ = nullptr;
257 // static
258 bool NativeStackSamplerWin::GetModuleForHandle(
259 HMODULE module_handle,
260 StackSamplingProfiler::Module* module) {
261 wchar_t module_name[MAX_PATH];
262 DWORD result_length =
263 GetModuleFileName(module_handle, module_name, arraysize(module_name));
264 if (result_length == 0)
265 return false;
267 module->filename = base::FilePath(module_name);
269 module->base_address = reinterpret_cast<const void*>(module_handle);
271 module->id = GetBuildIDForModule(module_handle);
272 if (module->id.empty())
273 return false;
275 return true;
278 size_t NativeStackSamplerWin::GetModuleIndex(
279 HMODULE module_handle,
280 std::vector<StackSamplingProfiler::Module>* modules) {
281 if (!module_handle)
282 return StackSamplingProfiler::Frame::kUnknownModuleIndex;
284 auto loc = profile_module_index_.find(module_handle);
285 if (loc == profile_module_index_.end()) {
286 StackSamplingProfiler::Module module;
287 if (!GetModuleForHandle(module_handle, &module))
288 return StackSamplingProfiler::Frame::kUnknownModuleIndex;
289 modules->push_back(module);
290 loc = profile_module_index_.insert(std::make_pair(
291 module_handle, modules->size() - 1)).first;
294 return loc->second;
297 void NativeStackSamplerWin::CopyToSample(
298 const void* const instruction_pointers[],
299 const HMODULE module_handles[],
300 int stack_depth,
301 StackSamplingProfiler::Sample* sample,
302 std::vector<StackSamplingProfiler::Module>* module) {
303 sample->clear();
304 sample->reserve(stack_depth);
306 for (int i = 0; i < stack_depth; ++i) {
307 sample->push_back(StackSamplingProfiler::Frame(
308 instruction_pointers[i],
309 GetModuleIndex(module_handles[i], module)));
313 } // namespace
315 scoped_ptr<NativeStackSampler> NativeStackSampler::Create(
316 PlatformThreadId thread_id) {
317 #if _WIN64
318 // Get the thread's handle.
319 HANDLE thread_handle = ::OpenThread(
320 THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
321 FALSE,
322 thread_id);
324 if (thread_handle) {
325 return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin(
326 win::ScopedHandle(thread_handle)));
328 #endif
329 return scoped_ptr<NativeStackSampler>();
332 } // namespace base