Make castv2 performance test work.
[chromium-blink-merge.git] / base / profiler / stack_sampling_profiler_win.cc
blob4d0f1d6992f21a8e2072cd32689edd5e181467a1
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 <windows.h>
7 #include <map>
8 #include <utility>
10 #include "base/logging.h"
11 #include "base/profiler/native_stack_sampler.h"
12 #include "base/time/time.h"
13 #include "base/win/pe_image.h"
14 #include "base/win/scoped_handle.h"
16 namespace base {
18 namespace {
20 // Walks the stack represented by |context| from the current frame downwards,
21 // recording the instruction pointers for each frame in |instruction_pointers|.
22 int RecordStack(CONTEXT* context,
23 int max_stack_size,
24 const void* instruction_pointers[],
25 bool* last_frame_is_unknown_function) {
26 #ifdef _WIN64
27 *last_frame_is_unknown_function = false;
29 int i = 0;
30 for (; (i < max_stack_size) && context->Rip; ++i) {
31 // Try to look up unwind metadata for the current function.
32 ULONG64 image_base;
33 PRUNTIME_FUNCTION runtime_function =
34 RtlLookupFunctionEntry(context->Rip, &image_base, nullptr);
36 instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip);
38 if (runtime_function) {
39 KNONVOLATILE_CONTEXT_POINTERS nvcontext = {0};
40 void* handler_data;
41 ULONG64 establisher_frame;
42 RtlVirtualUnwind(0, image_base, context->Rip, runtime_function, context,
43 &handler_data, &establisher_frame, &nvcontext);
44 } else {
45 // If we don't have a RUNTIME_FUNCTION, then we've encountered a leaf
46 // function. Adjust the stack appropriately prior to the next function
47 // lookup.
48 context->Rip = *reinterpret_cast<PDWORD64>(context->Rsp);
49 context->Rsp += 8;
50 *last_frame_is_unknown_function = true;
53 return i;
54 #else
55 return 0;
56 #endif
59 // Fills in |module_handles| corresponding to the pointers to code in
60 // |addresses|. The module handles are returned with reference counts
61 // incremented and should be freed with FreeModuleHandles. See note in
62 // SuspendThreadAndRecordStack for why |addresses| and |module_handles| are
63 // arrays.
64 void FindModuleHandlesForAddresses(const void* const addresses[],
65 HMODULE module_handles[], int stack_depth,
66 bool last_frame_is_unknown_function) {
67 const int module_frames =
68 last_frame_is_unknown_function ? stack_depth - 1 : stack_depth;
69 for (int i = 0; i < module_frames; ++i) {
70 HMODULE module_handle = NULL;
71 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
72 reinterpret_cast<LPCTSTR>(addresses[i]),
73 &module_handle)) {
74 // HMODULE actually represents the base address of the module, so we can
75 // use it directly as an address.
76 DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]);
77 module_handles[i] = module_handle;
82 // Frees the modules handles returned by FindModuleHandlesForAddresses. See note
83 // in SuspendThreadAndRecordStack for why |module_handles| is an array.
84 void FreeModuleHandles(int stack_depth, HMODULE module_handles[]) {
85 for (int i = 0; i < stack_depth; ++i) {
86 if (module_handles[i])
87 ::FreeLibrary(module_handles[i]);
91 // Disables priority boost on a thread for the lifetime of the object.
92 class ScopedDisablePriorityBoost {
93 public:
94 ScopedDisablePriorityBoost(HANDLE thread_handle);
95 ~ScopedDisablePriorityBoost();
97 private:
98 HANDLE thread_handle_;
99 BOOL got_previous_boost_state_;
100 BOOL boost_state_was_disabled_;
102 DISALLOW_COPY_AND_ASSIGN(ScopedDisablePriorityBoost);
105 ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle)
106 : thread_handle_(thread_handle),
107 got_previous_boost_state_(false),
108 boost_state_was_disabled_(false) {
109 got_previous_boost_state_ =
110 ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_);
111 if (got_previous_boost_state_) {
112 // Confusingly, TRUE disables priority boost.
113 ::SetThreadPriorityBoost(thread_handle_, TRUE);
117 ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
118 if (got_previous_boost_state_)
119 ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_);
122 // Suspends the thread with |thread_handle|, records the stack into
123 // |instruction_pointers|, then resumes the thread. Returns the size of the
124 // stack.
126 // IMPORTANT NOTE: No heap allocations may occur between SuspendThread and
127 // ResumeThread. Otherwise this code can deadlock on heap locks acquired by the
128 // target thread before it was suspended. This is why we pass instruction
129 // pointers and module handles as preallocated arrays rather than vectors, since
130 // vectors make it too easy to subtly allocate memory.
131 int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size,
132 const void* instruction_pointers[],
133 bool* last_frame_is_unknown_function) {
134 if (::SuspendThread(thread_handle) == -1)
135 return 0;
137 int stack_depth = 0;
138 CONTEXT thread_context = {0};
139 thread_context.ContextFlags = CONTEXT_FULL;
140 if (::GetThreadContext(thread_handle, &thread_context)) {
141 stack_depth = RecordStack(&thread_context, max_stack_size,
142 instruction_pointers,
143 last_frame_is_unknown_function);
146 // Disable the priority boost that the thread would otherwise receive on
147 // resume. We do this to avoid artificially altering the dynamics of the
148 // executing application any more than we already are by suspending and
149 // resuming the thread.
151 // Note that this can racily disable a priority boost that otherwise would
152 // have been given to the thread, if the thread is waiting on other wait
153 // conditions at the time of SuspendThread and those conditions are satisfied
154 // before priority boost is reenabled. The measured length of this window is
155 // ~100us, so this should occur fairly rarely.
156 ScopedDisablePriorityBoost disable_priority_boost(thread_handle);
157 bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1;
158 CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError();
160 return stack_depth;
163 class NativeStackSamplerWin : public NativeStackSampler {
164 public:
165 explicit NativeStackSamplerWin(win::ScopedHandle thread_handle);
166 ~NativeStackSamplerWin() override;
168 // StackSamplingProfiler::NativeStackSampler:
169 void ProfileRecordingStarting(
170 std::vector<StackSamplingProfiler::Module>* modules) override;
171 void RecordStackSample(StackSamplingProfiler::Sample* sample) override;
172 void ProfileRecordingStopped() override;
174 private:
175 // Attempts to query the module filename, base address, and id for
176 // |module_handle|, and store them in |module|. Returns true if it succeeded.
177 static bool GetModuleForHandle(HMODULE module_handle,
178 StackSamplingProfiler::Module* module);
180 // Gets the index for the Module corresponding to |module_handle| in
181 // |modules|, adding it if it's not already present. Returns
182 // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be
183 // determined for |module|.
184 size_t GetModuleIndex(HMODULE module_handle,
185 std::vector<StackSamplingProfiler::Module>* modules);
187 // Copies the stack information represented by |instruction_pointers| into
188 // |sample| and |modules|.
189 void CopyToSample(const void* const instruction_pointers[],
190 const HMODULE module_handles[],
191 int stack_depth,
192 StackSamplingProfiler::Sample* sample,
193 std::vector<StackSamplingProfiler::Module>* modules);
195 win::ScopedHandle thread_handle_;
196 // Weak. Points to the modules associated with the profile being recorded
197 // between ProfileRecordingStarting() and ProfileRecordingStopped().
198 std::vector<StackSamplingProfiler::Module>* current_modules_;
199 // Maps a module handle to the corresponding Module's index within
200 // current_modules_.
201 std::map<HMODULE, size_t> profile_module_index_;
203 DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin);
206 NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle)
207 : thread_handle_(thread_handle.Take()) {
210 NativeStackSamplerWin::~NativeStackSamplerWin() {
213 void NativeStackSamplerWin::ProfileRecordingStarting(
214 std::vector<StackSamplingProfiler::Module>* modules) {
215 current_modules_ = modules;
216 profile_module_index_.clear();
219 void NativeStackSamplerWin::RecordStackSample(
220 StackSamplingProfiler::Sample* sample) {
221 DCHECK(current_modules_);
223 const int max_stack_size = 64;
224 const void* instruction_pointers[max_stack_size] = {0};
225 HMODULE module_handles[max_stack_size] = {0};
227 bool last_frame_is_unknown_function = false;
228 int stack_depth = SuspendThreadAndRecordStack(
229 thread_handle_.Get(), max_stack_size, instruction_pointers,
230 &last_frame_is_unknown_function);
231 FindModuleHandlesForAddresses(instruction_pointers, module_handles,
232 stack_depth, last_frame_is_unknown_function);
233 CopyToSample(instruction_pointers, module_handles, stack_depth, sample,
234 current_modules_);
235 FreeModuleHandles(stack_depth, module_handles);
238 void NativeStackSamplerWin::ProfileRecordingStopped() {
239 current_modules_ = nullptr;
242 // static
243 bool NativeStackSamplerWin::GetModuleForHandle(
244 HMODULE module_handle,
245 StackSamplingProfiler::Module* module) {
246 wchar_t module_name[MAX_PATH];
247 DWORD result_length =
248 GetModuleFileName(module_handle, module_name, arraysize(module_name));
249 if (result_length == 0)
250 return false;
252 module->filename = base::FilePath(module_name);
254 module->base_address = reinterpret_cast<const void*>(module_handle);
256 GUID guid;
257 DWORD age;
258 win::PEImage(module_handle).GetDebugId(&guid, &age);
259 module->id.assign(reinterpret_cast<char*>(&guid), sizeof(guid));
260 module->id.append(reinterpret_cast<char*>(&age), sizeof(age));
262 return true;
265 size_t NativeStackSamplerWin::GetModuleIndex(
266 HMODULE module_handle,
267 std::vector<StackSamplingProfiler::Module>* modules) {
268 if (!module_handle)
269 return StackSamplingProfiler::Frame::kUnknownModuleIndex;
271 auto loc = profile_module_index_.find(module_handle);
272 if (loc == profile_module_index_.end()) {
273 StackSamplingProfiler::Module module;
274 if (!GetModuleForHandle(module_handle, &module))
275 return StackSamplingProfiler::Frame::kUnknownModuleIndex;
276 modules->push_back(module);
277 loc = profile_module_index_.insert(std::make_pair(
278 module_handle, modules->size() - 1)).first;
281 return loc->second;
284 void NativeStackSamplerWin::CopyToSample(
285 const void* const instruction_pointers[],
286 const HMODULE module_handles[],
287 int stack_depth,
288 StackSamplingProfiler::Sample* sample,
289 std::vector<StackSamplingProfiler::Module>* module) {
290 sample->clear();
291 sample->reserve(stack_depth);
293 for (int i = 0; i < stack_depth; ++i) {
294 sample->push_back(StackSamplingProfiler::Frame(
295 instruction_pointers[i],
296 GetModuleIndex(module_handles[i], module)));
300 } // namespace
302 scoped_ptr<NativeStackSampler> NativeStackSampler::Create(
303 PlatformThreadId thread_id) {
304 #if _WIN64
305 // Get the thread's handle.
306 HANDLE thread_handle = ::OpenThread(
307 THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
308 FALSE,
309 thread_id);
311 if (thread_handle) {
312 return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin(
313 win::ScopedHandle(thread_handle)));
315 #endif
316 return scoped_ptr<NativeStackSampler>();
319 } // namespace base