Bug 1384819 (part 1) - Split MozStackWalk(). r=glandium.

MozStackWalk() is different on Windows to the other platforms. It has two extra
arguments, which can be used to walk the stack of a different thread.

This patch makes those differences clearer. Instead of having a single function
and forbidding those two arguments on non-Windows, it removes those arguments
from MozStackWalk, and splits off MozStackWalkThread() which retains them. This
also allows those arguments to have more appropriate types (HANDLE instead of
uintptr_t; CONTEXT* instead of than void*) and names (aContext instead of
aPlatformData).

The patch also removes unnecessary reinterpret_casts for the aClosure argument
at a couple of MozStackWalk() callsites.

--HG--
extra : rebase_source : 111ab7d6426d7be921facc2264f6db86c501d127
This commit is contained in:
Nicholas Nethercote 2017-07-27 12:46:47 +10:00
parent 1ebc47776f
commit 08e54b7c13
14 changed files with 104 additions and 90 deletions

View File

@ -811,8 +811,7 @@ StackTrace::Get(Thread* aT)
#else
int skipFrames = 2;
#endif
bool ok = MozStackWalk(StackWalkCallback, skipFrames, MaxFrames, &tmp, 0,
nullptr);
bool ok = MozStackWalk(StackWalkCallback, skipFrames, MaxFrames, &tmp);
#endif
if (!ok) {
tmp.mLength = 0; // re-zero in case the stack walk function changed it
@ -1631,7 +1630,7 @@ Init(const malloc_table_t* aMallocTable)
// just call MozStackWalk, because that calls StackWalkInitCriticalAddress().
// See the comment above StackWalkInitCriticalAddress() for more details.
(void)MozStackWalk(NopStackWalkCallback, /* skipFrames */ 0,
/* maxFrames */ 1, nullptr, 0, nullptr);
/* maxFrames */ 1, nullptr);
#endif
gStateLock = InfallibleAllocPolicy::new_<Mutex>();

View File

