OLD | NEW |
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ | 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 /* This Source Code Form is subject to the terms of the Mozilla Public | 3 /* This Source Code Form is subject to the terms of the Mozilla Public |
4 * License, v. 2.0. If a copy of the MPL was not distributed with this | 4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | 6 |
7 #include "ThreadStackHelper.h" | 7 #include "ThreadStackHelper.h" |
8 #include "MainThreadUtils.h" | 8 #include "MainThreadUtils.h" |
9 #include "nsJSPrincipals.h" | 9 #include "nsJSPrincipals.h" |
10 #include "nsScriptSecurityManager.h" | 10 #include "nsScriptSecurityManager.h" |
11 #include "jsfriendapi.h" | 11 #include "jsfriendapi.h" |
12 #include "prprf.h" | |
13 #ifdef MOZ_THREADSTACKHELPER_NATIVE | 12 #ifdef MOZ_THREADSTACKHELPER_NATIVE |
14 #include "shared-libraries.h" | 13 #include "shared-libraries.h" |
15 #endif | 14 #endif |
16 | 15 |
17 #include "mozilla/Assertions.h" | 16 #include "mozilla/Assertions.h" |
18 #include "mozilla/Attributes.h" | 17 #include "mozilla/Attributes.h" |
19 #include "mozilla/IntegerPrintfMacros.h" | 18 #include "mozilla/IntegerPrintfMacros.h" |
20 #include "mozilla/Move.h" | 19 #include "mozilla/Move.h" |
21 #include "mozilla/Scoped.h" | 20 #include "mozilla/Scoped.h" |
22 #include "mozilla/UniquePtr.h" | 21 #include "mozilla/UniquePtr.h" |
23 #include "mozilla/MemoryChecking.h" | 22 #include "mozilla/MemoryChecking.h" |
| 23 #include "mozilla/Snprintf.h" |
24 | 24 |
25 #ifdef MOZ_THREADSTACKHELPER_NATIVE | 25 #ifdef __GNUC__ |
26 #include "google_breakpad/processor/call_stack.h" | 26 # pragma GCC diagnostic push |
27 #include "google_breakpad/processor/basic_source_line_resolver.h" | 27 # pragma GCC diagnostic ignored "-Wshadow" |
28 #include "google_breakpad/processor/stack_frame_cpu.h" | |
29 #include "processor/basic_code_module.h" | |
30 #include "processor/basic_code_modules.h" | |
31 #endif | |
32 | |
33 #if defined(MOZ_THREADSTACKHELPER_X86) | |
34 #include "processor/stackwalker_x86.h" | |
35 #elif defined(MOZ_THREADSTACKHELPER_X64) | |
36 #include "processor/stackwalker_amd64.h" | |
37 #elif defined(MOZ_THREADSTACKHELPER_ARM) | |
38 #include "processor/stackwalker_arm.h" | |
39 #endif | 28 #endif |
40 | 29 |
41 #if defined(MOZ_VALGRIND) | 30 #if defined(MOZ_VALGRIND) |
42 # include <valgrind/valgrind.h> | 31 # include <valgrind/valgrind.h> |
43 #endif | 32 #endif |
44 | 33 |
45 #include <string.h> | 34 #include <string.h> |
46 #include <vector> | 35 #include <vector> |
| 36 #include <cstdlib> |
47 | 37 |
48 #ifdef XP_LINUX | 38 #ifdef XP_LINUX |
49 #ifdef ANDROID | 39 #ifdef ANDROID |
50 // Android NDK doesn't contain ucontext.h; use Breakpad's copy. | 40 // Android NDK doesn't contain ucontext.h; use Breakpad's copy. |
51 # include "common/android/include/sys/ucontext.h" | 41 # include "common/android/include/sys/ucontext.h" |
52 #else | 42 #else |
53 # include <ucontext.h> | 43 # include <ucontext.h> |
54 #endif | 44 #endif |
55 #include <unistd.h> | 45 #include <unistd.h> |
56 #include <sys/syscall.h> | 46 #include <sys/syscall.h> |
57 #endif | 47 #endif |
58 | 48 |
| 49 #ifdef __GNUC__ |
| 50 # pragma GCC diagnostic pop // -Wshadow |
| 51 #endif |
| 52 |
59 #if defined(XP_LINUX) || defined(XP_MACOSX) | 53 #if defined(XP_LINUX) || defined(XP_MACOSX) |
60 #include <pthread.h> | 54 #include <pthread.h> |
61 #endif | 55 #endif |
62 | 56 |
63 #ifdef ANDROID | 57 #ifdef ANDROID |
64 #ifndef SYS_gettid | 58 #ifndef SYS_gettid |
65 #define SYS_gettid __NR_gettid | 59 #define SYS_gettid __NR_gettid |
66 #endif | 60 #endif |
67 #if defined(__arm__) && !defined(__NR_rt_tgsigqueueinfo) | 61 #if defined(__arm__) && !defined(__NR_rt_tgsigqueueinfo) |
68 // Some NDKs don't define this constant even though the kernel supports it. | 62 // Some NDKs don't define this constant even though the kernel supports it. |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
209 namespace { | 203 namespace { |
210 template<typename T> | 204 template<typename T> |
211 class ScopedSetPtr | 205 class ScopedSetPtr |
212 { | 206 { |
213 private: | 207 private: |
214 T*& mPtr; | 208 T*& mPtr; |
215 public: | 209 public: |
216 ScopedSetPtr(T*& p, T* val) : mPtr(p) { mPtr = val; } | 210 ScopedSetPtr(T*& p, T* val) : mPtr(p) { mPtr = val; } |
217 ~ScopedSetPtr() { mPtr = nullptr; } | 211 ~ScopedSetPtr() { mPtr = nullptr; } |
218 }; | 212 }; |
219 } | 213 } // namespace |
220 | 214 |
221 void | 215 void |
222 ThreadStackHelper::GetStack(Stack& aStack) | 216 ThreadStackHelper::GetStack(Stack& aStack) |
223 { | 217 { |
224 // Always run PrepareStackBuffer first to clear aStack | 218 // Always run PrepareStackBuffer first to clear aStack |
225 if (!PrepareStackBuffer(aStack)) { | 219 if (!PrepareStackBuffer(aStack)) { |
226 // Skip and return empty aStack | 220 // Skip and return empty aStack |
227 return; | 221 return; |
228 } | 222 } |
229 | 223 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 | 273 |
280 FillStackBuffer(); | 274 FillStackBuffer(); |
281 FillThreadContext(); | 275 FillThreadContext(); |
282 | 276 |
283 MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS); | 277 MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS); |
284 | 278 |
285 #endif | 279 #endif |
286 } | 280 } |
287 | 281 |
288 #ifdef MOZ_THREADSTACKHELPER_NATIVE | 282 #ifdef MOZ_THREADSTACKHELPER_NATIVE |
289 class ThreadStackHelper::CodeModulesProvider | |
290 : public google_breakpad::CodeModules | |
291 { | |
292 private: | |
293 typedef google_breakpad::CodeModule CodeModule; | |
294 typedef google_breakpad::BasicCodeModule BasicCodeModule; | |
295 | |
296 const SharedLibraryInfo mLibs; | |
297 mutable ScopedDeletePtr<BasicCodeModule> mModule; | |
298 | |
299 public: | |
300 CodeModulesProvider() : mLibs(SharedLibraryInfo::GetInfoForSelf()) {} | |
301 virtual ~CodeModulesProvider() {} | |
302 | |
303 virtual unsigned int module_count() const | |
304 { | |
305 return mLibs.GetSize(); | |
306 } | |
307 | |
308 virtual const CodeModule* GetModuleForAddress(uint64_t aAddress) const | |
309 { | |
310 MOZ_CRASH("Not implemented"); | |
311 } | |
312 | |
313 virtual const CodeModule* GetMainModule() const | |
314 { | |
315 return nullptr; | |
316 } | |
317 | |
318 virtual const CodeModule* GetModuleAtSequence(unsigned int aSequence) const | |
319 { | |
320 MOZ_CRASH("Not implemented"); | |
321 } | |
322 | |
323 virtual const CodeModule* GetModuleAtIndex(unsigned int aIndex) const | |
324 { | |
325 const SharedLibrary& lib = mLibs.GetEntry(aIndex); | |
326 mModule = new BasicCodeModule(lib.GetStart(), lib.GetEnd() - lib.GetStart(), | |
327 lib.GetName(), lib.GetBreakpadId(), | |
328 lib.GetName(), lib.GetBreakpadId(), ""); | |
329 // Keep mModule valid until the next GetModuleAtIndex call. | |
330 return mModule; | |
331 } | |
332 | |
333 virtual const CodeModules* Copy() const | |
334 { | |
335 MOZ_CRASH("Not implemented"); | |
336 } | |
337 }; | |
338 | |
339 class ThreadStackHelper::ThreadContext final | 283 class ThreadStackHelper::ThreadContext final |
340 : public google_breakpad::MemoryRegion | |
341 { | 284 { |
342 public: | 285 public: |
343 #if defined(MOZ_THREADSTACKHELPER_X86) | 286 // TODO: provide per-platform definition of Context. |
344 typedef MDRawContextX86 Context; | 287 typedef struct {} Context; |
345 #elif defined(MOZ_THREADSTACKHELPER_X64) | 288 |
346 typedef MDRawContextAMD64 Context; | |
347 #elif defined(MOZ_THREADSTACKHELPER_ARM) | |
348 typedef MDRawContextARM Context; | |
349 #endif | |
350 // Limit copied stack to 4kB | 289 // Limit copied stack to 4kB |
351 static const size_t kMaxStackSize = 0x1000; | 290 static const size_t kMaxStackSize = 0x1000; |
352 // Limit unwound stack to 32 frames | 291 // Limit unwound stack to 32 frames |
353 static const unsigned int kMaxStackFrames = 32; | 292 static const unsigned int kMaxStackFrames = 32; |
354 // Whether this structure contains valid data | 293 // Whether this structure contains valid data |
355 bool mValid; | 294 bool mValid; |
356 // Processor context | 295 // Processor context |
357 Context mContext; | 296 Context mContext; |
358 // Stack area | 297 // Stack area |
359 UniquePtr<uint8_t[]> mStack; | 298 UniquePtr<uint8_t[]> mStack; |
360 // Start of stack area | 299 // Start of stack area |
361 uintptr_t mStackBase; | 300 uintptr_t mStackBase; |
362 // Size of stack area | 301 // Size of stack area |
363 size_t mStackSize; | 302 size_t mStackSize; |
364 // End of stack area | 303 // End of stack area |
365 const void* mStackEnd; | 304 const void* mStackEnd; |
366 | 305 |
367 ThreadContext() | 306 ThreadContext() |
368 : mValid(false) | 307 : mValid(false) |
369 , mStackBase(0) | 308 , mStackBase(0) |
370 , mStackSize(0) | 309 , mStackSize(0) |
371 , mStackEnd(nullptr) {} | 310 , mStackEnd(nullptr) {} |
372 virtual ~ThreadContext() {} | |
373 | |
374 virtual uint64_t GetBase() const { return uint64_t(mStackBase); } | |
375 virtual uint32_t GetSize() const { return mStackSize; } | |
376 virtual bool GetMemoryAtAddress(uint64_t aAddress, uint8_t* aValue) const | |
377 { | |
378 return GetMemoryAtAddressInternal(aAddress, aValue); | |
379 } | |
380 virtual bool GetMemoryAtAddress(uint64_t aAddress, uint16_t* aValue) const | |
381 { | |
382 return GetMemoryAtAddressInternal(aAddress, aValue); | |
383 } | |
384 virtual bool GetMemoryAtAddress(uint64_t aAddress, uint32_t* aValue) const | |
385 { | |
386 return GetMemoryAtAddressInternal(aAddress, aValue); | |
387 } | |
388 virtual bool GetMemoryAtAddress(uint64_t aAddress, uint64_t* aValue) const | |
389 { | |
390 return GetMemoryAtAddressInternal(aAddress, aValue); | |
391 } | |
392 | |
393 private: | |
394 template<typename T> | |
395 bool GetMemoryAtAddressInternal(uint64_t aAddress, T* aValue) const | |
396 { | |
397 const intptr_t offset = intptr_t(aAddress) - intptr_t(GetBase()); | |
398 if (offset < 0 || uintptr_t(offset) > (GetSize() - sizeof(T))) { | |
399 return false; | |
400 } | |
401 *aValue = *reinterpret_cast<const T*>(&mStack[offset]); | |
402 return true; | |
403 } | |
404 }; | 311 }; |
405 #endif // MOZ_THREADSTACKHELPER_NATIVE | 312 #endif // MOZ_THREADSTACKHELPER_NATIVE |
406 | 313 |
407 void | 314 void |
408 ThreadStackHelper::GetNativeStack(Stack& aStack) | 315 ThreadStackHelper::GetNativeStack(Stack& aStack) |
409 { | 316 { |
410 #ifdef MOZ_THREADSTACKHELPER_NATIVE | 317 #ifdef MOZ_THREADSTACKHELPER_NATIVE |
411 ThreadContext context; | 318 ThreadContext context; |
412 context.mStack = MakeUnique<uint8_t[]>(ThreadContext::kMaxStackSize); | 319 context.mStack = MakeUnique<uint8_t[]>(ThreadContext::kMaxStackSize); |
413 | 320 |
414 ScopedSetPtr<ThreadContext> contextPtr(mContextToFill, &context); | 321 ScopedSetPtr<ThreadContext> contextPtr(mContextToFill, &context); |
415 | 322 |
416 // Get pseudostack first and fill the thread context. | 323 // Get pseudostack first and fill the thread context. |
417 GetStack(aStack); | 324 GetStack(aStack); |
418 NS_ENSURE_TRUE_VOID(context.mValid); | 325 NS_ENSURE_TRUE_VOID(context.mValid); |
419 | 326 |
420 CodeModulesProvider modulesProvider; | 327 // TODO: walk the saved stack frames. |
421 google_breakpad::BasicCodeModules modules(&modulesProvider); | |
422 google_breakpad::BasicSourceLineResolver resolver; | |
423 google_breakpad::StackFrameSymbolizer symbolizer(nullptr, &resolver); | |
424 | |
425 #if defined(MOZ_THREADSTACKHELPER_X86) | |
426 google_breakpad::StackwalkerX86 stackWalker( | |
427 nullptr, &context.mContext, &context, &modules, &symbolizer); | |
428 #elif defined(MOZ_THREADSTACKHELPER_X64) | |
429 google_breakpad::StackwalkerAMD64 stackWalker( | |
430 nullptr, &context.mContext, &context, &modules, &symbolizer); | |
431 #elif defined(MOZ_THREADSTACKHELPER_ARM) | |
432 google_breakpad::StackwalkerARM stackWalker( | |
433 nullptr, &context.mContext, -1, &context, &modules, &symbolizer); | |
434 #else | |
435 #error "Unsupported architecture" | |
436 #endif | |
437 | |
438 google_breakpad::CallStack callStack; | |
439 std::vector<const google_breakpad::CodeModule*> modules_without_symbols; | |
440 | |
441 google_breakpad::Stackwalker::set_max_frames(ThreadContext::kMaxStackFrames); | |
442 google_breakpad::Stackwalker:: | |
443 set_max_frames_scanned(ThreadContext::kMaxStackFrames); | |
444 | |
445 NS_ENSURE_TRUE_VOID(stackWalker.Walk(&callStack, &modules_without_symbols)); | |
446 | |
447 const std::vector<google_breakpad::StackFrame*>& frames(*callStack.frames()); | |
448 for (intptr_t i = frames.size() - 1; i >= 0; i--) { | |
449 const google_breakpad::StackFrame& frame = *frames[i]; | |
450 if (!frame.module) { | |
451 continue; | |
452 } | |
453 const string& module = frame.module->code_file(); | |
454 #if defined(XP_LINUX) || defined(XP_MACOSX) | |
455 const char PATH_SEP = '/'; | |
456 #elif defined(XP_WIN) | |
457 const char PATH_SEP = '\\'; | |
458 #endif | |
459 const char* const module_basename = strrchr(module.c_str(), PATH_SEP); | |
460 const char* const module_name = module_basename ? | |
461 module_basename + 1 : module.c_str(); | |
462 | |
463 char buffer[0x100]; | |
464 size_t len = 0; | |
465 if (!frame.function_name.empty()) { | |
466 len = PR_snprintf(buffer, sizeof(buffer), "%s:%s", | |
467 module_name, frame.function_name.c_str()); | |
468 } else { | |
469 len = PR_snprintf(buffer, sizeof(buffer), "%s:0x%p", | |
470 module_name, (intptr_t) | |
471 (frame.instruction - frame.module->base_address())); | |
472 } | |
473 if (len) { | |
474 aStack.AppendViaBuffer(buffer, len); | |
475 } | |
476 } | |
477 #endif // MOZ_THREADSTACKHELPER_NATIVE | 328 #endif // MOZ_THREADSTACKHELPER_NATIVE |
478 } | 329 } |
479 | 330 |
480 #ifdef XP_LINUX | 331 #ifdef XP_LINUX |
481 | 332 |
482 int ThreadStackHelper::sInitialized; | 333 int ThreadStackHelper::sInitialized; |
483 int ThreadStackHelper::sFillStackSignum; | 334 int ThreadStackHelper::sFillStackSignum; |
484 | 335 |
485 void | 336 void |
486 ThreadStackHelper::FillStackHandler(int aSignal, siginfo_t* aInfo, | 337 ThreadStackHelper::FillStackHandler(int aSignal, siginfo_t* aInfo, |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
604 } | 455 } |
605 if (!basename) { | 456 if (!basename) { |
606 // If the (add-on) script is located under the {profile}/extensions | 457 // If the (add-on) script is located under the {profile}/extensions |
607 // directory, extract the path after the /extensions/ part. | 458 // directory, extract the path after the /extensions/ part. |
608 basename = GetPathAfterComponent(filename, "/extensions/"); | 459 basename = GetPathAfterComponent(filename, "/extensions/"); |
609 } | 460 } |
610 if (!basename) { | 461 if (!basename) { |
611 // Only keep the file base name for paths outside the above formats. | 462 // Only keep the file base name for paths outside the above formats. |
612 basename = strrchr(filename, '/'); | 463 basename = strrchr(filename, '/'); |
613 basename = basename ? basename + 1 : filename; | 464 basename = basename ? basename + 1 : filename; |
| 465 // Look for Windows path separator as well. |
| 466 filename = strrchr(basename, '\\'); |
| 467 if (filename) { |
| 468 basename = filename + 1; |
| 469 } |
614 } | 470 } |
615 | 471 |
616 size_t len = PR_snprintf(buffer, sizeof(buffer), "%s:%u", basename, lineno); | 472 size_t len = snprintf_literal(buffer, "%s:%u", basename, lineno); |
617 if (len < sizeof(buffer)) { | 473 if (len < sizeof(buffer)) { |
618 if (mStackToFill->IsSameAsEntry(aPrevLabel, buffer)) { | 474 if (mStackToFill->IsSameAsEntry(aPrevLabel, buffer)) { |
619 return aPrevLabel; | 475 return aPrevLabel; |
620 } | 476 } |
621 | 477 |
622 // Keep track of the required buffer size | 478 // Keep track of the required buffer size |
623 aAvailableBufferSize -= (len + 1); | 479 aAvailableBufferSize -= (len + 1); |
624 if (aAvailableBufferSize >= 0) { | 480 if (aAvailableBufferSize >= 0) { |
625 // Buffer is big enough. | 481 // Buffer is big enough. |
626 return mStackToFill->InfallibleAppendViaBuffer(buffer, len); | 482 return mStackToFill->InfallibleAppendViaBuffer(buffer, len); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
694 } | 550 } |
695 | 551 |
696 MOZ_ASAN_BLACKLIST void | 552 MOZ_ASAN_BLACKLIST void |
697 ThreadStackHelper::FillThreadContext(void* aContext) | 553 ThreadStackHelper::FillThreadContext(void* aContext) |
698 { | 554 { |
699 #ifdef MOZ_THREADSTACKHELPER_NATIVE | 555 #ifdef MOZ_THREADSTACKHELPER_NATIVE |
700 if (!mContextToFill) { | 556 if (!mContextToFill) { |
701 return; | 557 return; |
702 } | 558 } |
703 | 559 |
| 560 #if 0 // TODO: remove dependency on Breakpad structs. |
704 #if defined(XP_LINUX) | 561 #if defined(XP_LINUX) |
705 const ucontext_t& context = *reinterpret_cast<ucontext_t*>(aContext); | 562 const ucontext_t& context = *reinterpret_cast<ucontext_t*>(aContext); |
706 #if defined(MOZ_THREADSTACKHELPER_X86) | 563 #if defined(MOZ_THREADSTACKHELPER_X86) |
707 mContextToFill->mContext.context_flags = MD_CONTEXT_X86_FULL; | 564 mContextToFill->mContext.context_flags = MD_CONTEXT_X86_FULL; |
708 mContextToFill->mContext.edi = context.uc_mcontext.gregs[REG_EDI]; | 565 mContextToFill->mContext.edi = context.uc_mcontext.gregs[REG_EDI]; |
709 mContextToFill->mContext.esi = context.uc_mcontext.gregs[REG_ESI]; | 566 mContextToFill->mContext.esi = context.uc_mcontext.gregs[REG_ESI]; |
710 mContextToFill->mContext.ebx = context.uc_mcontext.gregs[REG_EBX]; | 567 mContextToFill->mContext.ebx = context.uc_mcontext.gregs[REG_EBX]; |
711 mContextToFill->mContext.edx = context.uc_mcontext.gregs[REG_EDX]; | 568 mContextToFill->mContext.edx = context.uc_mcontext.gregs[REG_EDX]; |
712 mContextToFill->mContext.ecx = context.uc_mcontext.gregs[REG_ECX]; | 569 mContextToFill->mContext.ecx = context.uc_mcontext.gregs[REG_ECX]; |
713 mContextToFill->mContext.eax = context.uc_mcontext.gregs[REG_EAX]; | 570 mContextToFill->mContext.eax = context.uc_mcontext.gregs[REG_EAX]; |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
851 intptr_t* dst = reinterpret_cast<intptr_t*>(&mContextToFill->mStack[0]); | 708 intptr_t* dst = reinterpret_cast<intptr_t*>(&mContextToFill->mStack[0]); |
852 const intptr_t* src = reinterpret_cast<intptr_t*>(sp); | 709 const intptr_t* src = reinterpret_cast<intptr_t*>(sp); |
853 for (intptr_t len = stackSize; len > 0; len -= sizeof(*src)) { | 710 for (intptr_t len = stackSize; len > 0; len -= sizeof(*src)) { |
854 *(dst++) = *(src++); | 711 *(dst++) = *(src++); |
855 } | 712 } |
856 #endif | 713 #endif |
857 | 714 |
858 mContextToFill->mStackBase = uintptr_t(sp); | 715 mContextToFill->mStackBase = uintptr_t(sp); |
859 mContextToFill->mStackSize = stackSize; | 716 mContextToFill->mStackSize = stackSize; |
860 mContextToFill->mValid = true; | 717 mContextToFill->mValid = true; |
| 718 #endif |
861 #endif // MOZ_THREADSTACKHELPER_NATIVE | 719 #endif // MOZ_THREADSTACKHELPER_NATIVE |
862 } | 720 } |
863 | 721 |
864 } // namespace mozilla | 722 } // namespace mozilla |
| 723 |
OLD | NEW |