2008/5/9
这两天研究了一下C/C++ call stack traces,遂写了个小程序来输出一下call stack,该程序比较简单,只能输出调用栈上的函数名称,至于复杂点的输出请看下面2008/5/10处的修改 。该程序用到了Dbghelp.dll 相信各位达人都知道Dbghelp,这里就不多说了。采用__cdecl 堆栈调用标准,在VC++ 6.0下编译通过,下面贴出代码。
#include
<
stdio.h
>
#include
<
windows.h
>
#include
<
Dbghelp.h
>
#define
JMP_INSTR 0xE9
#define
SYM_BUFF_SIZE 512
#pragma comment(lib,"imagehlp.lib")
static
BYTE g_stSymbol [ SYM_BUFF_SIZE ] ;
void
CallStack(
void
)
{
int
retAddrs;
int
saved_ebp;
int
callFuncAddrs;
HANDLE hCurProcess;
//
Fill the IMAGEHLP_SYMBOL struct
PIMAGEHLP_SYMBOL pSym
=
(PIMAGEHLP_SYMBOL)
&
g_stSymbol ;
FillMemory ( pSym , NULL , SYM_BUFF_SIZE ) ;
pSym
->
SizeOfStruct
=
sizeof
( IMAGEHLP_SYMBOL ) ;
pSym
->
MaxNameLength
=
SYM_BUFF_SIZE
-
sizeof
( IMAGEHLP_SYMBOL);
//
Initialize & load the symbol table
hCurProcess
=
(HANDLE)::GetCurrentProcessId();
if
(
!
::SymInitialize(hCurProcess, NULL, TRUE))
{
return ;
}
__asm
{
mov eax, dword ptr [ebp + 4 ] // Get the return address
mov retAddrs, eax
mov eax, dword ptr [ebp]
mov saved_ebp, eax
}
printf(
"
CallSatck dump begin...
"
);
while
(saved_ebp
>
0
)
{
if (retAddrs != 0 )
{
callFuncAddrs = * ( int * )(retAddrs - 4 ) + retAddrs;
if ((unsigned int )callFuncAddrs > 0x80000000 )
{
break ;
}
// Jump over the JMP instruction to the real function address
while (JMP_INSTR == * (unsigned char * )callFuncAddrs)
{
int offset = callFuncAddrs + 1 ;
callFuncAddrs = * ( int * )(offset) + (callFuncAddrs + 5 );
}
if ( ! ::SymGetSymFromAddr(hCurProcess, (DWORD)callFuncAddrs, NULL, pSym))
{
break ;
}
// printf("0x%08X <--", callFuncAddrs);
printf( " %s( ) " , pSym -> Name);
}
retAddrs = * ( int * )(saved_ebp + 4 );
saved_ebp = * ( int * )saved_ebp;
}
printf(
"
CallSatck dump end!
"
);
}
class
CTest
{
public
:
void
Hello()
{
printf( " Hello World " );
CallStack();
}
;
void
SetPrivateVal(
int
nVal)
{
y = nVal;
}
public
:
int
x;
private
:
int
y;
}
;
int
Call_C(
int
a,
int
b)
{
CallStack();
return
(a
+
b);
}
int
Call_B(
int
a,
int
b)
{
return
Call_C(a, b);
}
int
Call_A(
int
a,
int
b)
{
return
Call_B(a, b);
}
//
#include <assert.h>
//
#include <BugsLayerUtil.h>
main()
{
//
Call_A(10, 20);
CTest test;
test.Hello();
return
0
;
}
控制台输出如下信息:
Hello World
CallSatck dump begin...
CallStack( )
CTest::Hello( )
main( )
CallSatck dump end !
Press any key to continue
如果改一下main( ) 函数如:
main()
{
Call_A( 10 , 20 );
// CTest test;
// test.Hello();
return 0 ;
}
则控制台输出如下信息
CallSatck dump begin...
CallStack( )
Call_C( )
Call_B( )
Call_A( )
main( )
CallSatck dump end !
Press any key to continue
2008/5/10
今天把程序修改了一下,可以输出函数所传递的实参个数及值,还增加了函数调用处的偏移量,总之输出有点像VC++ 6.0种调试时候的call stack,不过函数的参数类型我还是没有得到。本来想通过读取符号表中的decorated symbols 来自己undecorated 一下就知道了各个参数的类型,可不管我怎么调用SymSetOptions()函数进行设置也不好用,只能先搁一下了。当然,能通过反汇编来识别出参数类 型,但这么做有点太繁琐了。
修改后控制台能输出如下信息:
CallSatck dump begin...
StackTrace( )
Call_C( 0x0000000A , 0x00000014 ) line 466 + 0 bytes
Call_B( 0x0000000A , 0x00000014 ) line 471 + 13 bytes
Call_A( 0x0000000A , 0x00000014 ) line 476 + 13 bytes
main( 0x00000001 , 0x003913A0 , 0x003913F8 ) line 484 + 9 bytes
CallSatck dump end !
Press any key to continue
说了这么多,贴个修改后的代码先。(仍需要完善......)
#include < stdio.h >
#include < windows.h >
#include < Dbghelp.h >
#define JMP_INSTR 0xE9
#define SYM_BUFF_SIZE 512
#pragma comment(lib,"imagehlp.lib")
static BYTE g_stSymbol [ SYM_BUFF_SIZE ] ;
int GetFuncArgsNum( int retAddrs)
{
if ( 0xC483 == * (WORD * )retAddrs) // 0xC483 ''Add esp, xxx''
return * (BYTE * )(retAddrs + 2 ) >> 2 ;
else
return 0 ;
}
int GetFuncArgVal( int ebp, int index)
{
return * ( int * )(ebp + ((index + 2 ) << 2 ));
}
int GetCallFuncAddrs( int retAddrs)
{
int callFuncAddrs = * ( int * )(retAddrs - 4 ) + retAddrs;
// callFuncAddrs > 0x80000000 can cause the ''can not read the memory'' error!
if ((DWORD)callFuncAddrs > 0x80000000 )
{
return 0 ;
}
// Jump over the JMP instruction to the real address of function
while (JMP_INSTR == * (BYTE * )callFuncAddrs)
{
int offset = callFuncAddrs + 1 ;
callFuncAddrs = * ( int * )(offset) + (callFuncAddrs + 5 );
}
return callFuncAddrs;
}
void PrintCallFuncOffset( int retAddrs)
{
HANDLE hCurProcess = (HANDLE) ::GetCurrentProcessId();
static saved_retAddrs = 0 ;
if ( 0 == saved_retAddrs)
{
saved_retAddrs = retAddrs;
printf( " %s " , " " );
return ;
}
DWORD dwDisp;
IMAGEHLP_LINE stLine;
FillMemory( & stLine, NULL, sizeof (IMAGEHLP_LINE));
stLine.SizeOfStruct = sizeof (stLine);
if ( ! ::SymGetLineFromAddr(hCurProcess, (DWORD)saved_retAddrs, & dwDisp, & stLine))
{
printf( " %s " , " " );
return ;
}
printf( " line %d + %d bytes " , stLine.LineNumber, dwDisp);
saved_retAddrs = retAddrs;
}
void PrintCallFunc( int ebp)
{
int callFuncAddrs;
DWORD dwDisp = 0 ;
int retAddrs = * ( int * )(ebp + 4 );
HANDLE hCurProcess = (HANDLE) ::GetCurrentProcessId();
PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL) & g_stSymbol ;
int argsNum = ::GetFuncArgsNum(retAddrs);
callFuncAddrs = GetCallFuncAddrs(retAddrs);
if ( ! ::SymGetSymFromAddr(hCurProcess, (DWORD)callFuncAddrs, & dwDisp, pSym))
{
return ;
}
// Print the name of call function
printf( " %s( " , pSym -> Name);
// Print the args
if (argsNum > 0 )
{
argsNum -- ;
for ( int i = 0 ; i < argsNum; i ++ )
{
printf( " 0x%08X, " , GetFuncArgVal(ebp, i));
}
// print the last arg.
printf( " 0x%08X ) " , GetFuncArgVal(ebp, i));
}
else
{
printf( " %s " , " ) " );
}
// Print the line number
PrintCallFuncOffset(retAddrs);
}
void StackTrace( void )
{
int saved_ebp;
int cur_ebp;
HANDLE hCurProcess;
// Fill the IMAGEHLP_SYMBOL struct
PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL) & g_stSymbol ;
FillMemory ( pSym , NULL , SYM_BUFF_SIZE ) ;
pSym -> SizeOfStruct = sizeof ( IMAGEHLP_SYMBOL ) ;
pSym -> MaxNameLength = SYM_BUFF_SIZE - sizeof ( IMAGEHLP_SYMBOL );
// Initialize & load the symbol table
hCurProcess = (HANDLE)::GetCurrentProcessId();
::SymSetOptions ( SYMOPT_UNDNAME | SYMOPT_LOAD_LINES ) ;
if ( ! ::SymInitialize(hCurProcess, NULL, TRUE)) // Load the module automatically
{
return ;
}
__asm
{
// Get the current ebp
mov cur_ebp, ebp
}
// Get the saved ebp
saved_ebp = * ( int * )cur_ebp;
// Print the call stack
printf( " StackTrace dump begin... " );
while (saved_ebp != 0 )
{
PrintCallFunc(cur_ebp);
// Move to the next caller''s stack frame
cur_ebp = saved_ebp;
saved_ebp = * ( int * )saved_ebp;
}
printf( " StackTrace dump end! " );
::SymCleanup(hCurProcess);
}
class CTest
{
public :
void Hello()
{
StackTrace();
} ;
} ;
int Call_C( int a, int b)
{
StackTrace();
return (a + b);
}
int Call_B( int a, int b)
{
return Call_C(a, b);
}
int Call_A( int a, int b)
{
return Call_B(a, b);
}
main()
{
Call_A( 10 , 20 );
// CTest test;
// test.Hello();
return 0 ;
}
闻香止步 收集于:http://www.diybl.com/course/3_program/c++/cppjs/2008510/115247.html
淘宝店 摆件 饰品 *木雕系列*:海南黄花梨、越南黄花梨、草花梨、小叶紫檀、黑檀、绿檀木、黄杨木、桃木发簪 木梳 樟木壁挂 佛珠 车饰 摆件
http://shop36570193.taobao.com
朋友,有空来看看,喜欢的朋友请收藏