Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / base / profiler / stack_sampling_profiler_win.cc
blob73b8e11ce41257cc2be911750d59506b03fc336b
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/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/time/time.h"
17 #include "base/win/pe_image.h"
18 #include "base/win/scoped_handle.h"
20 namespace base {
22 namespace {
24 // Walks the stack represented by |context| from the current frame downwards,
25 // recording the instruction pointers for each frame in |instruction_pointers|.
26 int RecordStack(CONTEXT* context,
27 int max_stack_size,
28 const void* instruction_pointers[],
29 bool* last_frame_is_unknown_function) {
30 #ifdef _WIN64
31 *last_frame_is_unknown_function = false;
33 int i = 0;
34 for (; (i < max_stack_size) && context->Rip; ++i) {
35 // Try to look up unwind metadata for the current function.
36 ULONG64 image_base;
37 PRUNTIME_FUNCTION runtime_function =
38 RtlLookupFunctionEntry(context->Rip, &image_base, nullptr);
40 instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip);
42 if (runtime_function) {
43 KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
44 void* handler_data;
45 ULONG64 establisher_frame;
46 RtlVirtualUnwind(0, image_base, context->Rip, runtime_function, context,
47 &handler_data, &establisher_frame, &nvcontext);
48 } else {
49 // If we don't have a RUNTIME_FUNCTION, then in theory this should be a
50 // leaf function whose frame contains only a return address, at
51 // RSP. However, crash data also indicates that some third party libraries
52 // do not provide RUNTIME_FUNCTION information for non-leaf functions. We
53 // could manually unwind the stack in the former case, but attempting to
54 // do so in the latter case would produce wrong results and likely crash,
55 // so just bail out.
57 // Ad hoc runs with instrumentation show that ~5% of stack traces end with
58 // a valid leaf function. To avoid selectively omitting these traces it
59 // makes sense to ultimately try to distinguish these two cases and
60 // selectively unwind the stack for legitimate leaf functions. For the
61 // purposes of avoiding crashes though, just ignore them all for now.
62 return i;
65 return i;
66 #else
67 return 0;
68 #endif
71 // Fills in |module_handles| corresponding to the pointers to code in
72 // |addresses|. The module handles are returned with reference counts
73 // incremented and should be freed with FreeModuleHandles. See note in
74 // SuspendThreadAndRecordStack for why |addresses| and |module_handles| are
75 // arrays.
76 void FindModuleHandlesForAddresses(const void* const addresses[],
77 HMODULE module_handles[], int stack_depth,
78 bool last_frame_is_unknown_function) {
79 const int module_frames =
80 last_frame_is_unknown_function ? stack_depth - 1 : stack_depth;
81 for (int i = 0; i < module_frames; ++i) {
82 HMODULE module_handle = NULL;
83 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
84 reinterpret_cast<LPCTSTR>(addresses[i]),
85 &module_handle)) {
86 // HMODULE actually represents the base address of the module, so we can
87 // use it directly as an address.
88 DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]);
89 module_handles[i] = module_handle;
94 // Frees the modules handles returned by FindModuleHandlesForAddresses. See note
95 // in SuspendThreadAndRecordStack for why |module_handles| is an array.
96 void FreeModuleHandles(int stack_depth, HMODULE module_handles[]) {
97 for (int i = 0; i < stack_depth; ++i) {
98 if (module_handles[i])
99 ::FreeLibrary(module_handles[i]);
103 // Gets the unique build ID for a module. Windows build IDs are created by a
104 // concatenation of a GUID and AGE fields found in the headers of a module. The
105 // GUID is stored in the first 16 bytes and the AGE is stored in the last 4
106 // bytes. Returns the empty string if the function fails to get the build ID.
108 // Example:
109 // dumpbin chrome.exe /headers | find "Format:"
110 // ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ...
112 // The resulting buildID string of this instance of chrome.exe is
113 // "16B2A4281DED442E9A36FCE8CBD2972610".
115 // Note that the AGE field is encoded in decimal, not hex.
116 std::string GetBuildIDForModule(HMODULE module_handle) {
117 GUID guid;
118 DWORD age;
119 win::PEImage(module_handle).GetDebugId(&guid, &age);
120 const int kGUIDSize = 39;
121 std::wstring build_id;
122 int result =
123 ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize);
124 if (result != kGUIDSize)
125 return std::string();
126 RemoveChars(build_id, L"{}-", &build_id);
127 build_id += StringPrintf(L"%d", age);
128 return WideToUTF8(build_id);
131 // Disables priority boost on a thread for the lifetime of the object.
132 class ScopedDisablePriorityBoost {
133 public:
134 ScopedDisablePriorityBoost(HANDLE thread_handle);
135 ~ScopedDisablePriorityBoost();
137 private:
138 HANDLE thread_handle_;
139 BOOL got_previous_boost_state_;
140 BOOL boost_state_was_disabled_;
142 DISALLOW_COPY_AND_ASSIGN(ScopedDisablePriorityBoost);
145 ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle)
146 : thread_handle_(thread_handle),
147 got_previous_boost_state_(false),
148 boost_state_was_disabled_(false) {
149 got_previous_boost_state_ =
150 ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_);
151 if (got_previous_boost_state_) {
152 // Confusingly, TRUE disables priority boost.
153 ::SetThreadPriorityBoost(thread_handle_, TRUE);
157 ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
158 if (got_previous_boost_state_)
159 ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_);
162 // Suspends the thread with |thread_handle|, records the stack into
163 // |instruction_pointers|, then resumes the thread. Returns the size of the
164 // stack.
166 // IMPORTANT NOTE: No heap allocations may occur between SuspendThread and
167 // ResumeThread. Otherwise this code can deadlock on heap locks acquired by the
168 // target thread before it was suspended. This is why we pass instruction
169 // pointers and module handles as preallocated arrays rather than vectors, since
170 // vectors make it too easy to subtly allocate memory.
171 int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size,
172 const void* instruction_pointers[],
173 bool* last_frame_is_unknown_function) {
174 if (::SuspendThread(thread_handle) == -1)
175 return 0;
177 int stack_depth = 0;
178 CONTEXT thread_context = {0};
179 thread_context.ContextFlags = CONTEXT_FULL;
180 if (::GetThreadContext(thread_handle, &thread_context)) {
181 stack_depth = RecordStack(&thread_context, max_stack_size,
182 instruction_pointers,
183 last_frame_is_unknown_function);
186 // Disable the priority boost that the thread would otherwise receive on
187 // resume. We do this to avoid artificially altering the dynamics of the
188 // executing application any more than we already are by suspending and
189 // resuming the thread.
191 // Note that this can racily disable a priority boost that otherwise would
192 // have been given to the thread, if the thread is waiting on other wait
193 // conditions at the time of SuspendThread and those conditions are satisfied
194 // before priority boost is reenabled. The measured length of this window is
195 // ~100us, so this should occur fairly rarely.
196 ScopedDisablePriorityBoost disable_priority_boost(thread_handle);
197 bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1;
198 CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError();
200 return stack_depth;
203 class NativeStackSamplerWin : public NativeStackSampler {
204 public:
205 explicit NativeStackSamplerWin(win::ScopedHandle thread_handle);
206 ~NativeStackSamplerWin() override;
208 // StackSamplingProfiler::NativeStackSampler:
209 void ProfileRecordingStarting(
210 std::vector<StackSamplingProfiler::Module>* modules) override;
211 void RecordStackSample(StackSamplingProfiler::Sample* sample) override;
212 void ProfileRecordingStopped() override;
214 private:
215 // Attempts to query the module filename, base address, and id for
216 // |module_handle|, and store them in |module|. Returns true if it succeeded.
217 static bool GetModuleForHandle(HMODULE module_handle,
218 StackSamplingProfiler::Module* module);
220 // Gets the index for the Module corresponding to |module_handle| in
221 // |modules|, adding it if it's not already present. Returns
222 // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be
223 // determined for |module|.
224 size_t GetModuleIndex(HMODULE module_handle,
225 std::vector<StackSamplingProfiler::Module>* modules);
227 // Copies the stack information represented by |instruction_pointers| into
228 // |sample| and |modules|.
229 void CopyToSample(const void* const instruction_pointers[],
230 const HMODULE module_handles[],
231 int stack_depth,
232 StackSamplingProfiler::Sample* sample,
233 std::vector<StackSamplingProfiler::Module>* modules);
235 win::ScopedHandle thread_handle_;
236 // Weak. Points to the modules associated with the profile being recorded
237 // between ProfileRecordingStarting() and ProfileRecordingStopped().
238 std::vector<StackSamplingProfiler::Module>* current_modules_;
239 // Maps a module handle to the corresponding Module's index within
240 // current_modules_.
241 std::map<HMODULE, size_t> profile_module_index_;
243 DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin);
246 NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle)
247 : thread_handle_(thread_handle.Take()) {
250 NativeStackSamplerWin::~NativeStackSamplerWin() {
253 void NativeStackSamplerWin::ProfileRecordingStarting(
254 std::vector<StackSamplingProfiler::Module>* modules) {
255 current_modules_ = modules;
256 profile_module_index_.clear();
259 void NativeStackSamplerWin::RecordStackSample(
260 StackSamplingProfiler::Sample* sample) {
261 DCHECK(current_modules_);
263 const int max_stack_size = 64;
264 const void* instruction_pointers[max_stack_size] = {0};
265 HMODULE module_handles[max_stack_size] = {0};
267 bool last_frame_is_unknown_function = false;
268 int stack_depth = SuspendThreadAndRecordStack(
269 thread_handle_.Get(), max_stack_size, instruction_pointers,
270 &last_frame_is_unknown_function);
271 FindModuleHandlesForAddresses(instruction_pointers, module_handles,
272 stack_depth, last_frame_is_unknown_function);
273 CopyToSample(instruction_pointers, module_handles, stack_depth, sample,
274 current_modules_);
275 FreeModuleHandles(stack_depth, module_handles);
278 void NativeStackSamplerWin::ProfileRecordingStopped() {
279 current_modules_ = nullptr;
282 // static
283 bool NativeStackSamplerWin::GetModuleForHandle(
284 HMODULE module_handle,
285 StackSamplingProfiler::Module* module) {
286 wchar_t module_name[MAX_PATH];
287 DWORD result_length =
288 GetModuleFileName(module_handle, module_name, arraysize(module_name));
289 if (result_length == 0)
290 return false;
292 module->filename = base::FilePath(module_name);
294 module->base_address = reinterpret_cast<const void*>(module_handle);
296 module->id = GetBuildIDForModule(module_handle);
297 if (module->id.empty())
298 return false;
300 return true;
303 size_t NativeStackSamplerWin::GetModuleIndex(
304 HMODULE module_handle,
305 std::vector<StackSamplingProfiler::Module>* modules) {
306 if (!module_handle)
307 return StackSamplingProfiler::Frame::kUnknownModuleIndex;
309 auto loc = profile_module_index_.find(module_handle);
310 if (loc == profile_module_index_.end()) {
311 StackSamplingProfiler::Module module;
312 if (!GetModuleForHandle(module_handle, &module))
313 return StackSamplingProfiler::Frame::kUnknownModuleIndex;
314 modules->push_back(module);
315 loc = profile_module_index_.insert(std::make_pair(
316 module_handle, modules->size() - 1)).first;
319 return loc->second;
322 void NativeStackSamplerWin::CopyToSample(
323 const void* const instruction_pointers[],
324 const HMODULE module_handles[],
325 int stack_depth,
326 StackSamplingProfiler::Sample* sample,
327 std::vector<StackSamplingProfiler::Module>* module) {
328 sample->clear();
329 sample->reserve(stack_depth);
331 for (int i = 0; i < stack_depth; ++i) {
332 sample->push_back(StackSamplingProfiler::Frame(
333 instruction_pointers[i],
334 GetModuleIndex(module_handles[i], module)));
338 } // namespace
340 scoped_ptr<NativeStackSampler> NativeStackSampler::Create(
341 PlatformThreadId thread_id) {
342 #if _WIN64
343 // Get the thread's handle.
344 HANDLE thread_handle = ::OpenThread(
345 THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
346 FALSE,
347 thread_id);
349 if (thread_handle) {
350 return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin(
351 win::ScopedHandle(thread_handle)));
353 #endif
354 return scoped_ptr<NativeStackSampler>();
357 } // namespace base