| 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[], | 52 HMODULE module_handles[], |
| 78 int stack_depth, | 53 int stack_depth) { |
| 79 bool last_frame_is_unknown_function) { | 54 for (int i = 0; i < stack_depth; ++i) { |
| 80 const int module_frames = | |
| 81 last_frame_is_unknown_function ? stack_depth - 1 : stack_depth; | |
| 82 for (int i = 0; i < module_frames; ++i) { | |
| 83 HMODULE module_handle = NULL; | 55 HMODULE module_handle = NULL; |
| 84 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | 56 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, |
| 85 reinterpret_cast<LPCTSTR>(addresses[i]), | 57 reinterpret_cast<LPCTSTR>(addresses[i]), |
| 86 &module_handle)) { | 58 &module_handle)) { |
| 87 // HMODULE actually represents the base address of the module, so we can | 59 // HMODULE actually represents the base address of the module, so we can |
| 88 // use it directly as an address. | 60 // use it directly as an address. |
| 89 DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]); | 61 DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]); |
| 90 module_handles[i] = module_handle; | 62 module_handles[i] = module_handle; |
| 91 } | 63 } |
| 92 } | 64 } |
| (...skipping 29 matching lines...) Expand all Loading... |
| 122 std::wstring build_id; | 94 std::wstring build_id; |
| 123 int result = | 95 int result = |
| 124 ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize); | 96 ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize); |
| 125 if (result != kGUIDSize) | 97 if (result != kGUIDSize) |
| 126 return std::string(); | 98 return std::string(); |
| 127 RemoveChars(build_id, L"{}-", &build_id); | 99 RemoveChars(build_id, L"{}-", &build_id); |
| 128 build_id += StringPrintf(L"%d", age); | 100 build_id += StringPrintf(L"%d", age); |
| 129 return WideToUTF8(build_id); | 101 return WideToUTF8(build_id); |
| 130 } | 102 } |
| 131 | 103 |
| 104 // ScopedDisablePriorityBoost ------------------------------------------------- |
| 105 |
| 132 // Disables priority boost on a thread for the lifetime of the object. | 106 // Disables priority boost on a thread for the lifetime of the object. |
| 133 class ScopedDisablePriorityBoost { | 107 class ScopedDisablePriorityBoost { |
| 134 public: | 108 public: |
| 135 ScopedDisablePriorityBoost(HANDLE thread_handle); | 109 ScopedDisablePriorityBoost(HANDLE thread_handle); |
| 136 ~ScopedDisablePriorityBoost(); | 110 ~ScopedDisablePriorityBoost(); |
| 137 | 111 |
| 138 private: | 112 private: |
| 139 HANDLE thread_handle_; | 113 HANDLE thread_handle_; |
| 140 BOOL got_previous_boost_state_; | 114 BOOL got_previous_boost_state_; |
| 141 BOOL boost_state_was_disabled_; | 115 BOOL boost_state_was_disabled_; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 164 // |instruction_pointers|, then resumes the thread. Returns the size of the | 138 // |instruction_pointers|, then resumes the thread. Returns the size of the |
| 165 // stack. | 139 // stack. |
| 166 // | 140 // |
| 167 // IMPORTANT NOTE: No heap allocations may occur between SuspendThread and | 141 // IMPORTANT NOTE: No heap allocations may occur between SuspendThread and |
| 168 // ResumeThread. Otherwise this code can deadlock on heap locks acquired by the | 142 // ResumeThread. Otherwise this code can deadlock on heap locks acquired by the |
| 169 // target thread before it was suspended. This is why we pass instruction | 143 // target thread before it was suspended. This is why we pass instruction |
| 170 // pointers and module handles as preallocated arrays rather than vectors, since | 144 // pointers and module handles as preallocated arrays rather than vectors, since |
| 171 // vectors make it too easy to subtly allocate memory. | 145 // vectors make it too easy to subtly allocate memory. |
| 172 int SuspendThreadAndRecordStack(HANDLE thread_handle, | 146 int SuspendThreadAndRecordStack(HANDLE thread_handle, |
| 173 int max_stack_size, | 147 int max_stack_size, |
| 174 const void* instruction_pointers[], | 148 const void* instruction_pointers[]) { |
| 175 bool* last_frame_is_unknown_function) { | 149 Win32StackFrameUnwinder frame_unwinder; |
| 150 |
| 176 if (::SuspendThread(thread_handle) == -1) | 151 if (::SuspendThread(thread_handle) == -1) |
| 177 return 0; | 152 return 0; |
| 178 | 153 |
| 179 int stack_depth = 0; | 154 int stack_depth = 0; |
| 180 CONTEXT thread_context = {0}; | 155 CONTEXT thread_context = {0}; |
| 181 thread_context.ContextFlags = CONTEXT_FULL; | 156 thread_context.ContextFlags = CONTEXT_FULL; |
| 182 if (::GetThreadContext(thread_handle, &thread_context)) { | 157 if (::GetThreadContext(thread_handle, &thread_context)) { |
| 183 stack_depth = | 158 stack_depth = RecordStack(&thread_context, max_stack_size, |
| 184 RecordStack(&thread_context, max_stack_size, instruction_pointers, | 159 instruction_pointers, &frame_unwinder); |
| 185 last_frame_is_unknown_function); | |
| 186 } | 160 } |
| 187 | 161 |
| 188 // Disable the priority boost that the thread would otherwise receive on | 162 // Disable the priority boost that the thread would otherwise receive on |
| 189 // resume. We do this to avoid artificially altering the dynamics of the | 163 // resume. We do this to avoid artificially altering the dynamics of the |
| 190 // executing application any more than we already are by suspending and | 164 // executing application any more than we already are by suspending and |
| 191 // resuming the thread. | 165 // resuming the thread. |
| 192 // | 166 // |
| 193 // Note that this can racily disable a priority boost that otherwise would | 167 // Note that this can racily disable a priority boost that otherwise would |
| 194 // have been given to the thread, if the thread is waiting on other wait | 168 // have been given to the thread, if the thread is waiting on other wait |
| 195 // conditions at the time of SuspendThread and those conditions are satisfied | 169 // conditions at the time of SuspendThread and those conditions are satisfied |
| 196 // before priority boost is reenabled. The measured length of this window is | 170 // before priority boost is reenabled. The measured length of this window is |
| 197 // ~100us, so this should occur fairly rarely. | 171 // ~100us, so this should occur fairly rarely. |
| 198 ScopedDisablePriorityBoost disable_priority_boost(thread_handle); | 172 ScopedDisablePriorityBoost disable_priority_boost(thread_handle); |
| 199 bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1; | 173 bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1; |
| 200 CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError(); | 174 CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError(); |
| 201 | 175 |
| 202 return stack_depth; | 176 return stack_depth; |
| 203 } | 177 } |
| 204 | 178 |
| 179 // NativeStackSamplerWin ------------------------------------------------------ |
| 180 |
| 205 class NativeStackSamplerWin : public NativeStackSampler { | 181 class NativeStackSamplerWin : public NativeStackSampler { |
| 206 public: | 182 public: |
| 207 explicit NativeStackSamplerWin(win::ScopedHandle thread_handle); | 183 explicit NativeStackSamplerWin(win::ScopedHandle thread_handle); |
| 208 ~NativeStackSamplerWin() override; | 184 ~NativeStackSamplerWin() override; |
| 209 | 185 |
| 210 // StackSamplingProfiler::NativeStackSampler: | 186 // StackSamplingProfiler::NativeStackSampler: |
| 211 void ProfileRecordingStarting( | 187 void ProfileRecordingStarting( |
| 212 std::vector<StackSamplingProfiler::Module>* modules) override; | 188 std::vector<StackSamplingProfiler::Module>* modules) override; |
| 213 void RecordStackSample(StackSamplingProfiler::Sample* sample) override; | 189 void RecordStackSample(StackSamplingProfiler::Sample* sample) override; |
| 214 void ProfileRecordingStopped() override; | 190 void ProfileRecordingStopped() override; |
| (...skipping 42 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; | |
| 268 int stack_depth = SuspendThreadAndRecordStack( | 243 int stack_depth = SuspendThreadAndRecordStack( |
| 269 thread_handle_.Get(), max_stack_size, instruction_pointers, | 244 thread_handle_.Get(), max_stack_size, instruction_pointers); |
| 270 &last_frame_is_unknown_function); | |
| 271 FindModuleHandlesForAddresses(instruction_pointers, module_handles, | 245 FindModuleHandlesForAddresses(instruction_pointers, module_handles, |
| 272 stack_depth, last_frame_is_unknown_function); | 246 stack_depth); |
| 273 CopyToSample(instruction_pointers, module_handles, stack_depth, sample, | 247 CopyToSample(instruction_pointers, module_handles, stack_depth, sample, |
| 274 current_modules_); | 248 current_modules_); |
| 275 FreeModuleHandles(stack_depth, module_handles); | 249 FreeModuleHandles(stack_depth, module_handles); |
| 276 } | 250 } |
| 277 | 251 |
| 278 void NativeStackSamplerWin::ProfileRecordingStopped() { | 252 void NativeStackSamplerWin::ProfileRecordingStopped() { |
| 279 current_modules_ = nullptr; | 253 current_modules_ = nullptr; |
| 280 } | 254 } |
| 281 | 255 |
| 282 // static | 256 // static |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 347 | 321 |
| 348 if (thread_handle) { | 322 if (thread_handle) { |
| 349 return scoped_ptr<NativeStackSampler>( | 323 return scoped_ptr<NativeStackSampler>( |
| 350 new NativeStackSamplerWin(win::ScopedHandle(thread_handle))); | 324 new NativeStackSamplerWin(win::ScopedHandle(thread_handle))); |
| 351 } | 325 } |
| 352 #endif | 326 #endif |
| 353 return scoped_ptr<NativeStackSampler>(); | 327 return scoped_ptr<NativeStackSampler>(); |
| 354 } | 328 } |
| 355 | 329 |
| 356 } // namespace base | 330 } // namespace base |
| OLD | NEW |