@ -110,7 +110,7 @@ my_malloc_logger(uint32_t aType,
// stack shows up as having two pthread_cond_wait$UNIX2003 frames.
const char* name = "new_sem_from_pool";
MozStackWalk(stack_callback, /* skipFrames */ 0, /* maxFrames */ 0,
const_cast<char*>(name), 0, nullptr);
const_cast<char*>(name));
}
// This is called from NS_LogInit() and from the stack walking functions, but
@ -186,7 +186,7 @@ StackWalkInitCriticalAddress()
}
#endif
#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code
#if MOZ_STACKWALK_SUPPORTS_WINDOWS
#include <windows.h>
#include <process.h>
@ -222,7 +222,7 @@ struct WalkStackData
void** sps;
uint32_t sp_size;
uint32_t sp_count;
void* platformData;
CONTEXT* context;
};
DWORD gStackWalkThread;
@ -378,18 +378,20 @@ static void
WalkStackMain64(struct WalkStackData* aData)
{
// Get a context for the specified thread.
CONTEXT context;
if (!aData->platformData) {
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(aData->thread, &context)) {
CONTEXT context_buf;
CONTEXT* context;
if (!aData->context) {
context = &context_buf;
memset(context, 0, sizeof(CONTEXT));
context->ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(aData->thread, context)) {
if (aData->walkCallingThread) {
PrintError("GetThreadContext");
}
return;
}
} else {
context = *static_cast<CONTEXT*>(aData->platformData);
context = aData->context;
}
#if defined(_M_IX86) || defined(_M_IA64)
@ -397,13 +399,13 @@ WalkStackMain64(struct WalkStackData* aData)
STACKFRAME64 frame64;
memset(&frame64, 0, sizeof(frame64));
#ifdef _M_IX86
frame64.AddrPC.Offset = context.Eip;
frame64.AddrStack.Offset = context.Esp;
frame64.AddrFrame.Offset = context.Ebp;
frame64.AddrPC.Offset = context->Eip;
frame64.AddrStack.Offset = context->Esp;
frame64.AddrFrame.Offset = context->Ebp;
#elif defined _M_IA64
frame64.AddrPC.Offset = context.StIIP;
frame64.AddrStack.Offset = context.SP;
frame64.AddrFrame.Offset = context.RsBSP;
frame64.AddrPC.Offset = context->StIIP;
frame64.AddrStack.Offset = context->SP;
frame64.AddrFrame.Offset = context->RsBSP;
#endif
frame64.AddrPC.Mode = AddrModeFlat;
frame64.AddrStack.Mode = AddrModeFlat;
@ -452,7 +454,7 @@ WalkStackMain64(struct WalkStackData* aData)
aData->process,
aData->thread,
&frame64,
&context,
context,
nullptr,
SymFunctionTableAccess64, // function table access routine
SymGetModuleBase64, // module base routine
@ -479,8 +481,8 @@ WalkStackMain64(struct WalkStackData* aData)
// If we reach a frame in JIT code, we don't have enough information to
// unwind, so we have to give up.
if (sJitCodeRegionStart &&
(uint8_t*)context.Rip >= sJitCodeRegionStart &&
(uint8_t*)context.Rip < sJitCodeRegionStart + sJitCodeRegionSize) {
(uint8_t*)context->Rip >= sJitCodeRegionStart &&
(uint8_t*)context->Rip < sJitCodeRegionStart + sJitCodeRegionSize) {
break;
}
@ -488,8 +490,8 @@ WalkStackMain64(struct WalkStackData* aData)
// unwind data, so their JIT unwind callback just throws up its hands and
// terminates the process.
if (sMsMpegJitCodeRegionStart &&
(uint8_t*)context.Rip >= sMsMpegJitCodeRegionStart &&
(uint8_t*)context.Rip < sMsMpegJitCodeRegionStart + sMsMpegJitCodeRegionSize) {
(uint8_t*)context->Rip >= sMsMpegJitCodeRegionStart &&
(uint8_t*)context->Rip < sMsMpegJitCodeRegionStart + sMsMpegJitCodeRegionSize) {
break;
}
@ -497,30 +499,30 @@ WalkStackMain64(struct WalkStackData* aData)
// Try to look up unwind metadata for the current function.
ULONG64 imageBase;
PRUNTIME_FUNCTION runtimeFunction =
RtlLookupFunctionEntry(context.Rip, &imageBase, NULL);
RtlLookupFunctionEntry(context->Rip, &imageBase, NULL);
if (runtimeFunction) {
PVOID dummyHandlerData;
ULONG64 dummyEstablisherFrame;
RtlVirtualUnwind(UNW_FLAG_NHANDLER,
imageBase,
context.Rip,
context->Rip,
runtimeFunction,
&context,
context,
&dummyHandlerData,
&dummyEstablisherFrame,
nullptr);
} else if (firstFrame) {
// Leaf functions can be unwound by hand.
context.Rip = *reinterpret_cast<DWORD64*>(context.Rsp);
context.Rsp += sizeof(void*);
context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp);
context->Rsp += sizeof(void*);
} else {
// Something went wrong.
break;
}
addr = context.Rip;
spaddr = context.Rsp;
addr = context->Rip;
spaddr = context->Rsp;
firstFrame = false;
#else
#error "unknown platform"
@ -618,9 +620,9 @@ WalkStackThread(void* aData)
*/
MFBT_API bool
MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
void* aPlatformData)
MozStackWalkThread(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
uint32_t aMaxFrames, void* aClosure,
HANDLE aThread, CONTEXT* aContext)
{
StackWalkInitCriticalAddress();
static HANDLE myProcess = nullptr;
@ -637,8 +639,7 @@ MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
}
HANDLE currentThread = ::GetCurrentThread();
HANDLE targetThread =
aThread ? reinterpret_cast<HANDLE>(aThread) : currentThread;
HANDLE targetThread = aThread ? aThread : currentThread;
data.walkCallingThread = (targetThread == currentThread);
// Have to duplicate handle to get a real handle.
@ -677,7 +678,7 @@ MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
data.sps = local_sps;
data.sp_count = 0;
data.sp_size = ArrayLength(local_sps);
data.platformData = aPlatformData;
data.context = aContext;
if (aThread) {
// If we're walking the stack of another thread, we don't need to
@ -734,6 +735,13 @@ MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
return data.pc_count != 0;
}
MFBT_API bool
MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
uint32_t aMaxFrames, void* aClosure)
{
return MozStackWalkThread(aCallback, aSkipFrames, aMaxFrames, aClosure,
nullptr, nullptr);
}
static BOOL CALLBACK
callbackEspecial64(
@ -989,11 +997,8 @@ void DemangleSymbol(const char* aSymbol,
MFBT_API bool
MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
void* aPlatformData)
uint32_t aMaxFrames, void* aClosure)
{
MOZ_ASSERT(!aThread);
MOZ_ASSERT(!aPlatformData);
StackWalkInitCriticalAddress();
// Get the frame pointer
@ -1077,11 +1082,8 @@ unwind_callback(struct _Unwind_Context* context, void* closure)
MFBT_API bool
MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
void* aPlatformData)
uint32_t aMaxFrames, void* aClosure)
{
MOZ_ASSERT(!aThread);
MOZ_ASSERT(!aPlatformData);
StackWalkInitCriticalAddress();
unwind_info info;
info.callback = aCallback;
@ -1150,11 +1152,8 @@ MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
MFBT_API bool
MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
void* aPlatformData)
uint32_t aMaxFrames, void* aClosure)
{
MOZ_ASSERT(!aThread);
MOZ_ASSERT(!aPlatformData);
return false;
}

