使用DbgHelp獲取函式呼叫堆疊之inline assembly(內聯彙編)法
如果想自己獲取應用程式的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.
下面是執行這個程式碼列印出的呼叫程式碼示意圖:
相關文章
- Solidity之旅(十八)內聯彙編 [inline assembly]Solidinline
- 函式呼叫中堆疊的個人理解函式
- 從彙編角度來理解linux下多層函式呼叫堆疊執行狀態Linux函式
- PHP列印呼叫函式入口地址(堆疊),方便調式PHP函式
- 內聯(inline)函式與虛擬函式(virtual)的討論inline函式
- PHP-stacktrace: PHP 程式外檢視函式呼叫堆疊PHP函式
- AT&T彙編之使用C庫函式函式
- php 獲取函式被呼叫位置PHP函式
- GCC 內聯彙編GC
- Java獲取堆疊資訊的3種方法Java
- [golang]如何看懂呼叫堆疊Golang
- C++建構函式和解構函式呼叫虛擬函式時使用靜態聯編C++函式
- C++ 反彙編:關於函式呼叫約定C++函式
- 彙編中引數的傳遞和堆疊修正(轉)
- 解讀 JavaScript 之引擎、執行時和堆疊呼叫JavaScript
- 函式內聯函式
- 在Visual C++中使用內聯彙編C++
- C 語言宏 + 內聯彙編實現 MIPS 系統呼叫
- GCC內聯彙編(1)Get startedGC
- 在Visual C++中使用內聯彙編 (轉)C++
- 在Visual C++中使用內聯彙編(轉)C++
- 泛型鏈式堆疊泛型
- JS中函式內套函式的呼叫JS函式
- C++程式中不同函式呼叫方式的彙編碼比較C++函式
- 高階語言反彙編程式的函式呼叫過程 (轉)函式
- iOS安全–使用static inline方式編譯函式,防止靜態分析iOSinline編譯函式
- php獲取遠端檔案內容的函式PHP函式
- C語言的本質(32)——C語言與彙編之C語言內聯彙編C語言
- javascript如何獲取內聯樣式和外部樣式表的值JavaScript
- 【CORE】在UNIX環境下從核心檔案獲取堆疊資訊
- PLSQL獲取異常堆疊資訊-dbms_utility.format_error_backtraceSQLORMError
- 怎樣在程式中獲取所有執行緒的堆疊資訊?執行緒
- GCC內聯彙編(2)GCC生成彙編程式碼簡單例項GC單例
- 讀懂作業系統(x86)之堆疊幀(過程呼叫)作業系統
- 讀懂作業系統(x64)之堆疊幀(過程呼叫)作業系統
- AWK高階之內部函式使用函式
- VC++ 崩潰處理以及列印呼叫堆疊C++
- 關於inline函式inline函式