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/profiler/win32_stack_frame_unwinder.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/time/time.h"
18 #include "base/win/pe_image.h"
19 #include "base/win/scoped_handle.h"
23 // Stack recording functions --------------------------------------------------
27 // Walks the stack represented by |context| from the current frame downwards,
28 // recording the instruction pointers for each frame in |instruction_pointers|.
29 int RecordStack(CONTEXT
* context
,
31 const void* instruction_pointers
[],
32 Win32StackFrameUnwinder
* frame_unwinder
) {
35 for (; (i
< max_stack_size
) && context
->Rip
; ++i
) {
36 instruction_pointers
[i
] = reinterpret_cast<const void*>(context
->Rip
);
37 if (!frame_unwinder
->TryUnwind(context
))
46 // Fills in |module_handles| corresponding to the pointers to code in
47 // |addresses|. The module handles are returned with reference counts
48 // incremented and should be freed with FreeModuleHandles. See note in
49 // SuspendThreadAndRecordStack for why |addresses| and |module_handles| are
51 void FindModuleHandlesForAddresses(const void* const addresses
[],
52 HMODULE module_handles
[], int stack_depth
) {
53 for (int i
= 0; i
< stack_depth
; ++i
) {
54 HMODULE module_handle
= NULL
;
55 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
56 reinterpret_cast<LPCTSTR
>(addresses
[i
]),
58 // HMODULE actually represents the base address of the module, so we can
59 // use it directly as an address.
60 DCHECK_LE(reinterpret_cast<const void*>(module_handle
), addresses
[i
]);
61 module_handles
[i
] = module_handle
;
66 // Frees the modules handles returned by FindModuleHandlesForAddresses. See note
67 // in SuspendThreadAndRecordStack for why |module_handles| is an array.
68 void FreeModuleHandles(int stack_depth
, HMODULE module_handles
[]) {
69 for (int i
= 0; i
< stack_depth
; ++i
) {
70 if (module_handles
[i
])
71 ::FreeLibrary(module_handles
[i
]);
75 // Gets the unique build ID for a module. Windows build IDs are created by a
76 // concatenation of a GUID and AGE fields found in the headers of a module. The
77 // GUID is stored in the first 16 bytes and the AGE is stored in the last 4
78 // bytes. Returns the empty string if the function fails to get the build ID.
81 // dumpbin chrome.exe /headers | find "Format:"
82 // ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ...
84 // The resulting buildID string of this instance of chrome.exe is
85 // "16B2A4281DED442E9A36FCE8CBD2972610".
87 // Note that the AGE field is encoded in decimal, not hex.
88 std::string
GetBuildIDForModule(HMODULE module_handle
) {
91 win::PEImage(module_handle
).GetDebugId(&guid
, &age
);
92 const int kGUIDSize
= 39;
93 std::wstring build_id
;
95 ::StringFromGUID2(guid
, WriteInto(&build_id
, kGUIDSize
), kGUIDSize
);
96 if (result
!= kGUIDSize
)
98 RemoveChars(build_id
, L
"{}-", &build_id
);
99 build_id
+= StringPrintf(L
"%d", age
);
100 return WideToUTF8(build_id
);
103 // ScopedDisablePriorityBoost -------------------------------------------------
105 // Disables priority boost on a thread for the lifetime of the object.
106 class ScopedDisablePriorityBoost
{
108 ScopedDisablePriorityBoost(HANDLE thread_handle
);
109 ~ScopedDisablePriorityBoost();
112 HANDLE thread_handle_
;
113 BOOL got_previous_boost_state_
;
114 BOOL boost_state_was_disabled_
;
116 DISALLOW_COPY_AND_ASSIGN(ScopedDisablePriorityBoost
);
119 ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle
)
120 : thread_handle_(thread_handle
),
121 got_previous_boost_state_(false),
122 boost_state_was_disabled_(false) {
123 got_previous_boost_state_
=
124 ::GetThreadPriorityBoost(thread_handle_
, &boost_state_was_disabled_
);
125 if (got_previous_boost_state_
) {
126 // Confusingly, TRUE disables priority boost.
127 ::SetThreadPriorityBoost(thread_handle_
, TRUE
);
131 ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
132 if (got_previous_boost_state_
)
133 ::SetThreadPriorityBoost(thread_handle_
, boost_state_was_disabled_
);
136 // Suspends the thread with |thread_handle|, records the stack into
137 // |instruction_pointers|, then resumes the thread. Returns the size of the
140 // IMPORTANT NOTE: No heap allocations may occur between SuspendThread and
141 // ResumeThread. Otherwise this code can deadlock on heap locks acquired by the
142 // target thread before it was suspended. This is why we pass instruction
143 // pointers and module handles as preallocated arrays rather than vectors, since
144 // vectors make it too easy to subtly allocate memory.
145 int SuspendThreadAndRecordStack(HANDLE thread_handle
, int max_stack_size
,
146 const void* instruction_pointers
[]) {
147 Win32StackFrameUnwinder frame_unwinder
;
149 if (::SuspendThread(thread_handle
) == -1)
153 CONTEXT thread_context
= {0};
154 thread_context
.ContextFlags
= CONTEXT_FULL
;
155 if (::GetThreadContext(thread_handle
, &thread_context
)) {
156 stack_depth
= RecordStack(&thread_context
, max_stack_size
,
157 instruction_pointers
, &frame_unwinder
);
160 // Disable the priority boost that the thread would otherwise receive on
161 // resume. We do this to avoid artificially altering the dynamics of the
162 // executing application any more than we already are by suspending and
163 // resuming the thread.
165 // Note that this can racily disable a priority boost that otherwise would
166 // have been given to the thread, if the thread is waiting on other wait
167 // conditions at the time of SuspendThread and those conditions are satisfied
168 // before priority boost is reenabled. The measured length of this window is
169 // ~100us, so this should occur fairly rarely.
170 ScopedDisablePriorityBoost
disable_priority_boost(thread_handle
);
171 bool resume_thread_succeeded
= ::ResumeThread(thread_handle
) != -1;
172 CHECK(resume_thread_succeeded
) << "ResumeThread failed: " << GetLastError();
177 // NativeStackSamplerWin ------------------------------------------------------
179 class NativeStackSamplerWin
: public NativeStackSampler
{
181 explicit NativeStackSamplerWin(win::ScopedHandle thread_handle
);
182 ~NativeStackSamplerWin() override
;
184 // StackSamplingProfiler::NativeStackSampler:
185 void ProfileRecordingStarting(
186 std::vector
<StackSamplingProfiler::Module
>* modules
) override
;
187 void RecordStackSample(StackSamplingProfiler::Sample
* sample
) override
;
188 void ProfileRecordingStopped() override
;
191 // Attempts to query the module filename, base address, and id for
192 // |module_handle|, and store them in |module|. Returns true if it succeeded.
193 static bool GetModuleForHandle(HMODULE module_handle
,
194 StackSamplingProfiler::Module
* module
);
196 // Gets the index for the Module corresponding to |module_handle| in
197 // |modules|, adding it if it's not already present. Returns
198 // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be
199 // determined for |module|.
200 size_t GetModuleIndex(HMODULE module_handle
,
201 std::vector
<StackSamplingProfiler::Module
>* modules
);
203 // Copies the stack information represented by |instruction_pointers| into
204 // |sample| and |modules|.
205 void CopyToSample(const void* const instruction_pointers
[],
206 const HMODULE module_handles
[],
208 StackSamplingProfiler::Sample
* sample
,
209 std::vector
<StackSamplingProfiler::Module
>* modules
);
211 win::ScopedHandle thread_handle_
;
212 // Weak. Points to the modules associated with the profile being recorded
213 // between ProfileRecordingStarting() and ProfileRecordingStopped().
214 std::vector
<StackSamplingProfiler::Module
>* current_modules_
;
215 // Maps a module handle to the corresponding Module's index within
217 std::map
<HMODULE
, size_t> profile_module_index_
;
219 DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin
);
222 NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle
)
223 : thread_handle_(thread_handle
.Take()) {
226 NativeStackSamplerWin::~NativeStackSamplerWin() {
229 void NativeStackSamplerWin::ProfileRecordingStarting(
230 std::vector
<StackSamplingProfiler::Module
>* modules
) {
231 current_modules_
= modules
;
232 profile_module_index_
.clear();
235 void NativeStackSamplerWin::RecordStackSample(
236 StackSamplingProfiler::Sample
* sample
) {
237 DCHECK(current_modules_
);
239 const int max_stack_size
= 64;
240 const void* instruction_pointers
[max_stack_size
] = {0};
241 HMODULE module_handles
[max_stack_size
] = {0};
243 int stack_depth
= SuspendThreadAndRecordStack(thread_handle_
.Get(),
245 instruction_pointers
);
246 FindModuleHandlesForAddresses(instruction_pointers
, module_handles
,
248 CopyToSample(instruction_pointers
, module_handles
, stack_depth
, sample
,
250 FreeModuleHandles(stack_depth
, module_handles
);
253 void NativeStackSamplerWin::ProfileRecordingStopped() {
254 current_modules_
= nullptr;
258 bool NativeStackSamplerWin::GetModuleForHandle(
259 HMODULE module_handle
,
260 StackSamplingProfiler::Module
* module
) {
261 wchar_t module_name
[MAX_PATH
];
262 DWORD result_length
=
263 GetModuleFileName(module_handle
, module_name
, arraysize(module_name
));
264 if (result_length
== 0)
267 module
->filename
= base::FilePath(module_name
);
269 module
->base_address
= reinterpret_cast<const void*>(module_handle
);
271 module
->id
= GetBuildIDForModule(module_handle
);
272 if (module
->id
.empty())
278 size_t NativeStackSamplerWin::GetModuleIndex(
279 HMODULE module_handle
,
280 std::vector
<StackSamplingProfiler::Module
>* modules
) {
282 return StackSamplingProfiler::Frame::kUnknownModuleIndex
;
284 auto loc
= profile_module_index_
.find(module_handle
);
285 if (loc
== profile_module_index_
.end()) {
286 StackSamplingProfiler::Module module
;
287 if (!GetModuleForHandle(module_handle
, &module
))
288 return StackSamplingProfiler::Frame::kUnknownModuleIndex
;
289 modules
->push_back(module
);
290 loc
= profile_module_index_
.insert(std::make_pair(
291 module_handle
, modules
->size() - 1)).first
;
297 void NativeStackSamplerWin::CopyToSample(
298 const void* const instruction_pointers
[],
299 const HMODULE module_handles
[],
301 StackSamplingProfiler::Sample
* sample
,
302 std::vector
<StackSamplingProfiler::Module
>* module
) {
304 sample
->reserve(stack_depth
);
306 for (int i
= 0; i
< stack_depth
; ++i
) {
307 sample
->push_back(StackSamplingProfiler::Frame(
308 instruction_pointers
[i
],
309 GetModuleIndex(module_handles
[i
], module
)));
315 scoped_ptr
<NativeStackSampler
> NativeStackSampler::Create(
316 PlatformThreadId thread_id
) {
318 // Get the thread's handle.
319 HANDLE thread_handle
= ::OpenThread(
320 THREAD_GET_CONTEXT
| THREAD_SUSPEND_RESUME
| THREAD_QUERY_INFORMATION
,
325 return scoped_ptr
<NativeStackSampler
>(new NativeStackSamplerWin(
326 win::ScopedHandle(thread_handle
)));
329 return scoped_ptr
<NativeStackSampler
>();