Chromium Code Reviews| 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 #include <winternl.h> | 7 #include <winternl.h> |
| 8 | 8 |
| 9 #include <cstdlib> | 9 #include <cstdlib> |
| 10 #include <map> | 10 #include <map> |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 133 const void** end = reinterpret_cast<const void**>( | 133 const void** end = reinterpret_cast<const void**>( |
| 134 reinterpret_cast<char*>(stack_copy) + (top - bottom)); | 134 reinterpret_cast<char*>(stack_copy) + (top - bottom)); |
| 135 for (const void** loc = start; loc < end; ++loc) | 135 for (const void** loc = start; loc < end; ++loc) |
| 136 RewritePointerIfInOriginalStack(top, bottom, stack_copy, loc); | 136 RewritePointerIfInOriginalStack(top, bottom, stack_copy, loc); |
| 137 #endif | 137 #endif |
| 138 } | 138 } |
| 139 | 139 |
| 140 // Walks the stack represented by |context| from the current frame downwards, | 140 // Walks the stack represented by |context| from the current frame downwards, |
| 141 // recording the instruction pointers for each frame in |instruction_pointers|. | 141 // recording the instruction pointers for each frame in |instruction_pointers|. |
| 142 int RecordStack(CONTEXT* context, int max_stack_size, | 142 int RecordStack(CONTEXT* context, int max_stack_size, |
| 143 const void* instruction_pointers[]) { | 143 const void* instruction_pointers[], |
| 144 ScopedModuleHandle modules[]) { | |
|
brucedawson
2015/10/30 00:15:59
Having array function parameters is frustrating be
Mike Wittman
2015/10/30 17:08:07
That's a nice alternative, but unfortunately it wo
| |
| 144 #ifdef _WIN64 | 145 #ifdef _WIN64 |
| 145 Win32StackFrameUnwinder frame_unwinder; | 146 Win32StackFrameUnwinder frame_unwinder; |
| 146 int i = 0; | 147 int i = 0; |
| 147 for (; (i < max_stack_size) && context->Rip; ++i) { | 148 for (; (i < max_stack_size) && context->Rip; ++i) { |
| 148 instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip); | 149 instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip); |
| 149 if (!frame_unwinder.TryUnwind(context)) | 150 if (!frame_unwinder.TryUnwind(context, &modules[i])) |
| 150 return i + 1; | 151 return i + 1; |
| 151 } | 152 } |
| 152 return i; | 153 return i; |
| 153 #else | 154 #else |
| 154 return 0; | 155 return 0; |
| 155 #endif | 156 #endif |
| 156 } | 157 } |
| 157 | 158 |
| 158 // Fills in |module_handles| corresponding to the pointers to code in | |
| 159 // |addresses|. The module handles are returned with reference counts | |
| 160 // incremented and should be freed with FreeModuleHandles. See note in | |
| 161 // SuspendThreadAndRecordStack for why |addresses| and |module_handles| are | |
| 162 // arrays. | |
| 163 void FindModuleHandlesForAddresses(const void* const addresses[], | |
| 164 HMODULE module_handles[], int stack_depth) { | |
| 165 for (int i = 0; i < stack_depth; ++i) { | |
| 166 HMODULE module_handle = NULL; | |
| 167 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | |
| 168 reinterpret_cast<LPCTSTR>(addresses[i]), | |
| 169 &module_handle)) { | |
| 170 // HMODULE actually represents the base address of the module, so we can | |
| 171 // use it directly as an address. | |
| 172 DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]); | |
| 173 module_handles[i] = module_handle; | |
| 174 } | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 // Frees the modules handles returned by FindModuleHandlesForAddresses. See note | |
| 179 // in SuspendThreadAndRecordStack for why |module_handles| is an array. | |
| 180 void FreeModuleHandles(int stack_depth, HMODULE module_handles[]) { | |
| 181 for (int i = 0; i < stack_depth; ++i) { | |
| 182 if (module_handles[i]) | |
| 183 ::FreeLibrary(module_handles[i]); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 // Gets the unique build ID for a module. Windows build IDs are created by a | 159 // Gets the unique build ID for a module. Windows build IDs are created by a |
| 188 // concatenation of a GUID and AGE fields found in the headers of a module. The | 160 // concatenation of a GUID and AGE fields found in the headers of a module. The |
| 189 // GUID is stored in the first 16 bytes and the AGE is stored in the last 4 | 161 // GUID is stored in the first 16 bytes and the AGE is stored in the last 4 |
| 190 // bytes. Returns the empty string if the function fails to get the build ID. | 162 // bytes. Returns the empty string if the function fails to get the build ID. |
| 191 // | 163 // |
| 192 // Example: | 164 // Example: |
| 193 // dumpbin chrome.exe /headers | find "Format:" | 165 // dumpbin chrome.exe /headers | find "Format:" |
| 194 // ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ... | 166 // ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ... |
| 195 // | 167 // |
| 196 // The resulting buildID string of this instance of chrome.exe is | 168 // The resulting buildID string of this instance of chrome.exe is |
| 197 // "16B2A4281DED442E9A36FCE8CBD2972610". | 169 // "16B2A4281DED442E9A36FCE8CBD2972610". |
| 198 // | 170 // |
| 199 // Note that the AGE field is encoded in decimal, not hex. | 171 // Note that the AGE field is encoded in decimal, not hex. |
|
brucedawson
2015/10/30 00:15:59
Unless you are using it for symbol server purposes
Mike Wittman
2015/10/30 17:08:07
Yeah, it needs to be this way to match the server-
| |
| 200 std::string GetBuildIDForModule(HMODULE module_handle) { | 172 std::string GetBuildIDForModule(HMODULE module_handle) { |
| 201 GUID guid; | 173 GUID guid; |
| 202 DWORD age; | 174 DWORD age; |
| 203 win::PEImage(module_handle).GetDebugId(&guid, &age); | 175 win::PEImage(module_handle).GetDebugId(&guid, &age); |
| 204 const int kGUIDSize = 39; | 176 const int kGUIDSize = 39; |
| 205 std::wstring build_id; | 177 std::wstring build_id; |
| 206 int result = | 178 int result = |
| 207 ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize); | 179 ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize); |
| 208 if (result != kGUIDSize) | 180 if (result != kGUIDSize) |
| 209 return std::string(); | 181 return std::string(); |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 293 // IMPORTANT NOTE: No allocations from the default heap may occur in the | 265 // IMPORTANT NOTE: No allocations from the default heap may occur in the |
| 294 // ScopedSuspendThread scope, including indirectly via use of DCHECK/CHECK or | 266 // ScopedSuspendThread scope, including indirectly via use of DCHECK/CHECK or |
| 295 // other logging statements. Otherwise this code can deadlock on heap locks in | 267 // other logging statements. Otherwise this code can deadlock on heap locks in |
| 296 // the default heap acquired by the target thread before it was suspended. This | 268 // the default heap acquired by the target thread before it was suspended. This |
| 297 // is why we pass instruction pointers as preallocated arrays. | 269 // is why we pass instruction pointers as preallocated arrays. |
| 298 int SuspendThreadAndRecordStack(HANDLE thread_handle, | 270 int SuspendThreadAndRecordStack(HANDLE thread_handle, |
| 299 const void* base_address, | 271 const void* base_address, |
| 300 void* stack_copy_buffer, | 272 void* stack_copy_buffer, |
| 301 size_t stack_copy_buffer_size, | 273 size_t stack_copy_buffer_size, |
| 302 int max_stack_size, | 274 int max_stack_size, |
| 303 const void* instruction_pointers[]) { | 275 const void* instruction_pointers[], |
| 276 ScopedModuleHandle modules[], | |
| 277 NativeStackSamplerTestDelegate* test_delegate) { | |
| 304 CONTEXT thread_context = {0}; | 278 CONTEXT thread_context = {0}; |
| 305 thread_context.ContextFlags = CONTEXT_FULL; | 279 thread_context.ContextFlags = CONTEXT_FULL; |
| 306 // The stack bounds are saved to uintptr_ts for use outside | 280 // The stack bounds are saved to uintptr_ts for use outside |
| 307 // ScopedSuspendThread, as the thread's memory is not safe to dereference | 281 // ScopedSuspendThread, as the thread's memory is not safe to dereference |
| 308 // beyond that point. | 282 // beyond that point. |
| 309 const uintptr_t top = reinterpret_cast<uintptr_t>(base_address); | 283 const uintptr_t top = reinterpret_cast<uintptr_t>(base_address); |
| 310 uintptr_t bottom = 0u; | 284 uintptr_t bottom = 0u; |
| 311 | 285 |
| 312 { | 286 { |
| 313 ScopedSuspendThread suspend_thread(thread_handle); | 287 ScopedSuspendThread suspend_thread(thread_handle); |
| 314 | 288 |
| 315 if (!suspend_thread.was_successful()) | 289 if (!suspend_thread.was_successful()) |
| 316 return 0; | 290 return 0; |
| 317 | 291 |
| 318 if (!::GetThreadContext(thread_handle, &thread_context)) | 292 if (!::GetThreadContext(thread_handle, &thread_context)) |
| 319 return 0; | 293 return 0; |
| 320 #if defined(_WIN64) | 294 #if defined(_WIN64) |
| 321 bottom = thread_context.Rsp; | 295 bottom = thread_context.Rsp; |
| 322 #else | 296 #else |
| 323 bottom = thread_context.Esp; | 297 bottom = thread_context.Esp; |
| 324 #endif | 298 #endif |
| 325 | 299 |
| 326 if ((top - bottom) > stack_copy_buffer_size) | 300 if ((top - bottom) > stack_copy_buffer_size) |
| 327 return 0; | 301 return 0; |
| 328 | 302 |
| 329 std::memcpy(stack_copy_buffer, reinterpret_cast<const void*>(bottom), | 303 std::memcpy(stack_copy_buffer, reinterpret_cast<const void*>(bottom), |
| 330 top - bottom); | 304 top - bottom); |
| 331 } | 305 } |
| 332 | 306 |
| 307 if (test_delegate) | |
| 308 test_delegate->OnPreStackWalk(); | |
| 309 | |
| 333 RewritePointersToStackMemory(top, bottom, &thread_context, stack_copy_buffer); | 310 RewritePointersToStackMemory(top, bottom, &thread_context, stack_copy_buffer); |
| 334 | 311 |
| 335 return RecordStack(&thread_context, max_stack_size, instruction_pointers); | 312 return RecordStack(&thread_context, max_stack_size, instruction_pointers, |
| 313 modules); | |
| 336 } | 314 } |
| 337 | 315 |
| 338 // NativeStackSamplerWin ------------------------------------------------------ | 316 // NativeStackSamplerWin ------------------------------------------------------ |
| 339 | 317 |
| 340 class NativeStackSamplerWin : public NativeStackSampler { | 318 class NativeStackSamplerWin : public NativeStackSampler { |
| 341 public: | 319 public: |
| 342 explicit NativeStackSamplerWin(win::ScopedHandle thread_handle); | 320 NativeStackSamplerWin(win::ScopedHandle thread_handle, |
| 321 NativeStackSamplerTestDelegate* test_delegate); | |
| 343 ~NativeStackSamplerWin() override; | 322 ~NativeStackSamplerWin() override; |
| 344 | 323 |
| 345 // StackSamplingProfiler::NativeStackSampler: | 324 // StackSamplingProfiler::NativeStackSampler: |
| 346 void ProfileRecordingStarting( | 325 void ProfileRecordingStarting( |
| 347 std::vector<StackSamplingProfiler::Module>* modules) override; | 326 std::vector<StackSamplingProfiler::Module>* modules) override; |
| 348 void RecordStackSample(StackSamplingProfiler::Sample* sample) override; | 327 void RecordStackSample(StackSamplingProfiler::Sample* sample) override; |
| 349 void ProfileRecordingStopped() override; | 328 void ProfileRecordingStopped() override; |
| 350 | 329 |
| 351 private: | 330 private: |
| 352 enum { | 331 enum { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 366 // Gets the index for the Module corresponding to |module_handle| in | 345 // Gets the index for the Module corresponding to |module_handle| in |
| 367 // |modules|, adding it if it's not already present. Returns | 346 // |modules|, adding it if it's not already present. Returns |
| 368 // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be | 347 // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be |
| 369 // determined for |module|. | 348 // determined for |module|. |
| 370 size_t GetModuleIndex(HMODULE module_handle, | 349 size_t GetModuleIndex(HMODULE module_handle, |
| 371 std::vector<StackSamplingProfiler::Module>* modules); | 350 std::vector<StackSamplingProfiler::Module>* modules); |
| 372 | 351 |
| 373 // Copies the stack information represented by |instruction_pointers| into | 352 // Copies the stack information represented by |instruction_pointers| into |
| 374 // |sample| and |modules|. | 353 // |sample| and |modules|. |
| 375 void CopyToSample(const void* const instruction_pointers[], | 354 void CopyToSample(const void* const instruction_pointers[], |
| 376 const HMODULE module_handles[], | 355 const ScopedModuleHandle module_handles[], |
| 377 int stack_depth, | 356 int stack_depth, |
| 378 StackSamplingProfiler::Sample* sample, | 357 StackSamplingProfiler::Sample* sample, |
| 379 std::vector<StackSamplingProfiler::Module>* modules); | 358 std::vector<StackSamplingProfiler::Module>* modules); |
| 380 | 359 |
| 381 win::ScopedHandle thread_handle_; | 360 win::ScopedHandle thread_handle_; |
| 382 | 361 |
| 362 NativeStackSamplerTestDelegate* const test_delegate_; | |
| 363 | |
| 383 // The stack base address corresponding to |thread_handle_|. | 364 // The stack base address corresponding to |thread_handle_|. |
| 384 const void* const thread_stack_base_address_; | 365 const void* const thread_stack_base_address_; |
| 385 | 366 |
| 386 // Buffer to use for copies of the stack. We use the same buffer for all the | 367 // Buffer to use for copies of the stack. We use the same buffer for all the |
| 387 // samples to avoid the overhead of multiple allocations and frees. | 368 // samples to avoid the overhead of multiple allocations and frees. |
| 388 const scoped_ptr<unsigned char[]> stack_copy_buffer_; | 369 const scoped_ptr<unsigned char[]> stack_copy_buffer_; |
| 389 | 370 |
| 390 // Weak. Points to the modules associated with the profile being recorded | 371 // Weak. Points to the modules associated with the profile being recorded |
| 391 // between ProfileRecordingStarting() and ProfileRecordingStopped(). | 372 // between ProfileRecordingStarting() and ProfileRecordingStopped(). |
| 392 std::vector<StackSamplingProfiler::Module>* current_modules_; | 373 std::vector<StackSamplingProfiler::Module>* current_modules_; |
| 393 | 374 |
| 394 // Maps a module handle to the corresponding Module's index within | 375 // Maps a module handle to the corresponding Module's index within |
| 395 // current_modules_. | 376 // current_modules_. |
| 396 std::map<HMODULE, size_t> profile_module_index_; | 377 std::map<HMODULE, size_t> profile_module_index_; |
| 397 | 378 |
| 398 DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin); | 379 DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin); |
| 399 }; | 380 }; |
| 400 | 381 |
| 401 NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle) | 382 NativeStackSamplerWin::NativeStackSamplerWin( |
| 402 : thread_handle_(thread_handle.Take()), | 383 win::ScopedHandle thread_handle, |
| 384 NativeStackSamplerTestDelegate* test_delegate) | |
| 385 : thread_handle_(thread_handle.Take()), test_delegate_(test_delegate), | |
| 403 thread_stack_base_address_( | 386 thread_stack_base_address_( |
| 404 GetThreadEnvironmentBlock(thread_handle_.Get())->Tib.StackBase), | 387 GetThreadEnvironmentBlock(thread_handle_.Get())->Tib.StackBase), |
| 405 stack_copy_buffer_(new unsigned char[kStackCopyBufferSize]) { | 388 stack_copy_buffer_(new unsigned char[kStackCopyBufferSize]) { |
| 406 } | 389 } |
| 407 | 390 |
| 408 NativeStackSamplerWin::~NativeStackSamplerWin() { | 391 NativeStackSamplerWin::~NativeStackSamplerWin() { |
| 409 } | 392 } |
| 410 | 393 |
| 411 void NativeStackSamplerWin::ProfileRecordingStarting( | 394 void NativeStackSamplerWin::ProfileRecordingStarting( |
| 412 std::vector<StackSamplingProfiler::Module>* modules) { | 395 std::vector<StackSamplingProfiler::Module>* modules) { |
| 413 current_modules_ = modules; | 396 current_modules_ = modules; |
| 414 profile_module_index_.clear(); | 397 profile_module_index_.clear(); |
| 415 } | 398 } |
| 416 | 399 |
| 417 void NativeStackSamplerWin::RecordStackSample( | 400 void NativeStackSamplerWin::RecordStackSample( |
| 418 StackSamplingProfiler::Sample* sample) { | 401 StackSamplingProfiler::Sample* sample) { |
| 419 DCHECK(current_modules_); | 402 DCHECK(current_modules_); |
| 420 | 403 |
| 421 if (!stack_copy_buffer_) | 404 if (!stack_copy_buffer_) |
| 422 return; | 405 return; |
| 423 | 406 |
| 424 const int max_stack_size = 64; | 407 const int max_stack_size = 64; |
| 425 const void* instruction_pointers[max_stack_size] = {0}; | 408 const void* instruction_pointers[max_stack_size] = {0}; |
| 426 HMODULE module_handles[max_stack_size] = {0}; | 409 ScopedModuleHandle modules[max_stack_size]; |
| 427 | 410 |
| 428 int stack_depth = SuspendThreadAndRecordStack(thread_handle_.Get(), | 411 int stack_depth = SuspendThreadAndRecordStack(thread_handle_.Get(), |
| 429 thread_stack_base_address_, | 412 thread_stack_base_address_, |
| 430 stack_copy_buffer_.get(), | 413 stack_copy_buffer_.get(), |
| 431 kStackCopyBufferSize, | 414 kStackCopyBufferSize, |
| 432 max_stack_size, | 415 max_stack_size, |
| 433 instruction_pointers); | 416 instruction_pointers, |
| 434 FindModuleHandlesForAddresses(instruction_pointers, module_handles, | 417 modules, |
| 435 stack_depth); | 418 test_delegate_); |
| 436 CopyToSample(instruction_pointers, module_handles, stack_depth, sample, | 419 CopyToSample(instruction_pointers, modules, stack_depth, sample, |
| 437 current_modules_); | 420 current_modules_); |
| 438 FreeModuleHandles(stack_depth, module_handles); | |
| 439 } | 421 } |
| 440 | 422 |
| 441 void NativeStackSamplerWin::ProfileRecordingStopped() { | 423 void NativeStackSamplerWin::ProfileRecordingStopped() { |
| 442 current_modules_ = nullptr; | 424 current_modules_ = nullptr; |
| 443 } | 425 } |
| 444 | 426 |
| 445 // static | 427 // static |
| 446 bool NativeStackSamplerWin::GetModuleForHandle( | 428 bool NativeStackSamplerWin::GetModuleForHandle( |
| 447 HMODULE module_handle, | 429 HMODULE module_handle, |
| 448 StackSamplingProfiler::Module* module) { | 430 StackSamplingProfiler::Module* module) { |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 477 modules->push_back(module); | 459 modules->push_back(module); |
| 478 loc = profile_module_index_.insert(std::make_pair( | 460 loc = profile_module_index_.insert(std::make_pair( |
| 479 module_handle, modules->size() - 1)).first; | 461 module_handle, modules->size() - 1)).first; |
| 480 } | 462 } |
| 481 | 463 |
| 482 return loc->second; | 464 return loc->second; |
| 483 } | 465 } |
| 484 | 466 |
| 485 void NativeStackSamplerWin::CopyToSample( | 467 void NativeStackSamplerWin::CopyToSample( |
| 486 const void* const instruction_pointers[], | 468 const void* const instruction_pointers[], |
| 487 const HMODULE module_handles[], | 469 const ScopedModuleHandle module_handles[], |
| 488 int stack_depth, | 470 int stack_depth, |
| 489 StackSamplingProfiler::Sample* sample, | 471 StackSamplingProfiler::Sample* sample, |
| 490 std::vector<StackSamplingProfiler::Module>* module) { | 472 std::vector<StackSamplingProfiler::Module>* modules) { |
| 491 sample->clear(); | 473 sample->clear(); |
| 492 sample->reserve(stack_depth); | 474 sample->reserve(stack_depth); |
| 493 | 475 |
| 494 for (int i = 0; i < stack_depth; ++i) { | 476 for (int i = 0; i < stack_depth; ++i) { |
| 495 sample->push_back(StackSamplingProfiler::Frame( | 477 sample->push_back(StackSamplingProfiler::Frame( |
| 496 reinterpret_cast<uintptr_t>(instruction_pointers[i]), | 478 reinterpret_cast<uintptr_t>(instruction_pointers[i]), |
| 497 GetModuleIndex(module_handles[i], module))); | 479 GetModuleIndex(module_handles[i].Get(), modules))); |
| 498 } | 480 } |
| 499 } | 481 } |
| 500 | 482 |
| 501 } // namespace | 483 } // namespace |
| 502 | 484 |
| 503 scoped_ptr<NativeStackSampler> NativeStackSampler::Create( | 485 scoped_ptr<NativeStackSampler> NativeStackSampler::Create( |
| 504 PlatformThreadId thread_id) { | 486 PlatformThreadId thread_id, |
| 487 NativeStackSamplerTestDelegate* test_delegate) { | |
| 505 #if _WIN64 | 488 #if _WIN64 |
| 506 // Get the thread's handle. | 489 // Get the thread's handle. |
| 507 HANDLE thread_handle = ::OpenThread( | 490 HANDLE thread_handle = ::OpenThread( |
| 508 THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, | 491 THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, |
| 509 FALSE, | 492 FALSE, |
| 510 thread_id); | 493 thread_id); |
| 511 | 494 |
| 512 if (thread_handle) { | 495 if (thread_handle) { |
| 513 return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin( | 496 return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin( |
| 514 win::ScopedHandle(thread_handle))); | 497 win::ScopedHandle(thread_handle), |
| 498 test_delegate)); | |
| 515 } | 499 } |
| 516 #endif | 500 #endif |
| 517 return scoped_ptr<NativeStackSampler>(); | 501 return scoped_ptr<NativeStackSampler>(); |
| 518 } | 502 } |
| 519 | 503 |
| 520 } // namespace base | 504 } // namespace base |
| OLD | NEW |