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.
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"
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
,
24 const void* instruction_pointers
[],
25 bool* last_frame_is_unknown_function
) {
27 *last_frame_is_unknown_function
= false;
30 for (; (i
< max_stack_size
) && context
->Rip
; ++i
) {
31 // Try to look up unwind metadata for the current function.
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};
41 ULONG64 establisher_frame
;
42 RtlVirtualUnwind(0, image_base
, context
->Rip
, runtime_function
, context
,
43 &handler_data
, &establisher_frame
, &nvcontext
);
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
48 context
->Rip
= *reinterpret_cast<PDWORD64
>(context
->Rsp
);
50 *last_frame_is_unknown_function
= true;
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
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
]),
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
{
94 ScopedDisablePriorityBoost(HANDLE thread_handle
);
95 ~ScopedDisablePriorityBoost();
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
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)
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();
163 class NativeStackSamplerWin
: public NativeStackSampler
{
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
;
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
[],
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
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
,
235 FreeModuleHandles(stack_depth
, module_handles
);
238 void NativeStackSamplerWin::ProfileRecordingStopped() {
239 current_modules_
= nullptr;
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)
252 module
->filename
= base::FilePath(module_name
);
254 module
->base_address
= reinterpret_cast<const void*>(module_handle
);
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
));
265 size_t NativeStackSamplerWin::GetModuleIndex(
266 HMODULE module_handle
,
267 std::vector
<StackSamplingProfiler::Module
>* modules
) {
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
;
284 void NativeStackSamplerWin::CopyToSample(
285 const void* const instruction_pointers
[],
286 const HMODULE module_handles
[],
288 StackSamplingProfiler::Sample
* sample
,
289 std::vector
<StackSamplingProfiler::Module
>* module
) {
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
)));
302 scoped_ptr
<NativeStackSampler
> NativeStackSampler::Create(
303 PlatformThreadId thread_id
) {
305 // Get the thread's handle.
306 HANDLE thread_handle
= ::OpenThread(
307 THREAD_GET_CONTEXT
| THREAD_SUSPEND_RESUME
| THREAD_QUERY_INFORMATION
,
312 return scoped_ptr
<NativeStackSampler
>(new NativeStackSamplerWin(
313 win::ScopedHandle(thread_handle
)));
316 return scoped_ptr
<NativeStackSampler
>();