使用DbgHelp獲取函數(shù)調(diào)用堆棧之inline assembly(內(nèi)聯(lián)匯編)法
如果想自己獲取應(yīng)用程序的Call Stack,就需要查看Stack的內(nèi)容。Stack Walker,在最近查看SSCLI源碼的時候發(fā)現(xiàn)這個東西是和Stack Frame緊密聯(lián)系在一起的。
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 is StackWalk64. 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有兩種方法。使用內(nèi)聯(lián)匯編或者是使用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的關(guān)于這個方法的詳細(xì)使用。
Kevin Lynx也給做了關(guān)于使用這個方法來獲取調(diào)用堆棧的例子:
http://www.cppblog.com/kevinlynx/archive/2008/03/28/45628.html
這里,就給出一個采用內(nèi)聯(lián)匯編來獲取App調(diào)用堆棧的例子,這個例子里面,由于調(diào)試生成的符號文件沒有加載好,故而Mudule的信息不能很好的顯示出來,不過這個例子很好的演示了使用內(nèi)聯(lián)匯編來獲取Stack Frame,從而print出整個函數(shù)的調(diào)用堆棧來,同時也是一個很好的使用DbgHelp來獲取調(diào)試信息的例子:
#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;
}
//采用內(nèi)聯(lián)匯編獲取當(dāng)前stack Frame地址和當(dāng)前程序指令地址.
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;
}
兩個程序種需要用到的比較重要的結(jié)構(gòu)體,一個是關(guān)于某個方法Symbol信息的
typedef struct _SYMBOL_INFO,另外一個是關(guān)于模塊信息的,
typedef struct _IMAGEHLP_MODULE64.
posted on 2008-04-18 14:58 lbq1221119 閱讀(3899) 評論(3) 收藏 舉報
浙公網(wǎng)安備 33010602011771號