Bug 1358619 - Fetch the stack and native stack within the same pause of the target thread, r=froydnj

This commit is contained in:
Michael Layzell
2017-04-21 15:45:11 -04:00
parent ce38ccfce8
commit 7d3fc6981d
5 changed files with 131 additions and 108 deletions

View File

@@ -165,40 +165,42 @@ public:
} // namespace
void
ThreadStackHelper::GetStack(Stack& aStack)
ThreadStackHelper::GetPseudoStack(Stack& aStack)
{
GetStackInternal(aStack, /* aAppendNativeStack */ false);
GetStacksInternal(&aStack, nullptr);
}
void
ThreadStackHelper::GetStackInternal(Stack& aStack, bool aAppendNativeStack)
ThreadStackHelper::GetStacksInternal(Stack* aStack, NativeStack* aNativeStack)
{
// Always run PrepareStackBuffer first to clear aStack
if (!PrepareStackBuffer(aStack)) {
if (aStack && !PrepareStackBuffer(*aStack)) {
// Skip and return empty aStack
return;
}
ScopedSetPtr<Stack> stackPtr(mStackToFill, &aStack);
ScopedSetPtr<Stack> stackPtr(mStackToFill, aStack);
#if defined(XP_LINUX)
if (!sInitialized) {
MOZ_ASSERT(false);
return;
}
siginfo_t uinfo = {};
uinfo.si_signo = sFillStackSignum;
uinfo.si_code = SI_QUEUE;
uinfo.si_pid = getpid();
uinfo.si_uid = getuid();
uinfo.si_value.sival_ptr = this;
if (::syscall(SYS_rt_tgsigqueueinfo, uinfo.si_pid,
mThreadID, sFillStackSignum, &uinfo)) {
// rt_tgsigqueueinfo was added in Linux 2.6.31.
// Could have failed because the syscall did not exist.
return;
if (aStack) {
siginfo_t uinfo = {};
uinfo.si_signo = sFillStackSignum;
uinfo.si_code = SI_QUEUE;
uinfo.si_pid = getpid();
uinfo.si_uid = getuid();
uinfo.si_value.sival_ptr = this;
if (::syscall(SYS_rt_tgsigqueueinfo, uinfo.si_pid,
mThreadID, sFillStackSignum, &uinfo)) {
// rt_tgsigqueueinfo was added in Linux 2.6.31.
// Could have failed because the syscall did not exist.
return;
}
MOZ_ALWAYS_TRUE(!::sem_wait(&mSem));
}
MOZ_ALWAYS_TRUE(!::sem_wait(&mSem));
#elif defined(XP_WIN)
if (!mInitialized) {
@@ -210,8 +212,8 @@ ThreadStackHelper::GetStackInternal(Stack& aStack, bool aAppendNativeStack)
// platforms, because Win64 always omits frame pointers. We don't want to use
// MozStackWalk here, so we just skip collecting stacks entirely.
#ifndef MOZ_THREADSTACKHELPER_X64
if (aAppendNativeStack) {
aStack.EnsureNativeFrameCapacity(Telemetry::HangStack::sMaxNativeFrames);
if (aNativeStack) {
aNativeStack->reserve(Telemetry::HangStack::sMaxNativeFrames);
}
#endif
@@ -227,45 +229,47 @@ ThreadStackHelper::GetStackInternal(Stack& aStack, bool aAppendNativeStack)
memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_CONTROL;
if (::GetThreadContext(mThreadID, &context)) {
FillStackBuffer();
}
if (aStack) {
FillStackBuffer();
}
#ifndef MOZ_THREADSTACKHELPER_X64
if (aAppendNativeStack) {
auto callback = [](uint32_t, void* aPC, void*, void* aClosure) {
Stack* stack = static_cast<Stack*>(aClosure);
stack->AppendNativeFrame(reinterpret_cast<uintptr_t>(aPC));
};
if (aNativeStack) {
auto callback = [](uint32_t, void* aPC, void*, void* aClosure) {
NativeStack* stack = static_cast<NativeStack*>(aClosure);
stack->push_back(reinterpret_cast<uintptr_t>(aPC));
};
// Now we need to get our frame pointer, our stack pointer, and our stack
// top. Rather than registering and storing the stack tops ourselves, we use
// the gecko profiler to look it up.
void** framePointer = reinterpret_cast<void**>(context.Ebp);
void** stackPointer = reinterpret_cast<void**>(context.Esp);
// Now we need to get our frame pointer, our stack pointer, and our stack
// top. Rather than registering and storing the stack tops ourselves, we use
// the gecko profiler to look it up.
void** framePointer = reinterpret_cast<void**>(context.Ebp);
void** stackPointer = reinterpret_cast<void**>(context.Esp);
MOZ_ASSERT(mStackTop, "The thread should be registered by the profiler");
MOZ_ASSERT(mStackTop, "The thread should be registered by the profiler");
// Double check that the values we pulled for the thread make sense before
// walking the stack.
if (mStackTop && framePointer >= stackPointer && framePointer < mStackTop) {
// NOTE: In bug 1346415 this was changed to use FramePointerStackWalk.
// This was done because lowering the background hang timer threshold
// would cause it to fire on infra early during the boot process, causing
// a deadlock in MozStackWalk when the target thread was holding the
// windows-internal lock on the function table, as it would be suspended
// before we tried to grab the lock to walk its stack.
//
// FramePointerStackWalk is implemented entirely in userspace and thus
// doesn't have the same issues with deadlocking. Unfortunately as 64-bit
// windows is not guaranteed to have frame pointers, the stack walking
// code is only enabled on 32-bit windows builds (bug 1357829).
FramePointerStackWalk(callback, /* skipFrames */ 0,
/* maxFrames */ Telemetry::HangStack::sMaxNativeFrames,
reinterpret_cast<void*>(&aStack), framePointer,
mStackTop);
// Double check that the values we pulled for the thread make sense before
// walking the stack.
if (mStackTop && framePointer >= stackPointer && framePointer < mStackTop) {
// NOTE: In bug 1346415 this was changed to use FramePointerStackWalk.
// This was done because lowering the background hang timer threshold
// would cause it to fire on infra early during the boot process, causing
// a deadlock in MozStackWalk when the target thread was holding the
// windows-internal lock on the function table, as it would be suspended
// before we tried to grab the lock to walk its stack.
//
// FramePointerStackWalk is implemented entirely in userspace and thus
// doesn't have the same issues with deadlocking. Unfortunately as 64-bit
// windows is not guaranteed to have frame pointers, the stack walking
// code is only enabled on 32-bit windows builds (bug 1357829).
FramePointerStackWalk(callback, /* skipFrames */ 0,
/* maxFrames */ Telemetry::HangStack::sMaxNativeFrames,
reinterpret_cast<void*>(aNativeStack), framePointer,
mStackTop);
}
}
}
#endif
}
MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1));
@@ -278,26 +282,34 @@ ThreadStackHelper::GetStackInternal(Stack& aStack, bool aAppendNativeStack)
}
# endif
if (::thread_suspend(mThreadID) != KERN_SUCCESS) {
MOZ_ASSERT(false);
return;
if (aStack) {
if (::thread_suspend(mThreadID) != KERN_SUCCESS) {
MOZ_ASSERT(false);
return;
}
FillStackBuffer();
MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS);
}
FillStackBuffer();
MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS);
#endif
}
void
ThreadStackHelper::GetNativeStack(Stack& aStack)
ThreadStackHelper::GetNativeStack(NativeStack& aNativeStack)
{
#ifdef MOZ_THREADSTACKHELPER_NATIVE
GetStackInternal(aStack, /* aAppendNativeStack */ true);
GetStacksInternal(nullptr, &aNativeStack);
#endif // MOZ_THREADSTACKHELPER_NATIVE
}
void
ThreadStackHelper::GetPseudoAndNativeStack(Stack& aStack, NativeStack& aNativeStack)
{
GetStacksInternal(&aStack, &aNativeStack);
}
#ifdef XP_LINUX
int ThreadStackHelper::sInitialized;