mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 1452204 part 2 - Use RtlCaptureContext to capture context for current thread and remove walker thread. r=glandium
GetThreadContext() returns a context pointing to its own frame when it gets called with the current thread handle. That frame can go away after it returns. This patch instead uses RtlCaptureContext(), which captures the context of its caller, when walking the current thread. In the past, we also used a walker thread when nullptr is passed in for aThread, but the check doesn't cover all the cases, and having another thread is apparently more complicated than this approach. MozReview-Commit-ID: 3TAatDc9BLh --HG-- extra : rebase_source : 7978cce48b8939a723cd5ccafe86d3f7aca6d3ac
This commit is contained in:
parent
5ce8f98cba
commit
75cc8c371b
@ -95,7 +95,6 @@ struct WalkStackData
|
||||
CONTEXT* context;
|
||||
};
|
||||
|
||||
DWORD gStackWalkThread;
|
||||
CRITICAL_SECTION gDbgHelpCS;
|
||||
|
||||
#ifdef _M_AMD64
|
||||
@ -189,61 +188,6 @@ InitializeDbgHelpCriticalSection()
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
static unsigned int WINAPI WalkStackThread(void* aData);
|
||||
|
||||
static bool
|
||||
EnsureWalkThreadReady()
|
||||
{
|
||||
static bool walkThreadReady = false;
|
||||
static HANDLE stackWalkThread = nullptr;
|
||||
static HANDLE readyEvent = nullptr;
|
||||
|
||||
if (walkThreadReady) {
|
||||
return walkThreadReady;
|
||||
}
|
||||
|
||||
if (!stackWalkThread) {
|
||||
readyEvent = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
|
||||
FALSE /* initially non-signaled */,
|
||||
nullptr);
|
||||
if (!readyEvent) {
|
||||
PrintError("CreateEvent");
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int threadID;
|
||||
stackWalkThread = (HANDLE)_beginthreadex(nullptr, 0, WalkStackThread,
|
||||
(void*)readyEvent, 0, &threadID);
|
||||
if (!stackWalkThread) {
|
||||
PrintError("CreateThread");
|
||||
::CloseHandle(readyEvent);
|
||||
readyEvent = nullptr;
|
||||
return false;
|
||||
}
|
||||
gStackWalkThread = threadID;
|
||||
::CloseHandle(stackWalkThread);
|
||||
}
|
||||
|
||||
MOZ_ASSERT((stackWalkThread && readyEvent) ||
|
||||
(!stackWalkThread && !readyEvent));
|
||||
|
||||
// The thread was created. Try to wait an arbitrary amount of time (1 second
|
||||
// should be enough) for its event loop to start before posting events to it.
|
||||
DWORD waitRet = ::WaitForSingleObject(readyEvent, 1000);
|
||||
if (waitRet == WAIT_TIMEOUT) {
|
||||
// We get a timeout if we're called during static initialization because
|
||||
// the thread will only start executing after we return so it couldn't
|
||||
// have signalled the event. If that is the case, give up for now and
|
||||
// try again next time we're called.
|
||||
return false;
|
||||
}
|
||||
::CloseHandle(readyEvent);
|
||||
stackWalkThread = nullptr;
|
||||
readyEvent = nullptr;
|
||||
|
||||
return walkThreadReady = true;
|
||||
}
|
||||
|
||||
static void
|
||||
WalkStackMain64(struct WalkStackData* aData)
|
||||
{
|
||||
@ -254,10 +198,9 @@ WalkStackMain64(struct WalkStackData* aData)
|
||||
context = &context_buf;
|
||||
memset(context, 0, sizeof(CONTEXT));
|
||||
context->ContextFlags = CONTEXT_FULL;
|
||||
if (!GetThreadContext(aData->thread, context)) {
|
||||
if (aData->walkCallingThread) {
|
||||
PrintError("GetThreadContext");
|
||||
}
|
||||
if (aData->walkCallingThread) {
|
||||
::RtlCaptureContext(context);
|
||||
} else if (!GetThreadContext(aData->thread, context)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -428,59 +371,6 @@ WalkStackMain64(struct WalkStackData* aData)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int WINAPI
|
||||
WalkStackThread(void* aData)
|
||||
{
|
||||
BOOL msgRet;
|
||||
MSG msg;
|
||||
|
||||
// Call PeekMessage to force creation of a message queue so that
|
||||
// other threads can safely post events to us.
|
||||
::PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
|
||||
|
||||
// and tell the thread that created us that we're ready.
|
||||
HANDLE readyEvent = (HANDLE)aData;
|
||||
::SetEvent(readyEvent);
|
||||
|
||||
while ((msgRet = ::GetMessage(&msg, (HWND)-1, 0, 0)) != 0) {
|
||||
if (msgRet == -1) {
|
||||
PrintError("GetMessage");
|
||||
} else {
|
||||
DWORD ret;
|
||||
|
||||
struct WalkStackData* data = (WalkStackData*)msg.lParam;
|
||||
if (!data) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't suspend the calling thread until it's waiting for
|
||||
// us; otherwise the number of frames on the stack could vary.
|
||||
ret = ::WaitForSingleObject(data->eventStart, INFINITE);
|
||||
if (ret != WAIT_OBJECT_0) {
|
||||
PrintError("WaitForSingleObject");
|
||||
}
|
||||
|
||||
// Suspend the calling thread, dump his stack, and then resume him.
|
||||
// He's currently waiting for us to finish so now should be a good time.
|
||||
ret = ::SuspendThread(data->thread);
|
||||
if (ret == (DWORD)-1) {
|
||||
PrintError("ThreadSuspend");
|
||||
} else {
|
||||
WalkStackMain64(data);
|
||||
|
||||
ret = ::ResumeThread(data->thread);
|
||||
if (ret == (DWORD)-1) {
|
||||
PrintError("ThreadResume");
|
||||
}
|
||||
}
|
||||
|
||||
::SetEvent(data->eventEnd);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the stack, translating PC's found into strings and recording the
|
||||
* chain in aBuffer. For this to work properly, the DLLs must be rebased
|
||||
@ -496,17 +386,10 @@ MozStackWalkThread(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
|
||||
{
|
||||
static HANDLE myProcess = nullptr;
|
||||
HANDLE myThread;
|
||||
DWORD walkerReturn;
|
||||
struct WalkStackData data;
|
||||
|
||||
InitializeDbgHelpCriticalSection();
|
||||
|
||||
// EnsureWalkThreadReady's _beginthreadex takes a heap lock and must be
|
||||
// avoided if we're walking another (i.e. suspended) thread.
|
||||
if (!aThread && !EnsureWalkThreadReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE targetThread = aThread;
|
||||
if (!aThread) {
|
||||
targetThread = ::GetCurrentThread();
|
||||
@ -555,50 +438,16 @@ MozStackWalkThread(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
|
||||
data.sp_size = ArrayLength(local_sps);
|
||||
data.context = aContext;
|
||||
|
||||
if (aThread) {
|
||||
// If we're walking the stack of another thread, we don't need to
|
||||
// use a separate walker thread.
|
||||
WalkStackMain64(&data);
|
||||
|
||||
if (data.pc_count > data.pc_size) {
|
||||
data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
|
||||
data.pc_size = data.pc_count;
|
||||
data.pc_count = 0;
|
||||
data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
|
||||
data.sp_size = data.sp_count;
|
||||
data.sp_count = 0;
|
||||
WalkStackMain64(&data);
|
||||
|
||||
if (data.pc_count > data.pc_size) {
|
||||
data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
|
||||
data.pc_size = data.pc_count;
|
||||
data.pc_count = 0;
|
||||
data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
|
||||
data.sp_size = data.sp_count;
|
||||
data.sp_count = 0;
|
||||
WalkStackMain64(&data);
|
||||
}
|
||||
} else {
|
||||
data.eventStart = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
|
||||
FALSE /* initially non-signaled */, nullptr);
|
||||
data.eventEnd = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
|
||||
FALSE /* initially non-signaled */, nullptr);
|
||||
|
||||
::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
|
||||
|
||||
walkerReturn = ::SignalObjectAndWait(data.eventStart,
|
||||
data.eventEnd, INFINITE, FALSE);
|
||||
if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) {
|
||||
PrintError("SignalObjectAndWait (1)");
|
||||
}
|
||||
if (data.pc_count > data.pc_size) {
|
||||
data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
|
||||
data.pc_size = data.pc_count;
|
||||
data.pc_count = 0;
|
||||
data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
|
||||
data.sp_size = data.sp_count;
|
||||
data.sp_count = 0;
|
||||
::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
|
||||
walkerReturn = ::SignalObjectAndWait(data.eventStart,
|
||||
data.eventEnd, INFINITE, FALSE);
|
||||
if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) {
|
||||
PrintError("SignalObjectAndWait (2)");
|
||||
}
|
||||
}
|
||||
|
||||
::CloseHandle(data.eventStart);
|
||||
::CloseHandle(data.eventEnd);
|
||||
}
|
||||
|
||||
::CloseHandle(myThread);
|
||||
|
Loading…
Reference in New Issue
Block a user