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
朋友,有空來看看,喜歡的朋友請收藏