You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1122 lines
32 KiB
1122 lines
32 KiB
//----------------------------------------------------------------------------
|
|
//
|
|
// Stack walking support.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1997-2002.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "ntsdp.hpp"
|
|
|
|
BOOL g_AllowCorStack = TRUE;
|
|
BOOL g_DebugCorStack;
|
|
|
|
IMAGE_IA64_RUNTIME_FUNCTION_ENTRY g_EpcRfeBuffer;
|
|
PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY g_EpcRfe;
|
|
|
|
PFPO_DATA
|
|
SynthesizeKnownFpo(PSTR Symbol, ULONG64 OffStart, ULONG64 Disp)
|
|
{
|
|
static ULONG64 s_Nr2, s_Lu2, s_Eh3, s_Kuit;
|
|
|
|
if (!s_Nr2 || !s_Lu2 || !s_Eh3 || !s_Kuit)
|
|
{
|
|
GetOffsetFromSym(g_Process, "nt!_NLG_Return2", &s_Nr2, NULL);
|
|
GetOffsetFromSym(g_Process, "nt!_local_unwind2", &s_Lu2, NULL);
|
|
GetOffsetFromSym(g_Process, "nt!_except_handler3", &s_Eh3, NULL);
|
|
GetOffsetFromSym(g_Process,
|
|
"nt!KiUnexpectedInterruptTail", &s_Kuit, NULL);
|
|
}
|
|
|
|
if (OffStart == s_Nr2 || OffStart == s_Lu2)
|
|
{
|
|
static FPO_DATA s_Lu2Fpo;
|
|
|
|
s_Lu2Fpo.ulOffStart = (ULONG)OffStart;
|
|
s_Lu2Fpo.cbProcSize = 0x68;
|
|
s_Lu2Fpo.cdwLocals = 4;
|
|
s_Lu2Fpo.cdwParams = 0;
|
|
s_Lu2Fpo.cbProlog = 0;
|
|
s_Lu2Fpo.cbRegs = 3;
|
|
s_Lu2Fpo.fHasSEH = 0;
|
|
s_Lu2Fpo.fUseBP = 0;
|
|
s_Lu2Fpo.reserved = 0;
|
|
s_Lu2Fpo.cbFrame = FRAME_FPO;
|
|
return &s_Lu2Fpo;
|
|
}
|
|
else if (OffStart == s_Eh3)
|
|
{
|
|
static FPO_DATA s_Eh3Fpo;
|
|
|
|
s_Eh3Fpo.ulOffStart = (ULONG)OffStart;
|
|
s_Eh3Fpo.cbProcSize = 0xbd;
|
|
s_Eh3Fpo.cdwLocals = 2;
|
|
s_Eh3Fpo.cdwParams = 4;
|
|
s_Eh3Fpo.cbProlog = 3;
|
|
s_Eh3Fpo.cbRegs = 4;
|
|
s_Eh3Fpo.fHasSEH = 0;
|
|
s_Eh3Fpo.fUseBP = 0;
|
|
s_Eh3Fpo.reserved = 0;
|
|
s_Eh3Fpo.cbFrame = FRAME_NONFPO;
|
|
return &s_Eh3Fpo;
|
|
}
|
|
else if (OffStart == s_Kuit)
|
|
{
|
|
//
|
|
// KiUnexpectedInterruptTail has three special stubs
|
|
// following it for CommonDispatchException[0-2]Args.
|
|
// These stubs set up for the appropriate number of
|
|
// arguments and then call CommonDispatchException.
|
|
// They do not have symbols or FPO data so fake some
|
|
// up if we're in the region immediately after KUIT.
|
|
//
|
|
|
|
PFPO_DATA KuitData = (PFPO_DATA)
|
|
SymFunctionTableAccess(g_Process->m_SymHandle, OffStart);
|
|
if (KuitData != NULL &&
|
|
Disp >= (ULONG64)KuitData->cbProcSize &&
|
|
Disp < (ULONG64)KuitData->cbProcSize + 0x20)
|
|
{
|
|
static FPO_DATA s_CdeStubFpo;
|
|
|
|
s_CdeStubFpo.ulOffStart = (ULONG)OffStart;
|
|
s_CdeStubFpo.cbProcSize = 0x10;
|
|
s_CdeStubFpo.cdwLocals = 0;
|
|
s_CdeStubFpo.cdwParams = 0;
|
|
s_CdeStubFpo.cbProlog = 0;
|
|
s_CdeStubFpo.cbRegs = 0;
|
|
s_CdeStubFpo.fHasSEH = 0;
|
|
s_CdeStubFpo.fUseBP = 0;
|
|
s_CdeStubFpo.reserved = 0;
|
|
s_CdeStubFpo.cbFrame = FRAME_TRAP;
|
|
return &s_CdeStubFpo;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PFPO_DATA
|
|
SynthesizeFpoDataForModule(DWORD64 PCAddr)
|
|
{
|
|
DWORD64 Offset;
|
|
CHAR symbuf[MAX_SYMBOL_LEN];
|
|
|
|
GetSymbol(PCAddr, symbuf, sizeof(symbuf), &Offset);
|
|
|
|
if (Offset == PCAddr)
|
|
{
|
|
// No symbol.
|
|
return NULL;
|
|
}
|
|
|
|
PFPO_DATA KnownFpo =
|
|
SynthesizeKnownFpo(symbuf, PCAddr - Offset, Offset);
|
|
if (KnownFpo != NULL)
|
|
{
|
|
return KnownFpo;
|
|
}
|
|
|
|
// Not a known symbol so no FPO is available.
|
|
return NULL;
|
|
}
|
|
|
|
PFPO_DATA
|
|
SynthesizeFpoDataForFastSyscall(ULONG64 Offset)
|
|
{
|
|
static FPO_DATA s_FastFpo;
|
|
|
|
// XXX drewb - Temporary until the fake user-shared
|
|
// module is worked out.
|
|
|
|
s_FastFpo.ulOffStart = (ULONG)Offset;
|
|
s_FastFpo.cbProcSize = X86_SHARED_SYSCALL_SIZE;
|
|
s_FastFpo.cdwLocals = 0;
|
|
s_FastFpo.cdwParams = 0;
|
|
s_FastFpo.cbProlog = 0;
|
|
s_FastFpo.cbRegs = 0;
|
|
s_FastFpo.fHasSEH = 0;
|
|
s_FastFpo.fUseBP = 0;
|
|
s_FastFpo.reserved = 0;
|
|
s_FastFpo.cbFrame = FRAME_FPO;
|
|
return &s_FastFpo;
|
|
}
|
|
|
|
PFPO_DATA
|
|
ModifyFpoRecord(ImageInfo* Image, PFPO_DATA FpoData)
|
|
{
|
|
if (FpoData->cdwLocals == 80)
|
|
{
|
|
static ULONG64 s_CommonDispatchException;
|
|
|
|
// Some versions of CommonDispatchException have
|
|
// the wrong locals size, which screws up stack
|
|
// traces. Detect and fix up these problems.
|
|
if (s_CommonDispatchException == 0)
|
|
{
|
|
GetOffsetFromSym(g_Process,
|
|
"nt!CommonDispatchException",
|
|
&s_CommonDispatchException,
|
|
NULL);
|
|
}
|
|
|
|
if (Image->m_BaseOfImage + FpoData->ulOffStart ==
|
|
s_CommonDispatchException)
|
|
{
|
|
static FPO_DATA s_CdeFpo;
|
|
|
|
s_CdeFpo = *FpoData;
|
|
s_CdeFpo.cdwLocals = 20;
|
|
FpoData = &s_CdeFpo;
|
|
}
|
|
}
|
|
else if (FpoData->cdwLocals == 0 && FpoData->cdwParams == 0 &&
|
|
FpoData->cbRegs == 3)
|
|
{
|
|
static ULONG64 s_KiSwapThread;
|
|
|
|
// KiSwapThread has shrink-wrapping so that three registers
|
|
// are pushed in only a portion of the code. Unfortunately,
|
|
// the most important place in the code -- the call to
|
|
// KiSwapContext -- is outside of this region and therefore
|
|
// the register count is wrong much more often than it's
|
|
// correct. Switch the register count to two to make it
|
|
// correct more often than wrong.
|
|
if (s_KiSwapThread == 0)
|
|
{
|
|
GetOffsetFromSym(g_Process,
|
|
"nt!KiSwapThread", &s_KiSwapThread, NULL);
|
|
}
|
|
|
|
if (Image->m_BaseOfImage + FpoData->ulOffStart ==
|
|
s_KiSwapThread)
|
|
{
|
|
static FPO_DATA s_KstFpo;
|
|
|
|
s_KstFpo = *FpoData;
|
|
s_KstFpo.cbRegs = 2;
|
|
FpoData = &s_KstFpo;
|
|
}
|
|
}
|
|
else if (FpoData->fHasSEH)
|
|
{
|
|
static FPO_DATA s_SehFpo;
|
|
|
|
s_SehFpo = *FpoData;
|
|
s_SehFpo.cbFrame = FRAME_NONFPO;
|
|
FpoData = &s_SehFpo;
|
|
}
|
|
|
|
return FpoData;
|
|
}
|
|
|
|
PFPO_DATA
|
|
FindFpoDataForModule(DWORD64 PCAddr)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Locates the fpo data structure in the process's linked list for the
|
|
requested module.
|
|
|
|
Arguments:
|
|
|
|
PCAddr - address contained in the program counter
|
|
|
|
Return Value:
|
|
|
|
null - could not locate the entry
|
|
valid address - found the entry at the adress retured
|
|
|
|
--*/
|
|
{
|
|
ProcessInfo* Process;
|
|
ImageInfo* Image;
|
|
PFPO_DATA FpoData;
|
|
|
|
Process = g_Process;
|
|
Image = Process->m_ImageHead;
|
|
FpoData = 0;
|
|
while (Image)
|
|
{
|
|
if ((PCAddr >= Image->m_BaseOfImage) &&
|
|
(PCAddr < Image->m_BaseOfImage + Image->m_SizeOfImage))
|
|
{
|
|
FpoData = (PFPO_DATA)
|
|
SymFunctionTableAccess(g_Process->m_SymHandle, PCAddr);
|
|
if (!FpoData)
|
|
{
|
|
FpoData = SynthesizeFpoDataForModule(PCAddr);
|
|
}
|
|
else
|
|
{
|
|
FpoData = ModifyFpoRecord(Image, FpoData);
|
|
}
|
|
|
|
return FpoData;
|
|
}
|
|
|
|
Image = Image->m_Next;
|
|
}
|
|
|
|
ULONG64 FscBase;
|
|
|
|
switch(IsInFastSyscall(PCAddr, &FscBase))
|
|
{
|
|
case FSC_FOUND:
|
|
return SynthesizeFpoDataForFastSyscall(FscBase);
|
|
}
|
|
|
|
// the function is not part of any known loaded image
|
|
return NULL;
|
|
}
|
|
|
|
LPVOID
|
|
SwFunctionTableAccess(
|
|
HANDLE hProcess,
|
|
ULONG64 AddrBase
|
|
)
|
|
{
|
|
static IMAGE_IA64_RUNTIME_FUNCTION_ENTRY s_Ia64;
|
|
static _IMAGE_RUNTIME_FUNCTION_ENTRY s_Amd64;
|
|
|
|
PVOID pife;
|
|
|
|
if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
return (LPVOID)FindFpoDataForModule( AddrBase );
|
|
}
|
|
|
|
pife = SymFunctionTableAccess64(hProcess, AddrBase);
|
|
|
|
switch(g_Machine->m_ExecTypes[0])
|
|
{
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
if (pife)
|
|
{
|
|
s_Ia64 = *(PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY)pife;
|
|
return &s_Ia64;
|
|
}
|
|
else
|
|
{
|
|
if (IS_KERNEL_TARGET(g_Target) &&
|
|
(AddrBase >= IA64_MM_EPC_VA) &&
|
|
(AddrBase < (IA64_MM_EPC_VA + IA64_PAGE_SIZE)))
|
|
{
|
|
return g_EpcRfe;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
if (pife)
|
|
{
|
|
s_Amd64 = *(_PIMAGE_RUNTIME_FUNCTION_ENTRY)pife;
|
|
return &s_Amd64;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
DWORD64
|
|
SwTranslateAddress(
|
|
HANDLE hProcess,
|
|
HANDLE hThread,
|
|
LPADDRESS64 lpaddress
|
|
)
|
|
{
|
|
//
|
|
// don't support 16bit stacks
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
BOOL
|
|
SwReadMemory(
|
|
HANDLE hProcess,
|
|
ULONG64 BaseAddress,
|
|
LPVOID lpBuffer,
|
|
DWORD nSize,
|
|
LPDWORD lpNumberOfBytesRead
|
|
)
|
|
{
|
|
DBG_ASSERT(hProcess == OS_HANDLE(g_Process->m_SysHandle));
|
|
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
DWORD BytesRead;
|
|
HRESULT Status;
|
|
|
|
if ((LONG_PTR)lpNumberOfBytesRead == -1)
|
|
{
|
|
if (g_Target->m_MachineType == IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
BaseAddress += g_Target->m_TypeInfo.SizeTargetContext;
|
|
}
|
|
|
|
Status = g_Target->ReadControl(CURRENT_PROC,
|
|
(ULONG)BaseAddress,
|
|
lpBuffer,
|
|
nSize,
|
|
&BytesRead);
|
|
return Status == S_OK;
|
|
}
|
|
}
|
|
|
|
if (g_Target->ReadVirtual(g_Process, BaseAddress, lpBuffer, nSize,
|
|
lpNumberOfBytesRead) != S_OK)
|
|
{
|
|
// Make sure bytes read is zero.
|
|
if (lpNumberOfBytesRead != NULL)
|
|
{
|
|
*lpNumberOfBytesRead = 0;
|
|
}
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
SwReadMemory32(
|
|
HANDLE hProcess,
|
|
ULONG dwBaseAddress,
|
|
LPVOID lpBuffer,
|
|
DWORD nSize,
|
|
LPDWORD lpNumberOfBytesRead
|
|
)
|
|
{
|
|
return SwReadMemory(hProcess,
|
|
EXTEND64(dwBaseAddress),
|
|
lpBuffer,
|
|
nSize,
|
|
lpNumberOfBytesRead);
|
|
}
|
|
|
|
DWORD64
|
|
GetKernelModuleBase(
|
|
ULONG64 Address
|
|
)
|
|
{
|
|
ModuleInfo* ModIter;
|
|
|
|
if (!(ModIter = g_Target->GetModuleInfo(FALSE)) ||
|
|
ModIter->Initialize(g_Thread) != S_OK)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// We only want module base and size.
|
|
ModIter->m_InfoLevel = MODULE_INFO_BASE_SIZE;
|
|
|
|
for (;;)
|
|
{
|
|
MODULE_INFO_ENTRY ModEntry;
|
|
|
|
ZeroMemory(&ModEntry, sizeof(ModEntry));
|
|
if (ModIter->GetEntry(&ModEntry) != S_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Address >= ModEntry.Base &&
|
|
Address < ModEntry.Base + ModEntry.Size)
|
|
{
|
|
return ModEntry.Base;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD64
|
|
SwGetModuleBase(
|
|
HANDLE hProcess,
|
|
ULONG64 Address
|
|
)
|
|
{
|
|
if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_IA64 &&
|
|
IS_KERNEL_TARGET(g_Target) &&
|
|
(Address >= IA64_MM_EPC_VA) &&
|
|
(Address < (IA64_MM_EPC_VA + IA64_PAGE_SIZE)))
|
|
{
|
|
Address -= (IA64_MM_EPC_VA - g_Target->m_SystemCallVirtualAddress);
|
|
}
|
|
|
|
ImageInfo* Image = g_Process->FindImageByOffset(Address, TRUE);
|
|
if (Image)
|
|
{
|
|
return Image->m_BaseOfImage;
|
|
}
|
|
|
|
// This might be the JIT output for managed code.
|
|
// There's no 'base' as such but we need to return
|
|
// something non-zero to indicate this code is known.
|
|
// There won't be any FPO information so the module base
|
|
// value isn't that important. Just return the actual address.
|
|
if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_I386 &&
|
|
g_Process->IsCorCode(Address) == S_OK)
|
|
{
|
|
return Address;
|
|
}
|
|
|
|
// If no regular module was found we need to look in
|
|
// the dynamic function tables to see if an entry
|
|
// there matches.
|
|
ULONG64 DynBase = g_Target->
|
|
GetDynamicFunctionTableBase(g_Process, Address);
|
|
if (DynBase)
|
|
{
|
|
return DynBase;
|
|
}
|
|
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
// If no modules have been loaded there's still a possibility
|
|
// of getting a kernel stack trace (without symbols) by going
|
|
// after the module base directly. This also makes it possible
|
|
// to get a stack trace when there are no symbols available.
|
|
|
|
if (g_Process->m_ImageHead == NULL)
|
|
{
|
|
return GetKernelModuleBase( Address );
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD
|
|
SwGetModuleBase32(
|
|
HANDLE hProcess,
|
|
DWORD Address
|
|
)
|
|
{
|
|
return (DWORD)SwGetModuleBase(hProcess, Address);
|
|
}
|
|
|
|
|
|
void
|
|
PrintStackTraceHeaderLine(
|
|
ULONG Flags
|
|
)
|
|
{
|
|
if ( (Flags & DEBUG_STACK_COLUMN_NAMES) == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
StartOutLine(DEBUG_OUTPUT_NORMAL, OUT_LINE_NO_TIMESTAMP);
|
|
|
|
if (Flags & DEBUG_STACK_FRAME_NUMBERS)
|
|
{
|
|
dprintf(" # ");
|
|
}
|
|
|
|
if (Flags & DEBUG_STACK_FRAME_MEMORY_USAGE)
|
|
{
|
|
dprintf(" Memory ");
|
|
}
|
|
|
|
if (Flags & DEBUG_STACK_FRAME_ADDRESSES)
|
|
{
|
|
g_Machine->PrintStackFrameAddressesTitle(Flags);
|
|
}
|
|
|
|
if (Flags & DEBUG_STACK_ARGUMENTS)
|
|
{
|
|
g_Machine->PrintStackArgumentsTitle(Flags);
|
|
}
|
|
|
|
g_Machine->PrintStackCallSiteTitle(Flags);
|
|
|
|
dprintf("\n");
|
|
}
|
|
|
|
VOID
|
|
PrintStackFrame(
|
|
PDEBUG_STACK_FRAME StackFrame,
|
|
PDEBUG_STACK_FRAME PrevFrame,
|
|
ULONG Flags
|
|
)
|
|
{
|
|
ULONG64 Displacement;
|
|
ULONG64 InstructionOffset = StackFrame->InstructionOffset;
|
|
SYMBOL_INFO_AND_NAME SymInfo;
|
|
|
|
if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_IA64 &&
|
|
IS_KERNEL_TARGET(g_Target) &&
|
|
(InstructionOffset >= IA64_MM_EPC_VA) &&
|
|
(InstructionOffset < (IA64_MM_EPC_VA + IA64_PAGE_SIZE)))
|
|
{
|
|
InstructionOffset = InstructionOffset -
|
|
(IA64_MM_EPC_VA - g_Target->m_SystemCallVirtualAddress);
|
|
}
|
|
|
|
GetSymbolInfo(InstructionOffset, NULL, 0,
|
|
SymInfo, &Displacement);
|
|
|
|
StartOutLine(DEBUG_OUTPUT_NORMAL, OUT_LINE_NO_TIMESTAMP);
|
|
|
|
if (Flags & DEBUG_STACK_FRAME_NUMBERS)
|
|
{
|
|
dprintf("%02lx ", StackFrame->FrameNumber);
|
|
}
|
|
|
|
if (Flags & DEBUG_STACK_FRAME_MEMORY_USAGE)
|
|
{
|
|
if (PrevFrame)
|
|
{
|
|
g_Machine->PrintStackFrameMemoryUsage(StackFrame, PrevFrame);
|
|
}
|
|
else
|
|
{
|
|
dprintf(" ");
|
|
}
|
|
}
|
|
|
|
if (Flags & DEBUG_STACK_FRAME_ADDRESSES)
|
|
{
|
|
g_Machine->PrintStackFrameAddresses(Flags, StackFrame);
|
|
}
|
|
|
|
if (Flags & DEBUG_STACK_ARGUMENTS)
|
|
{
|
|
g_Machine->PrintStackArguments(Flags, StackFrame);
|
|
}
|
|
|
|
g_Machine->PrintStackCallSite(Flags, StackFrame,
|
|
SymInfo, SymInfo->Name,
|
|
Displacement);
|
|
|
|
if (Flags & DEBUG_STACK_SOURCE_LINE)
|
|
{
|
|
OutputLineAddr(InstructionOffset, " [%s @ %d]");
|
|
}
|
|
|
|
dprintf( "\n" );
|
|
}
|
|
|
|
BOOL
|
|
CheckFrameValidity(PDEBUG_STACK_FRAME Frame)
|
|
{
|
|
//
|
|
// If the current frame's IP is not in any loaded module
|
|
// it's likely that we won't be able to unwind.
|
|
//
|
|
// If the current frame's IP is in a loaded module and
|
|
// that module does not have symbols it's very possible
|
|
// that the stack trace will be incorrect since the
|
|
// debugger has to guess about how to unwind the stack.
|
|
// Non-x86 architectures have unwind info in the images
|
|
// themselves so restrict this check to x86.
|
|
//
|
|
|
|
if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_I386 &&
|
|
Frame->InstructionOffset != -1 &&
|
|
!Frame->FuncTableEntry &&
|
|
g_Process->IsCorCode(Frame->InstructionOffset) != S_OK)
|
|
{
|
|
IMAGEHLP_MODULE64 Mod;
|
|
|
|
Mod.SizeOfStruct = sizeof(Mod);
|
|
if (!SymGetModuleInfo64(g_Process->m_SymHandle,
|
|
Frame->InstructionOffset, &Mod))
|
|
{
|
|
WarnOut("WARNING: Frame IP not in any known module. "
|
|
"Following frames may be wrong.\n");
|
|
return TRUE;
|
|
}
|
|
else if (Mod.SymType == SymNone || Mod.SymType == SymExport ||
|
|
Mod.SymType == SymDeferred)
|
|
{
|
|
WarnOut("WARNING: Stack unwind information not available. "
|
|
"Following frames may be wrong.\n");
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
PrintStackTrace(
|
|
ULONG NumFrames,
|
|
PDEBUG_STACK_FRAME StackFrames,
|
|
ULONG Flags
|
|
)
|
|
{
|
|
ULONG i;
|
|
BOOL SymWarning = FALSE;
|
|
|
|
PrintStackTraceHeaderLine(Flags);
|
|
|
|
for (i = 0; i < NumFrames; i++)
|
|
{
|
|
if (!SymWarning && NumFrames > 1)
|
|
{
|
|
SymWarning = CheckFrameValidity(StackFrames + i);
|
|
}
|
|
|
|
PrintStackFrame(StackFrames + i,
|
|
i > 0 ? (StackFrames + (i - 1)) : NULL,
|
|
Flags);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
UnwindCorFrame(ICorDataStackWalk* CorStack,
|
|
PCROSS_PLATFORM_CONTEXT Context,
|
|
PDEBUG_STACK_FRAME DbgFrame,
|
|
LPSTACKFRAME64 VirtFrame)
|
|
{
|
|
HRESULT Status;
|
|
ADDRESS64 PreInstr, PreStack, PreFrame;
|
|
ADDRESS64 PostInstr, PostStack, PostFrame;
|
|
CorDataFrameType CorFrameType;
|
|
|
|
g_Machine->
|
|
GetStackDefaultsFromContext(Context,
|
|
&PreInstr, &PreStack, &PreFrame);
|
|
|
|
if ((Status = CorStack->
|
|
SetFrameContext(g_Target->m_TypeInfo.SizeTargetContext,
|
|
(BYTE*)Context)) != S_OK ||
|
|
(Status = CorStack->
|
|
GetFrameDescription(&CorFrameType, NULL, 0)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (CorFrameType != DAC_FRAME_COR_METHOD_FRAME)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
if ((Status = CorStack->UnwindFrame()) != S_OK ||
|
|
(Status = CorStack->
|
|
GetFrameContext(g_Target->m_TypeInfo.SizeTargetContext,
|
|
(BYTE*)Context)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
g_Machine->
|
|
GetStackDefaultsFromContext(Context,
|
|
&PostInstr, &PostStack, &PostFrame);
|
|
|
|
if (g_DebugCorStack)
|
|
{
|
|
dprintf(" COR Pre i %08X s %08X f %08x\n"
|
|
" Post i %08X s %08X f %08x\n",
|
|
(ULONG)PreInstr.Offset,
|
|
(ULONG)PreStack.Offset,
|
|
(ULONG)PreFrame.Offset,
|
|
(ULONG)PostInstr.Offset,
|
|
(ULONG)PostStack.Offset,
|
|
(ULONG)PostFrame.Offset);
|
|
}
|
|
|
|
DbgFrame->InstructionOffset = PreInstr.Offset;
|
|
DbgFrame->ReturnOffset = PostInstr.Offset;
|
|
DbgFrame->FrameOffset = PreFrame.Offset;
|
|
DbgFrame->StackOffset = PreStack.Offset;
|
|
DbgFrame->FuncTableEntry = 0;
|
|
DbgFrame->Virtual = FALSE;
|
|
|
|
ZeroMemory(DbgFrame->Reserved,
|
|
sizeof(DbgFrame->Reserved));
|
|
ZeroMemory(DbgFrame->Params,
|
|
sizeof(DbgFrame->Params));
|
|
|
|
// Prepare the StackWalk64 frame in case it's
|
|
// used later. Setting Virtual to FALSE should
|
|
// force the stack walker to reinitialize.
|
|
VirtFrame->Virtual = FALSE;
|
|
VirtFrame->AddrPC = PostInstr;
|
|
VirtFrame->AddrFrame = PostFrame;
|
|
VirtFrame->AddrStack = PostStack;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DWORD
|
|
StackTrace(
|
|
DebugClient* Client,
|
|
ULONG64 FramePointer,
|
|
ULONG64 StackPointer,
|
|
ULONG64 InstructionPointer,
|
|
ULONG PointerDefaults,
|
|
PDEBUG_STACK_FRAME StackFrames,
|
|
ULONG NumFrames,
|
|
ULONG64 ExtThread,
|
|
ULONG Flags,
|
|
BOOL EstablishingScope
|
|
)
|
|
{
|
|
STACKFRAME64 VirtualFrame;
|
|
DWORD i;
|
|
CROSS_PLATFORM_CONTEXT Context;
|
|
BOOL SymWarning = FALSE;
|
|
ULONG X86Ebp;
|
|
ULONG Seg;
|
|
ADDRESS64 DefInstr, DefStack, DefFrame;
|
|
HRESULT Status;
|
|
ICorDataStackWalk* CorStack;
|
|
|
|
if (!EstablishingScope)
|
|
{
|
|
RequireCurrentScope();
|
|
}
|
|
|
|
if (g_Machine->GetContextState(MCTX_FULL) != S_OK)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Context = *GetScopeOrMachineContext();
|
|
g_Machine->GetStackDefaultsFromContext(&Context, &DefInstr,
|
|
&DefStack, &DefFrame);
|
|
|
|
//
|
|
// let's start clean
|
|
//
|
|
ZeroMemory( StackFrames, sizeof(StackFrames[0]) * NumFrames );
|
|
ZeroMemory( &VirtualFrame, sizeof(VirtualFrame) );
|
|
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
//
|
|
// if debugger was initialized at boot, usermode addresses needed for
|
|
// stack traces on IA64 were not available. Try it now:
|
|
//
|
|
|
|
if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_IA64 &&
|
|
!g_Target->m_KdDebuggerData.KeUserCallbackDispatcher)
|
|
{
|
|
g_Target->QueryKernelInfo(g_Thread, FALSE);
|
|
}
|
|
|
|
ULONG64 ThreadData;
|
|
|
|
// If no explicit thread is given then we use the
|
|
// current thread. However, the current thread is only
|
|
// valid if the current thread is the event thread since
|
|
// tracing back into user mode requires that the appropriate
|
|
// user-mode memory state be active.
|
|
if (ExtThread != 0)
|
|
{
|
|
ThreadData = ExtThread;
|
|
}
|
|
else if (g_Thread != g_EventThread ||
|
|
g_Process != g_EventProcess ||
|
|
g_Process->
|
|
GetImplicitThreadData(g_Thread, &ThreadData) != S_OK)
|
|
{
|
|
ThreadData = 0;
|
|
}
|
|
|
|
VirtualFrame.KdHelp.Thread = ThreadData;
|
|
VirtualFrame.KdHelp.ThCallbackStack = ThreadData ?
|
|
g_Target->m_KdDebuggerData.ThCallbackStack : 0;
|
|
VirtualFrame.KdHelp.KiCallUserMode =
|
|
g_Target->m_KdDebuggerData.KiCallUserMode;
|
|
VirtualFrame.KdHelp.NextCallback =
|
|
g_Target->m_KdDebuggerData.NextCallback;
|
|
VirtualFrame.KdHelp.KeUserCallbackDispatcher =
|
|
g_Target->m_KdDebuggerData.KeUserCallbackDispatcher;
|
|
VirtualFrame.KdHelp.FramePointer =
|
|
g_Target->m_KdDebuggerData.FramePointer;
|
|
VirtualFrame.KdHelp.SystemRangeStart = g_Target->m_SystemRangeStart;
|
|
}
|
|
|
|
//
|
|
// setup the program counter
|
|
//
|
|
if (PointerDefaults & STACK_INSTR_DEFAULT)
|
|
{
|
|
VirtualFrame.AddrPC = DefInstr;
|
|
}
|
|
else
|
|
{
|
|
VirtualFrame.AddrPC.Mode = AddrModeFlat;
|
|
Seg = g_Machine->GetSegRegNum(SEGREG_CODE);
|
|
VirtualFrame.AddrPC.Segment =
|
|
Seg ? (WORD)g_Machine->FullGetVal32(Seg) : 0;
|
|
VirtualFrame.AddrPC.Offset = InstructionPointer;
|
|
}
|
|
|
|
//
|
|
// setup the frame pointer
|
|
//
|
|
if (PointerDefaults & STACK_FRAME_DEFAULT)
|
|
{
|
|
VirtualFrame.AddrFrame = DefFrame;
|
|
}
|
|
else
|
|
{
|
|
VirtualFrame.AddrFrame.Mode = AddrModeFlat;
|
|
Seg = g_Machine->GetSegRegNum(SEGREG_STACK);
|
|
VirtualFrame.AddrFrame.Segment =
|
|
Seg ? (WORD)g_Machine->FullGetVal32(Seg) : 0;
|
|
VirtualFrame.AddrFrame.Offset = FramePointer;
|
|
}
|
|
VirtualFrame.AddrBStore = VirtualFrame.AddrFrame;
|
|
X86Ebp = (ULONG)VirtualFrame.AddrFrame.Offset;
|
|
|
|
//
|
|
// setup the stack pointer
|
|
//
|
|
if (PointerDefaults & STACK_STACK_DEFAULT)
|
|
{
|
|
VirtualFrame.AddrStack = DefStack;
|
|
}
|
|
else
|
|
{
|
|
VirtualFrame.AddrStack.Mode = AddrModeFlat;
|
|
Seg = g_Machine->GetSegRegNum(SEGREG_STACK);
|
|
VirtualFrame.AddrStack.Segment =
|
|
Seg ? (WORD)g_Machine->FullGetVal32(Seg) : 0;
|
|
VirtualFrame.AddrStack.Offset = StackPointer;
|
|
}
|
|
|
|
if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_IA64 &&
|
|
IS_KERNEL_TARGET(g_Target) &&
|
|
g_Target->m_SystemCallVirtualAddress)
|
|
{
|
|
PVOID FunctionEntry;
|
|
|
|
FunctionEntry = SwFunctionTableAccess
|
|
(g_Process->m_SymHandle, g_Target->m_SystemCallVirtualAddress);
|
|
if (FunctionEntry != NULL)
|
|
{
|
|
RtlCopyMemory(&g_EpcRfeBuffer, FunctionEntry,
|
|
sizeof(IMAGE_IA64_RUNTIME_FUNCTION_ENTRY));
|
|
g_EpcRfe = &g_EpcRfeBuffer;
|
|
}
|
|
else
|
|
{
|
|
g_EpcRfe = NULL;
|
|
}
|
|
}
|
|
|
|
if (g_AllowCorStack &&
|
|
g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
// XXX drewb - No good way to know what runtime thread
|
|
// to use, so always use the current one.
|
|
CorStack = g_Process->StartCorStack(0);
|
|
}
|
|
else
|
|
{
|
|
// Non-x86 stacks can always be walked by StackWalk64
|
|
// because of high-quality unwind information.
|
|
CorStack = NULL;
|
|
}
|
|
|
|
for (i = 0; i < NumFrames; i++)
|
|
{
|
|
Status = S_FALSE;
|
|
|
|
if (g_DebugCorStack)
|
|
{
|
|
dprintf("Frame %d\n", i);
|
|
}
|
|
|
|
StackFrames[i].FrameNumber = i;
|
|
|
|
if (i > 0 && CorStack)
|
|
{
|
|
if (FAILED(Status = UnwindCorFrame(CorStack,
|
|
&Context,
|
|
&StackFrames[i],
|
|
&VirtualFrame)))
|
|
{
|
|
ErrOut("Managed frame unwind failed, %s\n",
|
|
FormatStatusCode(Status));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Status == S_FALSE)
|
|
{
|
|
// SwReadMemory doesn't currently use the thread handle
|
|
// but send in something reasonable in case of future changes.
|
|
if (!StackWalk64(g_Machine->m_ExecTypes[0],
|
|
OS_HANDLE(g_Process->m_SysHandle),
|
|
OS_HANDLE(g_Thread->m_Handle),
|
|
&VirtualFrame,
|
|
&Context,
|
|
SwReadMemory,
|
|
SwFunctionTableAccess,
|
|
SwGetModuleBase,
|
|
SwTranslateAddress))
|
|
{
|
|
break;
|
|
}
|
|
|
|
StackFrames[i].InstructionOffset = VirtualFrame.AddrPC.Offset;
|
|
StackFrames[i].ReturnOffset = VirtualFrame.AddrReturn.Offset;
|
|
StackFrames[i].FrameOffset = VirtualFrame.AddrFrame.Offset;
|
|
StackFrames[i].StackOffset = VirtualFrame.AddrStack.Offset;
|
|
StackFrames[i].FuncTableEntry =
|
|
(ULONG64)VirtualFrame.FuncTableEntry;
|
|
StackFrames[i].Virtual = VirtualFrame.Virtual;
|
|
|
|
// NOTE - we have more reserved space in the DEBUG_STACK_FRAME
|
|
memcpy(StackFrames[i].Reserved, VirtualFrame.Reserved,
|
|
sizeof(VirtualFrame.Reserved));
|
|
memcpy(StackFrames[i].Params, VirtualFrame.Params,
|
|
sizeof(VirtualFrame.Params));
|
|
|
|
if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_IA64 &&
|
|
IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
if ((VirtualFrame.AddrPC.Offset >= IA64_MM_EPC_VA) &&
|
|
(VirtualFrame.AddrPC.Offset <
|
|
(IA64_MM_EPC_VA + IA64_PAGE_SIZE)))
|
|
{
|
|
VirtualFrame.AddrPC.Offset -=
|
|
(IA64_MM_EPC_VA -
|
|
g_Target->m_SystemCallVirtualAddress);
|
|
}
|
|
|
|
if ((i != 0) &&
|
|
(StackFrames[i - 1].InstructionOffset >= IA64_MM_EPC_VA) &&
|
|
(VirtualFrame.AddrPC.Offset <
|
|
(IA64_MM_EPC_VA + IA64_PAGE_SIZE)))
|
|
{
|
|
StackFrames[i - 1].ReturnOffset =
|
|
VirtualFrame.AddrPC.Offset;
|
|
}
|
|
}
|
|
else if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
if (StackFrames[i].FuncTableEntry)
|
|
{
|
|
PFPO_DATA FpoData = (PFPO_DATA)
|
|
StackFrames[i].FuncTableEntry;
|
|
if ((FpoData->cbFrame == FRAME_FPO) && X86Ebp &&
|
|
(!FpoData->fUseBP &&
|
|
((SAVE_EBP(&StackFrames[i]) >> 32) != 0xEB)))
|
|
{
|
|
// EBP tag, so stack walker doesn't get confused
|
|
SAVE_EBP(&StackFrames[i]) = X86Ebp + 0xEB00000000;
|
|
}
|
|
}
|
|
|
|
X86Ebp = Context.X86Context.Ebp;
|
|
}
|
|
}
|
|
|
|
if (Flags)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
PrintStackTraceHeaderLine(Flags);
|
|
}
|
|
|
|
if (!SymWarning && NumFrames > 1)
|
|
{
|
|
SymWarning = CheckFrameValidity(StackFrames + i);
|
|
}
|
|
|
|
PrintStackFrame(StackFrames + i,
|
|
i > 0 ? (StackFrames + (i - 1)) : NULL,
|
|
Flags);
|
|
|
|
if (Flags & DEBUG_STACK_NONVOLATILE_REGISTERS)
|
|
{
|
|
g_Machine->PrintStackNonvolatileRegisters(Flags,
|
|
StackFrames + i,
|
|
&Context, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
RELEASE(CorStack);
|
|
return i;
|
|
}
|
|
|
|
VOID
|
|
DoStackTrace(
|
|
DebugClient* Client,
|
|
ULONG64 FramePointer,
|
|
ULONG64 StackPointer,
|
|
ULONG64 InstructionPointer,
|
|
ULONG PointerDefaults,
|
|
ULONG NumFrames,
|
|
ULONG TraceFlags
|
|
)
|
|
{
|
|
PDEBUG_STACK_FRAME StackFrames;
|
|
ULONG NumFramesToRead;
|
|
DWORD FrameCount;
|
|
|
|
if (NumFrames == 0)
|
|
{
|
|
NumFrames = g_DefaultStackTraceDepth;
|
|
}
|
|
|
|
if (TraceFlags & RAW_STACK_DUMP)
|
|
{
|
|
DBG_ASSERT(TraceFlags == RAW_STACK_DUMP);
|
|
NumFramesToRead = 1;
|
|
}
|
|
else
|
|
{
|
|
NumFramesToRead = NumFrames;
|
|
}
|
|
|
|
StackFrames = (PDEBUG_STACK_FRAME)
|
|
malloc( sizeof(StackFrames[0]) * NumFramesToRead );
|
|
if (!StackFrames)
|
|
{
|
|
ErrOut( "could not allocate memory for stack trace\n" );
|
|
return;
|
|
}
|
|
|
|
if (g_Machine->m_Ptr64 &&
|
|
(TraceFlags & DEBUG_STACK_ARGUMENTS) &&
|
|
!(TraceFlags & DEBUG_STACK_FUNCTION_INFO))
|
|
{
|
|
TraceFlags |= DEBUG_STACK_FRAME_ADDRESSES_RA_ONLY;
|
|
}
|
|
|
|
FrameCount = StackTrace(Client,
|
|
FramePointer,
|
|
StackPointer,
|
|
InstructionPointer,
|
|
PointerDefaults,
|
|
StackFrames,
|
|
NumFramesToRead,
|
|
0,
|
|
TraceFlags,
|
|
FALSE);
|
|
|
|
if (FrameCount == 0)
|
|
{
|
|
ErrOut( "could not fetch any stack frames\n" );
|
|
free(StackFrames);
|
|
return;
|
|
}
|
|
|
|
if (TraceFlags & RAW_STACK_DUMP)
|
|
{
|
|
// Starting with the frame pointer, dump NumFrames DWORD's
|
|
// and the symbol if possible.
|
|
|
|
ADDR StartAddr;
|
|
ADDRFLAT(&StartAddr, StackFrames[0].FrameOffset);
|
|
|
|
DumpDwordMemory(&StartAddr, NumFrames, TRUE);
|
|
}
|
|
|
|
free(StackFrames);
|
|
}
|