Enterprise policy: Ignore the deprecated ForceSafeSearch if ForceGoogleSafeSearch...
[chromium-blink-merge.git] / base / profiler / stack_sampling_profiler_win.cc
blob93014ad28096c405d0366d39ec2d8c7622f12241
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 = {0};
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 we've encountered a leaf
50 // function. Adjust the stack appropriately prior to the next function
51 // lookup.
52 context->Rip = *reinterpret_cast<PDWORD64>(context->Rsp);
53 context->Rsp += 8;
54 *last_frame_is_unknown_function = true;
57 return i;
58 #else
59 return 0;
60 #endif
63 // Fills in |module_handles| corresponding to the pointers to code in
64 // |addresses|. The module handles are returned with reference counts
65 // incremented and should be freed with FreeModuleHandles. See note in
66 // SuspendThreadAndRecordStack for why |addresses| and |module_handles| are
67 // arrays.
68 void FindModuleHandlesForAddresses(const void* const addresses[],
69 HMODULE module_handles[], int stack_depth,
70 bool last_frame_is_unknown_function) {
71 const int module_frames =
72 last_frame_is_unknown_function ? stack_depth - 1 : stack_depth;
73 for (int i = 0; i < module_frames; ++i) {
74 HMODULE module_handle = NULL;
75 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
76 reinterpret_cast<LPCTSTR>(addresses[i]),
77 &module_handle)) {
78 // HMODULE actually represents the base address of the module, so we can
79 // use it directly as an address.
80 DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]);
81 module_handles[i] = module_handle;
86 // Frees the modules handles returned by FindModuleHandlesForAddresses. See note
87 // in SuspendThreadAndRecordStack for why |module_handles| is an array.
88 void FreeModuleHandles(int stack_depth, HMODULE module_handles[]) {
89 for (int i = 0; i < stack_depth; ++i) {
90 if (module_handles[i])
91 ::FreeLibrary(module_handles[i]);
95 // Gets the unique build ID for a module. Windows build IDs are created by a
96 // concatenation of a GUID and AGE fields found in the headers of a module. The
97 // GUID is stored in the first 16 bytes and the AGE is stored in the last 4
98 // bytes. Returns the empty string if the function fails to get the build ID.
100 // Example:
101 // dumpbin chrome.exe /headers | find "Format:"
102 // ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ...
104 // The resulting buildID string of this instance of chrome.exe is
105 // "16B2A4281DED442E9A36FCE8CBD2972610".
107 // Note that the AGE field is encoded in decimal, not hex.
108 std::string GetBuildIDForModule(HMODULE module_handle) {
109 GUID guid;
110 DWORD age;
111 win::PEImage(module_handle).GetDebugId(&guid, &age);
112 const int kGUIDSize = 39;
113 std::wstring build_id;
114 int result =
115 ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize);
116 if (result != kGUIDSize)
117 return std::string();
118 RemoveChars(build_id, L"{}-", &build_id);
119 build_id += StringPrintf(L"%d", age);
120 return WideToUTF8(build_id);
123 // Disables priority boost on a thread for the lifetime of the object.
124 class ScopedDisablePriorityBoost {
125 public:
126 ScopedDisablePriorityBoost(HANDLE thread_handle);
127 ~ScopedDisablePriorityBoost();
129 private:
130 HANDLE thread_handle_;
131 BOOL got_previous_boost_state_;
132 BOOL boost_state_was_disabled_;
134 DISALLOW_COPY_AND_ASSIGN(ScopedDisablePriorityBoost);
137 ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle)
138 : thread_handle_(thread_handle),
139 got_previous_boost_state_(false),
140 boost_state_was_disabled_(false) {
141 got_previous_boost_state_ =
142 ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_);
143 if (got_previous_boost_state_) {
144 // Confusingly, TRUE disables priority boost.
145 ::SetThreadPriorityBoost(thread_handle_, TRUE);
149 ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
150 if (got_previous_boost_state_)
151 ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_);
154 // Suspends the thread with |thread_handle|, records the stack into
155 // |instruction_pointers|, then resumes the thread. Returns the size of the
156 // stack.
158 // IMPORTANT NOTE: No heap allocations may occur between SuspendThread and
159 // ResumeThread. Otherwise this code can deadlock on heap locks acquired by the
160 // target thread before it was suspended. This is why we pass instruction
161 // pointers and module handles as preallocated arrays rather than vectors, since
162 // vectors make it too easy to subtly allocate memory.
163 int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size,
164 const void* instruction_pointers[],
165 bool* last_frame_is_unknown_function) {
166 if (::SuspendThread(thread_handle) == -1)
167 return 0;
169 int stack_depth = 0;
170 CONTEXT thread_context = {0};
171 thread_context.ContextFlags = CONTEXT_FULL;
172 if (::GetThreadContext(thread_handle, &thread_context)) {
173 stack_depth = RecordStack(&thread_context, max_stack_size,
174 instruction_pointers,
175 last_frame_is_unknown_function);
178 // Disable the priority boost that the thread would otherwise receive on
179 // resume. We do this to avoid artificially altering the dynamics of the
180 // executing application any more than we already are by suspending and
181 // resuming the thread.
183 // Note that this can racily disable a priority boost that otherwise would
184 // have been given to the thread, if the thread is waiting on other wait
185 // conditions at the time of SuspendThread and those conditions are satisfied
186 // before priority boost is reenabled. The measured length of this window is
187 // ~100us, so this should occur fairly rarely.
188 ScopedDisablePriorityBoost disable_priority_boost(thread_handle);
189 bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1;
190 CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError();
192 return stack_depth;
195 class NativeStackSamplerWin : public NativeStackSampler {
196 public:
197 explicit NativeStackSamplerWin(win::ScopedHandle thread_handle);
198 ~NativeStackSamplerWin() override;
200 // StackSamplingProfiler::NativeStackSampler:
201 void ProfileRecordingStarting(
202 std::vector<StackSamplingProfiler::Module>* modules) override;
203 void RecordStackSample(StackSamplingProfiler::Sample* sample) override;
204 void ProfileRecordingStopped() override;
206 private:
207 // Attempts to query the module filename, base address, and id for
208 // |module_handle|, and store them in |module|. Returns true if it succeeded.
209 static bool GetModuleForHandle(HMODULE module_handle,
210 StackSamplingProfiler::Module* module);
212 // Gets the index for the Module corresponding to |module_handle| in
213 // |modules|, adding it if it's not already present. Returns
214 // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be
215 // determined for |module|.
216 size_t GetModuleIndex(HMODULE module_handle,
217 std::vector<StackSamplingProfiler::Module>* modules);
219 // Copies the stack information represented by |instruction_pointers| into
220 // |sample| and |modules|.
221 void CopyToSample(const void* const instruction_pointers[],
222 const HMODULE module_handles[],
223 int stack_depth,
224 StackSamplingProfiler::Sample* sample,
225 std::vector<StackSamplingProfiler::Module>* modules);
227 win::ScopedHandle thread_handle_;
228 // Weak. Points to the modules associated with the profile being recorded
229 // between ProfileRecordingStarting() and ProfileRecordingStopped().
230 std::vector<StackSamplingProfiler::Module>* current_modules_;
231 // Maps a module handle to the corresponding Module's index within
232 // current_modules_.
233 std::map<HMODULE, size_t> profile_module_index_;
235 DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin);
238 NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle)
239 : thread_handle_(thread_handle.Take()) {
242 NativeStackSamplerWin::~NativeStackSamplerWin() {
245 void NativeStackSamplerWin::ProfileRecordingStarting(
246 std::vector<StackSamplingProfiler::Module>* modules) {
247 current_modules_ = modules;
248 profile_module_index_.clear();
251 void NativeStackSamplerWin::RecordStackSample(
252 StackSamplingProfiler::Sample* sample) {
253 DCHECK(current_modules_);
255 const int max_stack_size = 64;
256 const void* instruction_pointers[max_stack_size] = {0};
257 HMODULE module_handles[max_stack_size] = {0};
259 bool last_frame_is_unknown_function = false;
260 int stack_depth = SuspendThreadAndRecordStack(
261 thread_handle_.Get(), max_stack_size, instruction_pointers,
262 &last_frame_is_unknown_function);
263 FindModuleHandlesForAddresses(instruction_pointers, module_handles,
264 stack_depth, last_frame_is_unknown_function);
265 CopyToSample(instruction_pointers, module_handles, stack_depth, sample,
266 current_modules_);
267 FreeModuleHandles(stack_depth, module_handles);
270 void NativeStackSamplerWin::ProfileRecordingStopped() {
271 current_modules_ = nullptr;
274 // static
275 bool NativeStackSamplerWin::GetModuleForHandle(
276 HMODULE module_handle,
277 StackSamplingProfiler::Module* module) {
278 wchar_t module_name[MAX_PATH];
279 DWORD result_length =
280 GetModuleFileName(module_handle, module_name, arraysize(module_name));
281 if (result_length == 0)
282 return false;
284 module->filename = base::FilePath(module_name);
286 module->base_address = reinterpret_cast<const void*>(module_handle);
288 module->id = GetBuildIDForModule(module_handle);
289 if (module->id.empty())
290 return false;
292 return true;
295 size_t NativeStackSamplerWin::GetModuleIndex(
296 HMODULE module_handle,
297 std::vector<StackSamplingProfiler::Module>* modules) {
298 if (!module_handle)
299 return StackSamplingProfiler::Frame::kUnknownModuleIndex;
301 auto loc = profile_module_index_.find(module_handle);
302 if (loc == profile_module_index_.end()) {
303 StackSamplingProfiler::Module module;
304 if (!GetModuleForHandle(module_handle, &module))
305 return StackSamplingProfiler::Frame::kUnknownModuleIndex;
306 modules->push_back(module);
307 loc = profile_module_index_.insert(std::make_pair(
308 module_handle, modules->size() - 1)).first;
311 return loc->second;
314 void NativeStackSamplerWin::CopyToSample(
315 const void* const instruction_pointers[],
316 const HMODULE module_handles[],
317 int stack_depth,
318 StackSamplingProfiler::Sample* sample,
319 std::vector<StackSamplingProfiler::Module>* module) {
320 sample->clear();
321 sample->reserve(stack_depth);
323 for (int i = 0; i < stack_depth; ++i) {
324 sample->push_back(StackSamplingProfiler::Frame(
325 instruction_pointers[i],
326 GetModuleIndex(module_handles[i], module)));
330 } // namespace
332 scoped_ptr<NativeStackSampler> NativeStackSampler::Create(
333 PlatformThreadId thread_id) {
334 #if _WIN64
335 // Get the thread's handle.
336 HANDLE thread_handle = ::OpenThread(
337 THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
338 FALSE,
339 thread_id);
341 if (thread_handle) {
342 return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin(
343 win::ScopedHandle(thread_handle)));
345 #endif
346 return scoped_ptr<NativeStackSampler>();
349 } // namespace base