View File

@ -15,7 +15,7 @@
#include <stdint.h>
/**
* The callback for MozStackWalk.
* The callback for MozStackWalk and MozStackWalkThread.
*
* @param aFrameNumber The frame number (starts at 1, not 0).
* @param aPC The program counter value.
@ -23,7 +23,8 @@
* pointer will be pointing to when the execution returns
* to executing that at aPC. If no approximation can
* be made it will be nullptr.
* @param aClosure Extra data passed in via MozStackWalk().
* @param aClosure Extra data passed in from MozStackWalk() or
* MozStackWalkThread().
*/
typedef void
(*MozWalkStackCallback)(uint32_t aFrameNumber, void* aPC, void* aSP,
@ -39,24 +40,46 @@ typedef void
* MozStackWalk.
* @param aMaxFrames Maximum number of frames to trace. 0 means no limit.
* @param aClosure Caller-supplied data passed through to aCallback.
* @param aThread The thread for which the stack is to be retrieved.
* Passing null causes us to walk the stack of the
* current thread. On Windows, this is a thread HANDLE.
* It is currently not supported on any other platform.
* @param aPlatformData Platform specific data that can help in walking the
* stack, this should be nullptr unless you really know
* what you're doing! This needs to be a pointer to a
* CONTEXT on Windows and should not be passed on other
* platforms.
*
* May skip some stack frames due to compiler optimizations or code
* generation.
*
*/
MFBT_API bool
MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
void* aPlatformData);
uint32_t aMaxFrames, void* aClosure);
#if defined(_WIN32) && \
(defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64))
#include <windows.h>
#define MOZ_STACKWALK_SUPPORTS_WINDOWS 1
/**
* Like MozStackWalk, but walks the stack for another thread.
* Call aCallback for the C/C++ stack frames on the current thread, from
* the caller of MozStackWalk to main (or above).
*
* @param aCallback Same as for MozStackWalk().
* @param aSkipFrames Same as for MozStackWalk().
* @param aMaxFrames Same as for MozStackWalk().
* @param aClosure Same as for MozStackWalk().
* @param aThread The handle of the thread whose stack is to be walked.
* If 0, walks the current thread.
* @param aContext A CONTEXT, presumably obtained with GetThreadContext()
* after suspending the thread with SuspendThread(). If
* null, the CONTEXT will be re-obtained.
*/
MFBT_API bool
MozStackWalkThread(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
uint32_t aMaxFrames, void* aClosure,
HANDLE aThread, CONTEXT* aContext);
#else
#define MOZ_STACKWALK_SUPPORTS_WINDOWS 0
#endif
typedef struct
{

View File

@ -59,7 +59,7 @@ Log(const char* aMessageType,
if (sStackTraceDepth) {
msgStream << std::endl << "Stack Trace:";
MozStackWalk(StackFrameToOStringStream, aFramesToSkip, sStackTraceDepth,
&msgStream, 0, nullptr);
&msgStream);
}
}

View File

@ -98,8 +98,7 @@ SandboxLogCStack()
// can't walk past the signal trampoline on ARM (bug 968531), and
// x86 frame pointer walking may or may not work (bug 1082276).
MozStackWalk(SandboxPrintStackFrame, /* skip */ 3, /* max */ 0,
nullptr, 0, nullptr);
MozStackWalk(SandboxPrintStackFrame, /* skip */ 3, /* max */ 0, nullptr);
SANDBOX_LOG_ERROR("end of stack.");
}

View File

@ -98,8 +98,7 @@ void KeyedStackCapturer::Capture(const nsACString& aKey) {
static_cast<std::vector<uintptr_t>*>(aClosure);
stack->push_back(reinterpret_cast<uintptr_t>(aPC));
};
MozStackWalk(callback, /* skipFrames */ 0,
/* maxFrames */ 0, reinterpret_cast<void*>(&rawStack), 0, nullptr);
MozStackWalk(callback, /* skipFrames */ 0, /* maxFrames */ 0, &rawStack);
ProcessedStack stack = GetStackAndModules(rawStack);
// Store the new stack info.

View File

@ -89,8 +89,7 @@ ah_crap_handler(int signum)
signum);
printf("Stack:\n");
MozStackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0,
nullptr, 0, nullptr);
MozStackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, nullptr);
printf("Sleeping for %d seconds.\n",_gdb_sleep_duration);
printf("Type 'gdb %s %d' to attach your debugger to this thread.\n",

View File

@ -91,10 +91,10 @@ private:
HANDLE mProfiledThread;
};
uintptr_t
HANDLE
GetThreadHandle(PlatformData* aData)
{
return (uintptr_t) aData->ProfiledThread();
return aData->ProfiledThread();
}
static const HANDLE kNoThread = INVALID_HANDLE_VALUE;

