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
= {};
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 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,
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.
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
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
]),
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.
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
) {
119 win::PEImage(module_handle
).GetDebugId(&guid
, &age
);
120 const int kGUIDSize
= 39;
121 std::wstring build_id
;
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
{
134 ScopedDisablePriorityBoost(HANDLE thread_handle
);
135 ~ScopedDisablePriorityBoost();
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
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)
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();
203 class NativeStackSamplerWin
: public NativeStackSampler
{
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
;
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
[],
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
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
,
275 FreeModuleHandles(stack_depth
, module_handles
);
278 void NativeStackSamplerWin::ProfileRecordingStopped() {
279 current_modules_
= nullptr;
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)
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())
303 size_t NativeStackSamplerWin::GetModuleIndex(
304 HMODULE module_handle
,
305 std::vector
<StackSamplingProfiler::Module
>* modules
) {
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
;
322 void NativeStackSamplerWin::CopyToSample(
323 const void* const instruction_pointers
[],
324 const HMODULE module_handles
[],
326 StackSamplingProfiler::Sample
* sample
,
327 std::vector
<StackSamplingProfiler::Module
>* module
) {
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
)));
340 scoped_ptr
<NativeStackSampler
> NativeStackSampler::Create(
341 PlatformThreadId thread_id
) {
343 // Get the thread's handle.
344 HANDLE thread_handle
= ::OpenThread(
345 THREAD_GET_CONTEXT
| THREAD_SUSPEND_RESUME
| THREAD_QUERY_INFORMATION
,
350 return scoped_ptr
<NativeStackSampler
>(new NativeStackSamplerWin(
351 win::ScopedHandle(thread_handle
)));
354 return scoped_ptr
<NativeStackSampler
>();