如果想自己获取应用程序的Call Stack,就需要查看Stack的内容。Stack Walker,在最近查看SSCLI源码的时候发现这个东西是和Stack Frame紧密联系在一起的。
Walking the Stack
We could conceivably attempt to unwind the stack ourselves using inline assembly. But stack frames can be organized in different ways, depending on compiler optimizations and calling conventions, so it could become complicated to do it that way. Once again, Microsoft has provided us with a tool to help us out. This time it is a function that we can call iteratively to walk the stack, frame by frame. That function isStackWalk64. It is part of the Debug Help Library (dbghelp.dll). As long as we provide it with the information that it needs to establish a starting "frame of reference", so to speak, it can examine our stack from there and reliably unwind it for us. Each time StackWalk64 is called, it gives back a STACKFRAME64 structure that can be reused as input for the next call to StackWalk64. It can be repeatedly called this way until the end of the stack is reached.
从上面的Walking the Stack可以看到,查看App的Stack有两种方法。使用内联汇编或者是使用DbgHelp库里面的StackWalk64方法。
找到DbgHelp里面是StackWalk64:
BOOL
IMAGEAPI
StackWalk64(
DWORD |
MachineType, |
HANDLE |
hProcess, |
HANDLE |
hThread, |
LPSTACKFRAME64 |
StackFrame, |
PVOID |
ContextRecord, |
PREAD_PROCESS_MEMORY_ROUTINE64 |
ReadMemoryRoutine, |
PFUNCTION_TABLE_ACCESS_ROUTINE64 |
FunctionTableAccessRoutine, |
PGET_MODULE_BASE_ROUTINE64 |
GetModuleBaseRoutine, |
PTRANSLATE_ADDRESS_ROUTINE64 |
TranslateAddress |
);
可以参考MSDN里面:http://msdn2.microsoft.com/en-us/library/ms680650(VS.85).aspx的关于这个方法的详细使用。
Kevin Lynx也给做了关于使用这个方法来获取调用堆栈的例子:
http://www.cppblog.com/kevinlynx/archive/2008/03/28/45628.html
这里,就给出一个采用内联汇编来获取
App
调用堆栈的例子,这个例子里面,由于调试生成的符号文件没有加载好,故而
Mudule
的信息不能很好的显示出来,不过这个例子很好的演示了使用内联汇编来获取
Stack Frame
,从而
print
出整个函数的调用堆栈来,同时也是一个很好的使用
DbgHelp
来获取调试信息的例子:
#include "stdafx.h"
#include <windows.h>
#include <dbghelp.h>
#define INVALID_FP_RET_ADDR_VALUE 0x00000000
BOOL g_fSymInit;
HANDLE g_hProcess;
//address of the founction stack-call to walk.
BOOL DisplaySymbolDetails(DWORD dwAddress)
{
DWORD64 displacement = 0;
ULONG64 buffer[(sizeof(SYMBOL_INFO) +
MAX_SYM_NAME*sizeof(TCHAR) +
sizeof(ULONG64) - 1) /
sizeof(ULONG64)];
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
if (SymFromAddr(g_hProcess,dwAddress,&displacement,pSymbol))
{
// Try to get the Module details
IMAGEHLP_MODULE64 moduleinfo;
moduleinfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
if (SymGetModuleInfo64(g_hProcess,pSymbol->Address,&moduleinfo))
{
printf("%s!",moduleinfo.ModuleName);
}
else
{
printf("<ErrorModuleInfo_%d>!", GetLastError());
}
// now print the function name
if (pSymbol->MaxNameLen > 0)
{
printf("%s",pSymbol->Name);
}
else
{
printf("<Unknown_Function>");
}
}
else
{
printf(" <Unable to get symbol details_%d>", GetLastError());
}
return TRUE;
}
//采用内联汇编获取当前stack Frame地址和当前程序指令地址.
bool WalkTheStack()
{
DWORD _ebp = INVALID_FP_RET_ADDR_VALUE;
DWORD dwIPOfCurrentFunction = (DWORD)&WalkTheStack;
// Get the current Frame pointer
__asm
{
mov [_ebp], ebp
}
// We cannot walk the stack (yet!) without a frame pointer
if (_ebp == INVALID_FP_RET_ADDR_VALUE)
return false;
printf("CurFP\t\t\tRetAddr\n");
//current Frame Pointer
DWORD *pCurFP = (DWORD *)_ebp;
BOOL fFirstFP = TRUE;
while (pCurFP != INVALID_FP_RET_ADDR_VALUE)
{
// pointer arithmetic works in terms of type pointed to. Thus,
// "+1" below is equivalent of 4 bytes since we are doing DWORD
// math.
// Find Caller,next print.
DWORD pRetAddrInCaller = (*((DWORD *)(pCurFP + 1)));
printf("%p\t\t%p ",pCurFP, (DWORD *)pRetAddrInCaller);
if (g_fSymInit)
{
if (fFirstFP)
{
fFirstFP = FALSE;
}
DisplaySymbolDetails(dwIPOfCurrentFunction);
// To get the name of the next function up the stack,
// we use the return address of the current frame
dwIPOfCurrentFunction = pRetAddrInCaller;
}
printf("\n");
if (pRetAddrInCaller == INVALID_FP_RET_ADDR_VALUE)
{
// StackWalk is over now...
break;
}
// move up the stack to our caller
DWORD pCallerFP = *((DWORD *)pCurFP);
pCurFP = (DWORD *)pCallerFP;
}
return true;
}
int _tmain(int argc, _TCHAR* argv[])
{
// Initialize the debugger services to retrieve detailed stack info
g_fSymInit = FALSE;
g_hProcess = GetCurrentProcess();
if (!SymInitialize(g_hProcess, NULL,TRUE))
{
printf("Unable to initialize symbols!\n\n");
}
g_fSymInit = TRUE;
//SYMOPT_UNDNAME:All symbols are presented in undecorated form.
//SYMOPT_INCLUDE_32BIT_MODULES:
//When debugging on 64-bit Windows, include any 32-bit modules.
//SYMOPT_ALLOW_ABSOLUTE_SYMBOLS:
//Enables the use of symbols that are stored with absolute addresses. instead of RAVS forms.
SymSetOptions(SYMOPT_UNDNAME|SYMOPT_INCLUDE_32BIT_MODULES|SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
if (WalkTheStack() == false)
printf("Stackwalk failed!\n");
return 0;
}
两个程序种需要用到的比较重要的结构体,一个是关于某个方法Symbol信息的
typedef struct _SYMBOL_INFO,另外一个是关于模块信息的,
typedef struct _IMAGEHLP_MODULE64.
下面是运行这个代码打印出的调用代码示意图: