Backed out 4 changesets (bug 1357829) for build bustage in xpcshell\selftest.py on windows 8 x64 opt. a=backout
Backed out changeset 8ea202bb1103 (bug 1357829) Backed out changeset cebe4d7abeda (bug 1357829) Backed out changeset 378d473c9619 (bug 1357829) Backed out changeset 86ebe868d443 (bug 1357829)
This commit is contained in:
@@ -9,6 +9,9 @@
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "nsScriptSecurityManager.h"
|
||||
#include "jsfriendapi.h"
|
||||
#ifdef MOZ_THREADSTACKHELPER_NATIVE
|
||||
#include "shared-libraries.h"
|
||||
#endif
|
||||
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
|
||||
#include "PseudoStack.h"
|
||||
#endif
|
||||
@@ -62,20 +65,91 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_THREADSTACKHELPER_NATIVE
|
||||
#if defined(MOZ_THREADSTACKHELPER_X86) || \
|
||||
defined(MOZ_THREADSTACKHELPER_X64)
|
||||
// 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
|
||||
ThreadStackHelper::Startup()
|
||||
{
|
||||
#if defined(XP_LINUX)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sInitialized) {
|
||||
// TODO: centralize signal number allocation
|
||||
sFillStackSignum = SIGRTMIN + 4;
|
||||
if (sFillStackSignum > SIGRTMAX) {
|
||||
// Leave uninitialized
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
struct sigaction sigact = {};
|
||||
sigact.sa_sigaction = FillStackHandler;
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
sigact.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
MOZ_ALWAYS_TRUE(!::sigaction(sFillStackSignum, &sigact, nullptr));
|
||||
}
|
||||
sInitialized++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::Shutdown()
|
||||
{
|
||||
#if defined(XP_LINUX)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (sInitialized == 1) {
|
||||
struct sigaction sigact = {};
|
||||
sigact.sa_handler = SIG_DFL;
|
||||
MOZ_ALWAYS_TRUE(!::sigaction(sFillStackSignum, &sigact, nullptr));
|
||||
}
|
||||
sInitialized--;
|
||||
#endif
|
||||
}
|
||||
|
||||
ThreadStackHelper::ThreadStackHelper()
|
||||
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
|
||||
: mStackToFill(nullptr)
|
||||
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
|
||||
, mPseudoStack(profiler_get_pseudo_stack())
|
||||
, mMaxStackSize(Stack::sMaxInlineStorage)
|
||||
, mMaxBufferSize(512)
|
||||
#endif
|
||||
#ifdef MOZ_THREADSTACKHELPER_NATIVE
|
||||
, mNativeStackToFill(nullptr)
|
||||
#endif
|
||||
{
|
||||
mThreadId = profiler_current_thread_id();
|
||||
#if defined(XP_LINUX)
|
||||
MOZ_ALWAYS_TRUE(!::sem_init(&mSem, 0, 0));
|
||||
mThreadID = ::syscall(SYS_gettid);
|
||||
#elif defined(XP_WIN)
|
||||
mInitialized = !!::DuplicateHandle(
|
||||
::GetCurrentProcess(), ::GetCurrentThread(),
|
||||
::GetCurrentProcess(), &mThreadID,
|
||||
THREAD_SUSPEND_RESUME
|
||||
#ifdef MOZ_THREADSTACKHELPER_NATIVE
|
||||
| THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION
|
||||
#endif
|
||||
, FALSE, 0);
|
||||
mStackTop = profiler_get_stack_top();
|
||||
MOZ_ASSERT(mInitialized);
|
||||
#elif defined(XP_MACOSX)
|
||||
mThreadID = mach_thread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
ThreadStackHelper::~ThreadStackHelper()
|
||||
{
|
||||
#if defined(XP_LINUX)
|
||||
MOZ_ALWAYS_TRUE(!::sem_destroy(&mSem));
|
||||
#elif defined(XP_WIN)
|
||||
if (mInitialized) {
|
||||
MOZ_ALWAYS_TRUE(!!::CloseHandle(mThreadID));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -96,10 +170,138 @@ ThreadStackHelper::GetPseudoStack(Stack& aStack)
|
||||
GetStacksInternal(&aStack, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::GetStacksInternal(Stack* aStack, NativeStack* aNativeStack)
|
||||
{
|
||||
// Always run PrepareStackBuffer first to clear aStack
|
||||
if (aStack && !PrepareStackBuffer(*aStack)) {
|
||||
// Skip and return empty aStack
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedSetPtr<Stack> stackPtr(mStackToFill, aStack);
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
if (!sInitialized) {
|
||||
MOZ_ASSERT(false);
|
||||
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));
|
||||
}
|
||||
|
||||
#elif defined(XP_WIN)
|
||||
if (!mInitialized) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: We can only perform frame pointer stack walking on non win64
|
||||
// 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 (aNativeStack) {
|
||||
aNativeStack->reserve(Telemetry::HangStack::sMaxNativeFrames);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (::SuspendThread(mThreadID) == DWORD(-1)) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// SuspendThread is asynchronous, so the thread may still be running. Use
|
||||
// GetThreadContext to ensure it's really suspended.
|
||||
// See https://blogs.msdn.microsoft.com/oldnewthing/20150205-00/?p=44743.
|
||||
CONTEXT context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.ContextFlags = CONTEXT_CONTROL;
|
||||
if (::GetThreadContext(mThreadID, &context)) {
|
||||
if (aStack) {
|
||||
FillStackBuffer();
|
||||
}
|
||||
|
||||
#ifndef MOZ_THREADSTACKHELPER_X64
|
||||
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);
|
||||
|
||||
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*>(aNativeStack), framePointer,
|
||||
mStackTop);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1));
|
||||
|
||||
#elif defined(XP_MACOSX)
|
||||
# if defined(MOZ_VALGRIND) && defined(RUNNING_ON_VALGRIND)
|
||||
if (RUNNING_ON_VALGRIND) {
|
||||
/* thread_suspend and thread_resume sometimes hang runs on Valgrind,
|
||||
for unknown reasons. So, just avoid them. See bug 1100911. */
|
||||
return;
|
||||
}
|
||||
# endif
|
||||
|
||||
if (aStack) {
|
||||
if (::thread_suspend(mThreadID) != KERN_SUCCESS) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
FillStackBuffer();
|
||||
|
||||
MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::GetNativeStack(NativeStack& aNativeStack)
|
||||
{
|
||||
#ifdef MOZ_THREADSTACKHELPER_NATIVE
|
||||
GetStacksInternal(nullptr, &aNativeStack);
|
||||
#endif // MOZ_THREADSTACKHELPER_NATIVE
|
||||
}
|
||||
|
||||
void
|
||||
@@ -108,48 +310,23 @@ ThreadStackHelper::GetPseudoAndNativeStack(Stack& aStack, NativeStack& aNativeSt
|
||||
GetStacksInternal(&aStack, &aNativeStack);
|
||||
}
|
||||
|
||||
#ifdef XP_LINUX
|
||||
|
||||
int ThreadStackHelper::sInitialized;
|
||||
int ThreadStackHelper::sFillStackSignum;
|
||||
|
||||
void
|
||||
ThreadStackHelper::GetStacksInternal(Stack* aStack, NativeStack* aNativeStack)
|
||||
ThreadStackHelper::FillStackHandler(int aSignal, siginfo_t* aInfo,
|
||||
void* aContext)
|
||||
{
|
||||
#if defined(MOZ_THREADSTACKHELPER_PSEUDO) || defined(MOZ_THREADSTACKHELPER_NATIVE)
|
||||
// Always run PrepareStackBuffer first to clear aStack
|
||||
if (aStack && !PrepareStackBuffer(*aStack)) {
|
||||
// Skip and return empty aStack
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare the native stack
|
||||
if (aNativeStack) {
|
||||
aNativeStack->clear();
|
||||
aNativeStack->reserve(Telemetry::HangStack::sMaxNativeFrames);
|
||||
}
|
||||
|
||||
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
|
||||
ScopedSetPtr<Stack> stackPtr(mStackToFill, aStack);
|
||||
#endif
|
||||
#ifdef MOZ_THREADSTACKHELPER_NATIVE
|
||||
ScopedSetPtr<NativeStack> nativeStackPtr(mNativeStackToFill, aNativeStack);
|
||||
#endif
|
||||
|
||||
auto callback = [&, this] (void** aPCs, size_t aCount) {
|
||||
FillStackBuffer();
|
||||
|
||||
#ifdef MOZ_THREADSTACKHELPER_NATIVE
|
||||
if (mNativeStackToFill) {
|
||||
while (aCount-- &&
|
||||
mNativeStackToFill->size() < mNativeStackToFill->capacity()) {
|
||||
mNativeStackToFill->push_back(reinterpret_cast<uintptr_t>(aPCs[aCount]));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
profiler_suspend_and_sample_thread(mThreadId,
|
||||
callback,
|
||||
/* aSampleNative = */ !!aNativeStack);
|
||||
#endif
|
||||
ThreadStackHelper* const helper =
|
||||
reinterpret_cast<ThreadStackHelper*>(aInfo->si_value.sival_ptr);
|
||||
helper->FillStackBuffer();
|
||||
::sem_post(&helper->mSem);
|
||||
}
|
||||
|
||||
#endif // XP_LINUX
|
||||
|
||||
bool
|
||||
ThreadStackHelper::PrepareStackBuffer(Stack& aStack)
|
||||
{
|
||||
@@ -307,9 +484,9 @@ ThreadStackHelper::AppendJSEntry(const volatile js::ProfileEntry* aEntry,
|
||||
void
|
||||
ThreadStackHelper::FillStackBuffer()
|
||||
{
|
||||
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
|
||||
MOZ_ASSERT(mStackToFill->empty());
|
||||
|
||||
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
|
||||
size_t reservedSize = mStackToFill->capacity();
|
||||
size_t reservedBufferSize = mStackToFill->AvailableBufferSize();
|
||||
intptr_t availableBufferSize = intptr_t(reservedBufferSize);
|
||||
|
||||
Reference in New Issue
Block a user