diff options
Diffstat (limited to 'xpcom/threads/ThreadStackHelper.cpp')
-rw-r--r-- | xpcom/threads/ThreadStackHelper.cpp | 466 |
1 files changed, 3 insertions, 463 deletions
diff --git a/xpcom/threads/ThreadStackHelper.cpp b/xpcom/threads/ThreadStackHelper.cpp index 1713c9194..2c0e87986 100644 --- a/xpcom/threads/ThreadStackHelper.cpp +++ b/xpcom/threads/ThreadStackHelper.cpp @@ -9,9 +9,6 @@ #include "nsJSPrincipals.h" #include "nsScriptSecurityManager.h" #include "jsfriendapi.h" -#ifdef MOZ_THREADSTACKHELPER_NATIVE -#include "shared-libraries.h" -#endif #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" @@ -62,17 +59,6 @@ #endif #endif -#ifdef MOZ_THREADSTACKHELPER_NATIVE -#if defined(MOZ_THREADSTACKHELPER_X86) || \ - defined(MOZ_THREADSTACKHELPER_X64) || \ - defined(MOZ_THREADSTACKHELPER_ARM) -// On these architectures, the stack grows downwards (toward lower addresses). -#define MOZ_THREADSTACKHELPER_STACK_GROWS_DOWN -#else -#error "Unsupported architecture" -#endif -#endif // MOZ_THREADSTACKHELPER_NATIVE - namespace mozilla { void @@ -114,14 +100,6 @@ ThreadStackHelper::Shutdown() ThreadStackHelper::ThreadStackHelper() : mStackToFill(nullptr) -#ifdef MOZ_THREADSTACKHELPER_PSEUDO - , mPseudoStack(mozilla_get_pseudo_stack()) -#ifdef MOZ_THREADSTACKHELPER_NATIVE - , mContextToFill(nullptr) -#endif - , mMaxStackSize(Stack::sMaxInlineStorage) - , mMaxBufferSize(512) -#endif { #if defined(XP_LINUX) MOZ_ALWAYS_TRUE(!::sem_init(&mSem, 0, 0)); @@ -131,18 +109,11 @@ ThreadStackHelper::ThreadStackHelper() ::GetCurrentProcess(), ::GetCurrentThread(), ::GetCurrentProcess(), &mThreadID, THREAD_SUSPEND_RESUME -#ifdef MOZ_THREADSTACKHELPER_NATIVE - | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION -#endif , FALSE, 0); MOZ_ASSERT(mInitialized); #elif defined(XP_MACOSX) mThreadID = mach_thread_self(); #endif - -#ifdef MOZ_THREADSTACKHELPER_NATIVE - GetThreadStackBase(); -#endif } ThreadStackHelper::~ThreadStackHelper() @@ -156,45 +127,6 @@ ThreadStackHelper::~ThreadStackHelper() #endif } -#ifdef MOZ_THREADSTACKHELPER_NATIVE -void ThreadStackHelper::GetThreadStackBase() -{ - mThreadStackBase = 0; - -#if defined(XP_LINUX) - void* stackAddr; - size_t stackSize; - ::pthread_t pthr = ::pthread_self(); - ::pthread_attr_t pthr_attr; - NS_ENSURE_TRUE_VOID(!::pthread_getattr_np(pthr, &pthr_attr)); - if (!::pthread_attr_getstack(&pthr_attr, &stackAddr, &stackSize)) { -#ifdef MOZ_THREADSTACKHELPER_STACK_GROWS_DOWN - mThreadStackBase = intptr_t(stackAddr) + stackSize; -#else - mThreadStackBase = intptr_t(stackAddr); -#endif - } - MOZ_ALWAYS_TRUE(!::pthread_attr_destroy(&pthr_attr)); - -#elif defined(XP_WIN) - ::MEMORY_BASIC_INFORMATION meminfo = {}; - NS_ENSURE_TRUE_VOID(::VirtualQuery(&meminfo, &meminfo, sizeof(meminfo))); -#ifdef MOZ_THREADSTACKHELPER_STACK_GROWS_DOWN - mThreadStackBase = intptr_t(meminfo.BaseAddress) + meminfo.RegionSize; -#else - mThreadStackBase = intptr_t(meminfo.AllocationBase); -#endif - -#elif defined(XP_MACOSX) - ::pthread_t pthr = ::pthread_self(); - mThreadStackBase = intptr_t(::pthread_get_stackaddr_np(pthr)); - -#else - #error "Unsupported platform" -#endif // platform -} -#endif // MOZ_THREADSTACKHELPER_NATIVE - namespace { template<typename T> class ScopedSetPtr @@ -281,53 +213,10 @@ ThreadStackHelper::GetStack(Stack& aStack) #endif } -#ifdef MOZ_THREADSTACKHELPER_NATIVE -class ThreadStackHelper::ThreadContext final -{ -public: - // TODO: provide per-platform definition of Context. - typedef struct {} Context; - - // Limit copied stack to 4kB - static const size_t kMaxStackSize = 0x1000; - // Limit unwound stack to 32 frames - static const unsigned int kMaxStackFrames = 32; - // Whether this structure contains valid data - bool mValid; - // Processor context - Context mContext; - // Stack area - UniquePtr<uint8_t[]> mStack; - // Start of stack area - uintptr_t mStackBase; - // Size of stack area - size_t mStackSize; - // End of stack area - const void* mStackEnd; - - ThreadContext() - : mValid(false) - , mStackBase(0) - , mStackSize(0) - , mStackEnd(nullptr) {} -}; -#endif // MOZ_THREADSTACKHELPER_NATIVE - void ThreadStackHelper::GetNativeStack(Stack& aStack) { -#ifdef MOZ_THREADSTACKHELPER_NATIVE - ThreadContext context; - context.mStack = MakeUnique<uint8_t[]>(ThreadContext::kMaxStackSize); - - ScopedSetPtr<ThreadContext> contextPtr(mContextToFill, &context); - - // Get pseudostack first and fill the thread context. - GetStack(aStack); - NS_ENSURE_TRUE_VOID(context.mValid); - - // TODO: walk the saved stack frames. -#endif // MOZ_THREADSTACKHELPER_NATIVE + /*** STUB ***/ } #ifdef XP_LINUX @@ -353,369 +242,20 @@ ThreadStackHelper::PrepareStackBuffer(Stack& aStack) { // Return false to skip getting the stack and return an empty stack aStack.clear(); -#ifdef MOZ_THREADSTACKHELPER_PSEUDO - /* Normally, provided the profiler is enabled, it would be an error if we - don't have a pseudostack here (the thread probably forgot to call - profiler_register_thread). However, on B2G, profiling secondary threads - may be disabled despite profiler being enabled. This is by-design and - is not an error. */ - MOZ_ASSERT(mPseudoStack); - if (!aStack.reserve(mMaxStackSize) || - !aStack.reserve(aStack.capacity()) || // reserve up to the capacity - !aStack.EnsureBufferCapacity(mMaxBufferSize)) { - return false; - } - return true; -#else return false; -#endif -} - -#ifdef MOZ_THREADSTACKHELPER_PSEUDO - -namespace { - -bool -IsChromeJSScript(JSScript* aScript) -{ - // May be called from another thread or inside a signal handler. - // We assume querying the script is safe but we must not manipulate it. - - nsIScriptSecurityManager* const secman = - nsScriptSecurityManager::GetScriptSecurityManager(); - NS_ENSURE_TRUE(secman, false); - - JSPrincipals* const principals = JS_GetScriptPrincipals(aScript); - return secman->IsSystemPrincipal(nsJSPrincipals::get(principals)); -} - -// Get the full path after the URI scheme, if the URI matches the scheme. -// For example, GetFullPathForScheme("a://b/c/d/e", "a://") returns "b/c/d/e". -template <size_t LEN> -const char* -GetFullPathForScheme(const char* filename, const char (&scheme)[LEN]) { - // Account for the null terminator included in LEN. - if (!strncmp(filename, scheme, LEN - 1)) { - return filename + LEN - 1; - } - return nullptr; -} - -// Get the full path after a URI component, if the URI contains the component. -// For example, GetPathAfterComponent("a://b/c/d/e", "/c/") returns "d/e". -template <size_t LEN> -const char* -GetPathAfterComponent(const char* filename, const char (&component)[LEN]) { - const char* found = nullptr; - const char* next = strstr(filename, component); - while (next) { - // Move 'found' to end of the component, after the separator '/'. - // 'LEN - 1' accounts for the null terminator included in LEN, - found = next + LEN - 1; - // Resume searching before the separator '/'. - next = strstr(found - 1, component); - } - return found; } -} // namespace - -const char* -ThreadStackHelper::AppendJSEntry(const volatile StackEntry* aEntry, - intptr_t& aAvailableBufferSize, - const char* aPrevLabel) -{ - // May be called from another thread or inside a signal handler. - // We assume querying the script is safe but we must not manupulate it. - // Also we must not allocate any memory from heap. - MOZ_ASSERT(aEntry->isJs()); - - const char* label; - JSScript* script = aEntry->script(); - if (!script) { - label = "(profiling suppressed)"; - } else if (IsChromeJSScript(aEntry->script())) { - const char* filename = JS_GetScriptFilename(aEntry->script()); - const unsigned lineno = JS_PCToLineNumber(aEntry->script(), aEntry->pc()); - MOZ_ASSERT(filename); - - char buffer[128]; // Enough to fit longest js file name from the tree - - // Some script names are in the form "foo -> bar -> baz". - // Here we find the origin of these redirected scripts. - const char* basename = GetPathAfterComponent(filename, " -> "); - if (basename) { - filename = basename; - } - - basename = GetFullPathForScheme(filename, "chrome://"); - if (!basename) { - basename = GetFullPathForScheme(filename, "resource://"); - } - if (!basename) { - // If the (add-on) script is located under the {profile}/extensions - // directory, extract the path after the /extensions/ part. - basename = GetPathAfterComponent(filename, "/extensions/"); - } - if (!basename) { - // Only keep the file base name for paths outside the above formats. - basename = strrchr(filename, '/'); - basename = basename ? basename + 1 : filename; - // Look for Windows path separator as well. - filename = strrchr(basename, '\\'); - if (filename) { - basename = filename + 1; - } - } - - size_t len = SprintfLiteral(buffer, "%s:%u", basename, lineno); - if (len < sizeof(buffer)) { - if (mStackToFill->IsSameAsEntry(aPrevLabel, buffer)) { - return aPrevLabel; - } - - // Keep track of the required buffer size - aAvailableBufferSize -= (len + 1); - if (aAvailableBufferSize >= 0) { - // Buffer is big enough. - return mStackToFill->InfallibleAppendViaBuffer(buffer, len); - } - // Buffer is not big enough; fall through to using static label below. - } - // snprintf failed or buffer is not big enough. - label = "(chrome script)"; - } else { - label = "(content script)"; - } - - if (mStackToFill->IsSameAsEntry(aPrevLabel, label)) { - return aPrevLabel; - } - mStackToFill->infallibleAppend(label); - return label; -} - -#endif // MOZ_THREADSTACKHELPER_PSEUDO - void ThreadStackHelper::FillStackBuffer() { MOZ_ASSERT(mStackToFill->empty()); - -#ifdef MOZ_THREADSTACKHELPER_PSEUDO - size_t reservedSize = mStackToFill->capacity(); - size_t reservedBufferSize = mStackToFill->AvailableBufferSize(); - intptr_t availableBufferSize = intptr_t(reservedBufferSize); - - // Go from front to back - const volatile StackEntry* entry = mPseudoStack->mStack; - const volatile StackEntry* end = entry + mPseudoStack->stackSize(); - // Deduplicate identical, consecutive frames - const char* prevLabel = nullptr; - for (; reservedSize-- && entry != end; entry++) { - /* We only accept non-copy labels, including js::RunScript, - because we only want static labels in the hang stack. */ - if (entry->isCopyLabel()) { - continue; - } - if (entry->isJs()) { - prevLabel = AppendJSEntry(entry, availableBufferSize, prevLabel); - continue; - } -#ifdef MOZ_THREADSTACKHELPER_NATIVE - if (mContextToFill) { - mContextToFill->mStackEnd = entry->stackAddress(); - } -#endif - const char* const label = entry->label(); - if (mStackToFill->IsSameAsEntry(prevLabel, label)) { - // Avoid duplicate labels to save space in the stack. - continue; - } - mStackToFill->infallibleAppend(label); - prevLabel = label; - } - - // end != entry if we exited early due to not enough reserved frames. - // Expand the number of reserved frames for next time. - mMaxStackSize = mStackToFill->capacity() + (end - entry); - - // availableBufferSize < 0 if we needed a larger buffer than we reserved. - // Calculate a new reserve size for next time. - if (availableBufferSize < 0) { - mMaxBufferSize = reservedBufferSize - availableBufferSize; - } -#endif + /*** STUB ***/ } MOZ_ASAN_BLACKLIST void ThreadStackHelper::FillThreadContext(void* aContext) { -#ifdef MOZ_THREADSTACKHELPER_NATIVE - if (!mContextToFill) { - return; - } - -#if 0 // TODO: remove dependency on Breakpad structs. -#if defined(XP_LINUX) - const ucontext_t& context = *reinterpret_cast<ucontext_t*>(aContext); -#if defined(MOZ_THREADSTACKHELPER_X86) - mContextToFill->mContext.context_flags = MD_CONTEXT_X86_FULL; - mContextToFill->mContext.edi = context.uc_mcontext.gregs[REG_EDI]; - mContextToFill->mContext.esi = context.uc_mcontext.gregs[REG_ESI]; - mContextToFill->mContext.ebx = context.uc_mcontext.gregs[REG_EBX]; - mContextToFill->mContext.edx = context.uc_mcontext.gregs[REG_EDX]; - mContextToFill->mContext.ecx = context.uc_mcontext.gregs[REG_ECX]; - mContextToFill->mContext.eax = context.uc_mcontext.gregs[REG_EAX]; - mContextToFill->mContext.ebp = context.uc_mcontext.gregs[REG_EBP]; - mContextToFill->mContext.eip = context.uc_mcontext.gregs[REG_EIP]; - mContextToFill->mContext.eflags = context.uc_mcontext.gregs[REG_EFL]; - mContextToFill->mContext.esp = context.uc_mcontext.gregs[REG_ESP]; -#elif defined(MOZ_THREADSTACKHELPER_X64) - mContextToFill->mContext.context_flags = MD_CONTEXT_AMD64_FULL; - mContextToFill->mContext.eflags = uint32_t(context.uc_mcontext.gregs[REG_EFL]); - mContextToFill->mContext.rax = context.uc_mcontext.gregs[REG_RAX]; - mContextToFill->mContext.rcx = context.uc_mcontext.gregs[REG_RCX]; - mContextToFill->mContext.rdx = context.uc_mcontext.gregs[REG_RDX]; - mContextToFill->mContext.rbx = context.uc_mcontext.gregs[REG_RBX]; - mContextToFill->mContext.rsp = context.uc_mcontext.gregs[REG_RSP]; - mContextToFill->mContext.rbp = context.uc_mcontext.gregs[REG_RBP]; - mContextToFill->mContext.rsi = context.uc_mcontext.gregs[REG_RSI]; - mContextToFill->mContext.rdi = context.uc_mcontext.gregs[REG_RDI]; - memcpy(&mContextToFill->mContext.r8, - &context.uc_mcontext.gregs[REG_R8], 8 * sizeof(int64_t)); - mContextToFill->mContext.rip = context.uc_mcontext.gregs[REG_RIP]; -#elif defined(MOZ_THREADSTACKHELPER_ARM) - mContextToFill->mContext.context_flags = MD_CONTEXT_ARM_FULL; - memcpy(&mContextToFill->mContext.iregs[0], - &context.uc_mcontext.arm_r0, 17 * sizeof(int32_t)); -#else - #error "Unsupported architecture" -#endif // architecture - -#elif defined(XP_WIN) - // Breakpad context struct is based off of the Windows CONTEXT struct, - // so we assume they are the same; do some sanity checks to make sure. - static_assert(sizeof(ThreadContext::Context) == sizeof(::CONTEXT), - "Context struct mismatch"); - static_assert(offsetof(ThreadContext::Context, context_flags) == - offsetof(::CONTEXT, ContextFlags), - "Context struct mismatch"); - mContextToFill->mContext.context_flags = CONTEXT_FULL; - NS_ENSURE_TRUE_VOID(::GetThreadContext(mThreadID, - reinterpret_cast<::CONTEXT*>(&mContextToFill->mContext))); - -#elif defined(XP_MACOSX) -#if defined(MOZ_THREADSTACKHELPER_X86) - const thread_state_flavor_t flavor = x86_THREAD_STATE32; - x86_thread_state32_t state = {}; - mach_msg_type_number_t count = x86_THREAD_STATE32_COUNT; -#elif defined(MOZ_THREADSTACKHELPER_X64) - const thread_state_flavor_t flavor = x86_THREAD_STATE64; - x86_thread_state64_t state = {}; - mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; -#elif defined(MOZ_THREADSTACKHELPER_ARM) - const thread_state_flavor_t flavor = ARM_THREAD_STATE; - arm_thread_state_t state = {}; - mach_msg_type_number_t count = ARM_THREAD_STATE_COUNT; -#endif - NS_ENSURE_TRUE_VOID(KERN_SUCCESS == ::thread_get_state( - mThreadID, flavor, reinterpret_cast<thread_state_t>(&state), &count)); -#if __DARWIN_UNIX03 -#define GET_REGISTER(s, r) ((s).__##r) -#else -#define GET_REGISTER(s, r) ((s).r) -#endif -#if defined(MOZ_THREADSTACKHELPER_X86) - mContextToFill->mContext.context_flags = MD_CONTEXT_X86_FULL; - mContextToFill->mContext.edi = GET_REGISTER(state, edi); - mContextToFill->mContext.esi = GET_REGISTER(state, esi); - mContextToFill->mContext.ebx = GET_REGISTER(state, ebx); - mContextToFill->mContext.edx = GET_REGISTER(state, edx); - mContextToFill->mContext.ecx = GET_REGISTER(state, ecx); - mContextToFill->mContext.eax = GET_REGISTER(state, eax); - mContextToFill->mContext.ebp = GET_REGISTER(state, ebp); - mContextToFill->mContext.eip = GET_REGISTER(state, eip); - mContextToFill->mContext.eflags = GET_REGISTER(state, eflags); - mContextToFill->mContext.esp = GET_REGISTER(state, esp); -#elif defined(MOZ_THREADSTACKHELPER_X64) - mContextToFill->mContext.context_flags = MD_CONTEXT_AMD64_FULL; - mContextToFill->mContext.eflags = uint32_t(GET_REGISTER(state, rflags)); - mContextToFill->mContext.rax = GET_REGISTER(state, rax); - mContextToFill->mContext.rcx = GET_REGISTER(state, rcx); - mContextToFill->mContext.rdx = GET_REGISTER(state, rdx); - mContextToFill->mContext.rbx = GET_REGISTER(state, rbx); - mContextToFill->mContext.rsp = GET_REGISTER(state, rsp); - mContextToFill->mContext.rbp = GET_REGISTER(state, rbp); - mContextToFill->mContext.rsi = GET_REGISTER(state, rsi); - mContextToFill->mContext.rdi = GET_REGISTER(state, rdi); - memcpy(&mContextToFill->mContext.r8, - &GET_REGISTER(state, r8), 8 * sizeof(int64_t)); - mContextToFill->mContext.rip = GET_REGISTER(state, rip); -#elif defined(MOZ_THREADSTACKHELPER_ARM) - mContextToFill->mContext.context_flags = MD_CONTEXT_ARM_FULL; - memcpy(mContextToFill->mContext.iregs, - GET_REGISTER(state, r), 17 * sizeof(int32_t)); -#else - #error "Unsupported architecture" -#endif // architecture -#undef GET_REGISTER - -#else - #error "Unsupported platform" -#endif // platform - - intptr_t sp = 0; -#if defined(MOZ_THREADSTACKHELPER_X86) - sp = mContextToFill->mContext.esp; -#elif defined(MOZ_THREADSTACKHELPER_X64) - sp = mContextToFill->mContext.rsp; -#elif defined(MOZ_THREADSTACKHELPER_ARM) - sp = mContextToFill->mContext.iregs[13]; -#else - #error "Unsupported architecture" -#endif // architecture - NS_ENSURE_TRUE_VOID(sp); - NS_ENSURE_TRUE_VOID(mThreadStackBase); - - size_t stackSize = std::min(intptr_t(ThreadContext::kMaxStackSize), - std::abs(sp - mThreadStackBase)); - - if (mContextToFill->mStackEnd) { - // Limit the start of stack to a certain location if specified. - stackSize = std::min(intptr_t(stackSize), - std::abs(sp - intptr_t(mContextToFill->mStackEnd))); - } - -#ifndef MOZ_THREADSTACKHELPER_STACK_GROWS_DOWN - // If if the stack grows upwards, and we need to recalculate our - // stack copy's base address. Subtract sizeof(void*) so that the - // location pointed to by sp is included. - sp -= stackSize - sizeof(void*); -#endif - -#ifndef MOZ_ASAN - memcpy(mContextToFill->mStack.get(), reinterpret_cast<void*>(sp), stackSize); - // Valgrind doesn't care about the access outside the stack frame, but - // the presence of uninitialised values on the stack does cause it to - // later report a lot of false errors when Breakpad comes to unwind it. - // So mark the extracted data as defined. - MOZ_MAKE_MEM_DEFINED(mContextToFill->mStack.get(), stackSize); -#else - // ASan will flag memcpy for access outside of stack frames, - // so roll our own memcpy here. - intptr_t* dst = reinterpret_cast<intptr_t*>(&mContextToFill->mStack[0]); - const intptr_t* src = reinterpret_cast<intptr_t*>(sp); - for (intptr_t len = stackSize; len > 0; len -= sizeof(*src)) { - *(dst++) = *(src++); - } -#endif - - mContextToFill->mStackBase = uintptr_t(sp); - mContextToFill->mStackSize = stackSize; - mContextToFill->mValid = true; -#endif -#endif // MOZ_THREADSTACKHELPER_NATIVE + /*** STUB ***/ } } // namespace mozilla |