使用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 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可以看到,查看AppStack有两种方法。使用内联汇编或者是使用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.
     
下面是运行这个代码打印出的调用代码示意图:

r_aaaa.JPG



posted on 2008-04-18 14:58  lbq1221119  阅读(3835)  评论(3编辑  收藏  举报

导航