View File

@ -1001,7 +1001,7 @@ MergeStacks(uint32_t aFeatures, bool aIsSynchronous,
}
#if defined(GP_OS_windows)
static uintptr_t GetThreadHandle(PlatformData* aData);
static HANDLE GetThreadHandle(PlatformData* aData);
#endif
#if defined(USE_FRAME_POINTER_STACK_WALK) || defined(USE_MOZ_STACK_WALK)
@ -1024,9 +1024,9 @@ DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
// cannot rely on ActivePS.
// Start with the current function. We use 0 as the frame number here because
// the FramePointerStackWalk() and MozStackWalk() calls below will use 1..N.
// This is a bit weird but it doesn't matter because StackWalkCallback()
// doesn't use the frame number argument.
// the FramePointerStackWalk() and MozStackWalkThread() calls below will use
// 1..N. This is a bit weird but it doesn't matter because
// StackWalkCallback() doesn't use the frame number argument.
StackWalkCallback(/* frameNum */ 0, aRegs.mPC, aRegs.mSP, &aNativeStack);
uint32_t maxFrames = uint32_t(MAX_NATIVE_FRAMES - aNativeStack.mCount);
@ -1039,10 +1039,10 @@ DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
stackEnd);
}
#elif defined(USE_MOZ_STACK_WALK)
uintptr_t thread = GetThreadHandle(aThreadInfo.GetPlatformData());
HANDLE thread = GetThreadHandle(aThreadInfo.GetPlatformData());
MOZ_ASSERT(thread);
MozStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, &aNativeStack,
thread, /* platformData */ nullptr);
MozStackWalkThread(StackWalkCallback, /* skipFrames */ 0, maxFrames,
&aNativeStack, thread, /* context */ nullptr);
#else
# error "bad configuration"
#endif

View File

@ -879,8 +879,7 @@ RecordStackFrame(uint32_t /*aFrameNumber*/, void* aPC, void* /*aSP*/,
void
nsTraceRefcnt::WalkTheStack(FILE* aStream)
{
MozStackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream,
0, nullptr);
MozStackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream);
}
/**
@ -898,7 +897,7 @@ WalkTheStackCached(FILE* aStream)
gCodeAddressService = new WalkTheStackCodeAddressService();
}
MozStackWalk(PrintStackFrameCached, /* skipFrames */ 2, /* maxFrames */ 0,
aStream, 0, nullptr);
aStream);
}
static void
@ -911,8 +910,7 @@ WalkTheStackSavingLocations(std::vector<void*>& aLocations)
0 + // this frame gets inlined
1 + // GetSerialNumber
1; // NS_LogCtor
MozStackWalk(RecordStackFrame, kFramesToSkip, /* maxFrames */ 0,
&aLocations, 0, nullptr);
MozStackWalk(RecordStackFrame, kFramesToSkip, /* maxFrames */ 0, &aLocations);
}
//----------------------------------------------------------------------

View File

@ -125,7 +125,7 @@ LateWriteObserver::Observe(IOInterposeObserver::Observation& aOb)
std::vector<uintptr_t> rawStack;
MozStackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
reinterpret_cast<void*>(&rawStack), 0, nullptr);
&rawStack);
Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
nsPrintfCString nameAux("%s%s%s", mProfileDirectory,

View File

@ -86,8 +86,7 @@ MaybeRecordCurrentStack(DWORD aTlsIndex)
auto stack = static_cast<stack_t*>(aClosure);
stack->AppendElement(reinterpret_cast<uintptr_t>(aPC));
};
MozStackWalk(callback, /* skip 2 frames */ 2,
/* maxFrames */ 0, &rawStack, 0, nullptr);
MozStackWalk(callback, /* skip 2 frames */ 2, /* maxFrames */ 0, &rawStack);
StaticMutexAutoLock lock(sMutex);
if (!sRecentTlsAllocationStacks) {

View File

@ -70,7 +70,7 @@ BlockingResourceBase::GetStackTrace(AcquisitionState& aState)
// NB: Ignore the return value, there's nothing useful we can do if this
// this fails.
MozStackWalk(StackWalkCallback, kSkipFrames, 24, &aState, 0, nullptr);
MozStackWalk(StackWalkCallback, kSkipFrames, 24, &aState);
#endif
}

View File

@ -169,9 +169,8 @@ GetChromeHangReport(Telemetry::ProcessedStack& aStack,
return;
}
MozStackWalk(ChromeStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
reinterpret_cast<void*>(&rawStack),
reinterpret_cast<uintptr_t>(winMainThreadHandle), nullptr);
MozStackWalkThread(ChromeStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
&rawStack, winMainThreadHandle, nullptr);
ret = ::ResumeThread(winMainThreadHandle);
if (ret == -1) {
return;