OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <objbase.h> | 5 #include <objbase.h> |
6 #include <windows.h> | 6 #include <windows.h> |
7 | 7 |
8 #include <map> | 8 #include <map> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/profiler/native_stack_sampler.h" | 12 #include "base/profiler/native_stack_sampler.h" |
| 13 #include "base/profiler/win32_stack_frame_unwinder.h" |
13 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
14 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
15 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
16 #include "base/time/time.h" | 17 #include "base/time/time.h" |
17 #include "base/win/pe_image.h" | 18 #include "base/win/pe_image.h" |
18 #include "base/win/scoped_handle.h" | 19 #include "base/win/scoped_handle.h" |
19 | 20 |
20 namespace base { | 21 namespace base { |
21 | 22 |
| 23 // Stack recording functions -------------------------------------------------- |
| 24 |
22 namespace { | 25 namespace { |
23 | 26 |
24 // Walks the stack represented by |context| from the current frame downwards, | 27 // Walks the stack represented by |context| from the current frame downwards, |
25 // recording the instruction pointers for each frame in |instruction_pointers|. | 28 // recording the instruction pointers for each frame in |instruction_pointers|. |
26 int RecordStack(CONTEXT* context, | 29 int RecordStack(CONTEXT* context, |
27 int max_stack_size, | 30 int max_stack_size, |
28 const void* instruction_pointers[], | 31 const void* instruction_pointers[], |
29 bool* last_frame_is_unknown_function) { | 32 Win32StackFrameUnwinder* frame_unwinder) { |
30 #ifdef _WIN64 | 33 #ifdef _WIN64 |
31 *last_frame_is_unknown_function = false; | |
32 | |
33 int i = 0; | 34 int i = 0; |
34 for (; (i < max_stack_size) && context->Rip; ++i) { | 35 for (; (i < max_stack_size) && context->Rip; ++i) { |
35 // Try to look up unwind metadata for the current function. | |
36 ULONG64 image_base; | |
37 PRUNTIME_FUNCTION runtime_function = | |
38 RtlLookupFunctionEntry(context->Rip, &image_base, nullptr); | |
39 | |
40 instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip); | 36 instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip); |
41 | 37 if (!frame_unwinder->TryUnwind(context)) |
42 if (runtime_function) { | 38 return i + 1; |
43 KNONVOLATILE_CONTEXT_POINTERS nvcontext = {}; | |
44 void* handler_data; | |
45 ULONG64 establisher_frame; | |
46 RtlVirtualUnwind(0, image_base, context->Rip, runtime_function, context, | |
47 &handler_data, &establisher_frame, &nvcontext); | |
48 } else { | |
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, | |
55 // so just bail out. | |
56 // | |
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. | |
62 return i; | |
63 } | |
64 } | 39 } |
65 return i; | 40 return i; |
66 #else | 41 #else |
67 return 0; | 42 return 0; |
68 #endif | 43 #endif |
69 } | 44 } |
70 | 45 |
71 // Fills in |module_handles| corresponding to the pointers to code in | 46 // Fills in |module_handles| corresponding to the pointers to code in |
72 // |addresses|. The module handles are returned with reference counts | 47 // |addresses|. The module handles are returned with reference counts |
73 // incremented and should be freed with FreeModuleHandles. See note in | 48 // incremented and should be freed with FreeModuleHandles. See note in |
74 // SuspendThreadAndRecordStack for why |addresses| and |module_handles| are | 49 // SuspendThreadAndRecordStack for why |addresses| and |module_handles| are |
75 // arrays. | 50 // arrays. |
76 void FindModuleHandlesForAddresses(const void* const addresses[], | 51 void FindModuleHandlesForAddresses(const void* const addresses[], |
77 HMODULE module_handles[], int stack_depth, | 52 HMODULE module_handles[], int stack_depth) { |
78 bool last_frame_is_unknown_function) { | 53 for (int i = 0; i < stack_depth; ++i) { |
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; | 54 HMODULE module_handle = NULL; |
83 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | 55 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, |
84 reinterpret_cast<LPCTSTR>(addresses[i]), | 56 reinterpret_cast<LPCTSTR>(addresses[i]), |
85 &module_handle)) { | 57 &module_handle)) { |
86 // HMODULE actually represents the base address of the module, so we can | 58 // HMODULE actually represents the base address of the module, so we can |
87 // use it directly as an address. | 59 // use it directly as an address. |
88 DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]); | 60 DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]); |
89 module_handles[i] = module_handle; | 61 module_handles[i] = module_handle; |
90 } | 62 } |
91 } | 63 } |
(...skipping 29 matching lines...) Expand all Loading... |
121 std::wstring build_id; | 93 std::wstring build_id; |
122 int result = | 94 int result = |
123 ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize); | 95 ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize); |
124 if (result != kGUIDSize) | 96 if (result != kGUIDSize) |
125 return std::string(); | 97 return std::string(); |
126 RemoveChars(build_id, L"{}-", &build_id); | 98 RemoveChars(build_id, L"{}-", &build_id); |
127 build_id += StringPrintf(L"%d", age); | 99 build_id += StringPrintf(L"%d", age); |
128 return WideToUTF8(build_id); | 100 return WideToUTF8(build_id); |
129 } | 101 } |
130 | 102 |
| 103 // ScopedDisablePriorityBoost ------------------------------------------------- |
| 104 |
131 // Disables priority boost on a thread for the lifetime of the object. | 105 // Disables priority boost on a thread for the lifetime of the object. |
132 class ScopedDisablePriorityBoost { | 106 class ScopedDisablePriorityBoost { |
133 public: | 107 public: |
134 ScopedDisablePriorityBoost(HANDLE thread_handle); | 108 ScopedDisablePriorityBoost(HANDLE thread_handle); |
135 ~ScopedDisablePriorityBoost(); | 109 ~ScopedDisablePriorityBoost(); |
136 | 110 |
137 private: | 111 private: |
138 HANDLE thread_handle_; | 112 HANDLE thread_handle_; |
139 BOOL got_previous_boost_state_; | 113 BOOL got_previous_boost_state_; |
140 BOOL boost_state_was_disabled_; | 114 BOOL boost_state_was_disabled_; |
(...skipping 21 matching lines...) Expand all Loading... |
162 // Suspends the thread with |thread_handle|, records the stack into | 136 // Suspends the thread with |thread_handle|, records the stack into |
163 // |instruction_pointers|, then resumes the thread. Returns the size of the | 137 // |instruction_pointers|, then resumes the thread. Returns the size of the |
164 // stack. | 138 // stack. |
165 // | 139 // |
166 // IMPORTANT NOTE: No heap allocations may occur between SuspendThread and | 140 // IMPORTANT NOTE: No heap allocations may occur between SuspendThread and |
167 // ResumeThread. Otherwise this code can deadlock on heap locks acquired by the | 141 // 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 | 142 // target thread before it was suspended. This is why we pass instruction |
169 // pointers and module handles as preallocated arrays rather than vectors, since | 143 // pointers and module handles as preallocated arrays rather than vectors, since |
170 // vectors make it too easy to subtly allocate memory. | 144 // vectors make it too easy to subtly allocate memory. |
171 int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size, | 145 int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size, |
172 const void* instruction_pointers[], | 146 const void* instruction_pointers[]) { |
173 bool* last_frame_is_unknown_function) { | 147 Win32StackFrameUnwinder frame_unwinder; |
| 148 |
174 if (::SuspendThread(thread_handle) == -1) | 149 if (::SuspendThread(thread_handle) == -1) |
175 return 0; | 150 return 0; |
176 | 151 |
177 int stack_depth = 0; | 152 int stack_depth = 0; |
178 CONTEXT thread_context = {0}; | 153 CONTEXT thread_context = {0}; |
179 thread_context.ContextFlags = CONTEXT_FULL; | 154 thread_context.ContextFlags = CONTEXT_FULL; |
180 if (::GetThreadContext(thread_handle, &thread_context)) { | 155 if (::GetThreadContext(thread_handle, &thread_context)) { |
181 stack_depth = RecordStack(&thread_context, max_stack_size, | 156 stack_depth = RecordStack(&thread_context, max_stack_size, |
182 instruction_pointers, | 157 instruction_pointers, &frame_unwinder); |
183 last_frame_is_unknown_function); | |
184 } | 158 } |
185 | 159 |
186 // Disable the priority boost that the thread would otherwise receive on | 160 // Disable the priority boost that the thread would otherwise receive on |
187 // resume. We do this to avoid artificially altering the dynamics of the | 161 // resume. We do this to avoid artificially altering the dynamics of the |
188 // executing application any more than we already are by suspending and | 162 // executing application any more than we already are by suspending and |
189 // resuming the thread. | 163 // resuming the thread. |
190 // | 164 // |
191 // Note that this can racily disable a priority boost that otherwise would | 165 // 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 | 166 // 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 | 167 // conditions at the time of SuspendThread and those conditions are satisfied |
194 // before priority boost is reenabled. The measured length of this window is | 168 // before priority boost is reenabled. The measured length of this window is |
195 // ~100us, so this should occur fairly rarely. | 169 // ~100us, so this should occur fairly rarely. |
196 ScopedDisablePriorityBoost disable_priority_boost(thread_handle); | 170 ScopedDisablePriorityBoost disable_priority_boost(thread_handle); |
197 bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1; | 171 bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1; |
198 CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError(); | 172 CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError(); |
199 | 173 |
200 return stack_depth; | 174 return stack_depth; |
201 } | 175 } |
202 | 176 |
| 177 // NativeStackSamplerWin ------------------------------------------------------ |
| 178 |
203 class NativeStackSamplerWin : public NativeStackSampler { | 179 class NativeStackSamplerWin : public NativeStackSampler { |
204 public: | 180 public: |
205 explicit NativeStackSamplerWin(win::ScopedHandle thread_handle); | 181 explicit NativeStackSamplerWin(win::ScopedHandle thread_handle); |
206 ~NativeStackSamplerWin() override; | 182 ~NativeStackSamplerWin() override; |
207 | 183 |
208 // StackSamplingProfiler::NativeStackSampler: | 184 // StackSamplingProfiler::NativeStackSampler: |
209 void ProfileRecordingStarting( | 185 void ProfileRecordingStarting( |
210 std::vector<StackSamplingProfiler::Module>* modules) override; | 186 std::vector<StackSamplingProfiler::Module>* modules) override; |
211 void RecordStackSample(StackSamplingProfiler::Sample* sample) override; | 187 void RecordStackSample(StackSamplingProfiler::Sample* sample) override; |
212 void ProfileRecordingStopped() override; | 188 void ProfileRecordingStopped() override; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 } | 233 } |
258 | 234 |
259 void NativeStackSamplerWin::RecordStackSample( | 235 void NativeStackSamplerWin::RecordStackSample( |
260 StackSamplingProfiler::Sample* sample) { | 236 StackSamplingProfiler::Sample* sample) { |
261 DCHECK(current_modules_); | 237 DCHECK(current_modules_); |
262 | 238 |
263 const int max_stack_size = 64; | 239 const int max_stack_size = 64; |
264 const void* instruction_pointers[max_stack_size] = {0}; | 240 const void* instruction_pointers[max_stack_size] = {0}; |
265 HMODULE module_handles[max_stack_size] = {0}; | 241 HMODULE module_handles[max_stack_size] = {0}; |
266 | 242 |
267 bool last_frame_is_unknown_function = false; | 243 int stack_depth = SuspendThreadAndRecordStack(thread_handle_.Get(), |
268 int stack_depth = SuspendThreadAndRecordStack( | 244 max_stack_size, |
269 thread_handle_.Get(), max_stack_size, instruction_pointers, | 245 instruction_pointers); |
270 &last_frame_is_unknown_function); | |
271 FindModuleHandlesForAddresses(instruction_pointers, module_handles, | 246 FindModuleHandlesForAddresses(instruction_pointers, module_handles, |
272 stack_depth, last_frame_is_unknown_function); | 247 stack_depth); |
273 CopyToSample(instruction_pointers, module_handles, stack_depth, sample, | 248 CopyToSample(instruction_pointers, module_handles, stack_depth, sample, |
274 current_modules_); | 249 current_modules_); |
275 FreeModuleHandles(stack_depth, module_handles); | 250 FreeModuleHandles(stack_depth, module_handles); |
276 } | 251 } |
277 | 252 |
278 void NativeStackSamplerWin::ProfileRecordingStopped() { | 253 void NativeStackSamplerWin::ProfileRecordingStopped() { |
279 current_modules_ = nullptr; | 254 current_modules_ = nullptr; |
280 } | 255 } |
281 | 256 |
282 // static | 257 // static |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 | 323 |
349 if (thread_handle) { | 324 if (thread_handle) { |
350 return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin( | 325 return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin( |
351 win::ScopedHandle(thread_handle))); | 326 win::ScopedHandle(thread_handle))); |
352 } | 327 } |
353 #endif | 328 #endif |
354 return scoped_ptr<NativeStackSampler>(); | 329 return scoped_ptr<NativeStackSampler>(); |
355 } | 330 } |
356 | 331 |
357 } // namespace base | 332 } // namespace base |
OLD | NEW |