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.
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"
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
,
28 const void* instruction_pointers
[],
29 bool* last_frame_is_unknown_function
) {
31 *last_frame_is_unknown_function
= false;
34 for (; (i
< max_stack_size
) && context
->Rip
; ++i
) {
35 // Try to look up unwind metadata for the current function.
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};
45 ULONG64 establisher_frame
;
46 RtlVirtualUnwind(0, image_base
, context
->Rip
, runtime_function
, context
,
47 &handler_data
, &establisher_frame
, &nvcontext
);
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
52 context
->Rip
= *reinterpret_cast<PDWORD64
>(context
->Rsp
);
54 *last_frame_is_unknown_function
= true;
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
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
]),
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.
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
) {
111 win::PEImage(module_handle
).GetDebugId(&guid
, &age
);
112 const int kGUIDSize
= 39;
113 std::wstring build_id
;
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
{
126 ScopedDisablePriorityBoost(HANDLE thread_handle
);
127 ~ScopedDisablePriorityBoost();
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
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)
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();
195 class NativeStackSamplerWin
: public NativeStackSampler
{
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
;
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
[],
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
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
,
267 FreeModuleHandles(stack_depth
, module_handles
);
270 void NativeStackSamplerWin::ProfileRecordingStopped() {
271 current_modules_
= nullptr;
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)
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())
295 size_t NativeStackSamplerWin::GetModuleIndex(
296 HMODULE module_handle
,
297 std::vector
<StackSamplingProfiler::Module
>* modules
) {
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
;
314 void NativeStackSamplerWin::CopyToSample(
315 const void* const instruction_pointers
[],
316 const HMODULE module_handles
[],
318 StackSamplingProfiler::Sample
* sample
,
319 std::vector
<StackSamplingProfiler::Module
>* module
) {
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
)));
332 scoped_ptr
<NativeStackSampler
> NativeStackSampler::Create(
333 PlatformThreadId thread_id
) {
335 // Get the thread's handle.
336 HANDLE thread_handle
= ::OpenThread(
337 THREAD_GET_CONTEXT
| THREAD_SUSPEND_RESUME
| THREAD_QUERY_INFORMATION
,
342 return scoped_ptr
<NativeStackSampler
>(new NativeStackSamplerWin(
343 win::ScopedHandle(thread_handle
)));
346 return scoped_ptr
<NativeStackSampler
>();