etrace 跟踪程序函数动态执行流程

时间:2021-10-19 05:55:47
https://github.com/elcritch/etrace

窗口1: 监控窗口,执行监控程序,显示监控结果

[root@monitor example]# pwd
/root/etrace-master/example [root@monitor example]# ll
total
-rwxr-xr-x root root Jun : crumble
-rw-r--r-- root root Aug crumble.c
-rw-r--r-- root root Aug Makefile [root@monitor example]# ../src/etrace crumble [root@monitor example]# ll
total
-rwxr-xr-x root root Jun : crumble
-rw-r--r-- root root Aug crumble.c
-rw-r--r-- root root Aug Makefile
prw-r--r-- root root Jun : TRACE
窗口2:执行程序
[root@monitor example]# ./crumble
buy 125 grams of sugar
buy 125 grams of butter
buy 200 grams of wheat
buy 1 pinch of salt
buy 5 pieces of apple
skin apples and make little dices
mix butter with sugar, wheat and salt
put apples below
put crumble on top
put apple crumble in oven
cook apple crumble at 220 degrees for 45 minutes
窗口1:显示程序监控结果     //etrace跟据 TRACE 这个文件中的 记录的函数调用地址 查找对应 crumble中的函数符号名,并生调用图
[root@monitor example]# ../src/etrace crumble    \--main
   |    \--Crumble_make_apple_crumble
   |    |       \--Crumble_buy_stuff
   |    |       |       \--Crumble_buy
   |    |       |       \--Crumble_buy (total: 5 times)
   |    |       \--Crumble_prepare_apples
   |    |       |       \--Crumble_skin_and_dice
   |    |       \--Crumble_mix
   |    |       \--Crumble_finalize
   |    |       |       \--Crumble_put
   |    |       |       \--Crumble_put (total: 2 times)
   |    |       \--Crumble_cook
   |    |       |       \--Crumble_put
   |    |       |       \--Crumble_bake
原理:
[root@monitor example]# cat Makefile CC = gcc
CFLAGS = -g -finstrument-functions --std=gnu99 crumble: crumble.c ../src/ptrace.c
        $(CC) $(CFLAGS) -o crumble crumble.c ../src/ptrace.c run:
        touch TRACE ;
        ./crumble
        ../src/etrace crumble
        rm -f TRACE clean:
        rm -f crumble TRACE [root@monitor example]# make //执行原理
gcc -g -finstrument-functions --std=gnu99 -o crumble crumble.c ../src/ptrace.c
单窗口也可以显示跟踪结果
[root@monitor example]# make run
touch TRACE ;
./crumble
buy grams of sugar
buy grams of butter
buy grams of wheat
buy pinch of salt
buy pieces of apple
skin apples and make little dices
mix butter with sugar, wheat and salt
put apples below
put crumble on top
put apple crumble in oven
cook apple crumble at degrees for minutes
../src/etrace crumble \--main
| \--Crumble_make_apple_crumble
| | \--Crumble_buy_stuff
| | | \--Crumble_buy
| | | \--Crumble_buy (total: times)
| | \--Crumble_prepare_apples
| | | \--Crumble_skin_and_dice
| | \--Crumble_mix
| | \--Crumble_finalize
| | | \--Crumble_put
| | | \--Crumble_put (total: times)
| | \--Crumble_cook
| | | \--Crumble_put
| | | \--Crumble_bake
rm -f TRACE
执行原理:
[root@monitor example]# touch TRACE
[root@monitor example]# ./crumble
buy grams of sugar
buy grams of butter
buy grams of wheat
buy pinch of salt
buy pieces of apple
skin apples and make little dices
mix butter with sugar, wheat and salt
put apples below
put crumble on top
put apple crumble in oven
cook apple crumble at degrees for minutes
[root@monitor example]# ll
total
-rwxr-xr-x root root Jun : crumble
-rw-r--r-- root root Jun : crumble.c
-rw-r--r-- root root Jun : Makefile
-rw-r--r-- root root Jun : TRACE
[root@monitor example]# cat TRACE
enter 0x400beb
enter 0x400b88
enter 0x400904
enter 0x4008a4
enter 0x400ba1
enter 0x400b3e
enter 0x4008ba
enter 0x400864
exit 0x400864
enter 0x400864
exit 0x400864
enter 0x400864
exit 0x400864
enter 0x400864
exit 0x400864
enter 0x400864
exit 0x400864
exit 0x4008ba
enter 0x40096c
enter 0x400940
exit 0x400940
exit 0x40096c
enter 0x4009a4
exit 0x4009a4
enter 0x400a5a
enter 0x400a0f
exit 0x400a0f
enter 0x400a0f
exit 0x400a0f
exit 0x400a5a
enter 0x400af9
enter 0x400a0f
exit 0x400a0f
enter 0x400aa4
exit 0x400aa4
exit 0x400af9
exit 0x400b3e
exit 0x400ba1
EXIT
0000000000400ba1 <main>:
400ba1: push %rbp
400ba2: e5 mov %rsp,%rbp
400ba5: push %rbx
400ba6: ec sub $0x18,%rsp
400baa: 7d ec mov %edi,-0x14(%rbp)
400bad: e0 mov %rsi,-0x20(%rbp)
400bb1: 8b mov 0x8(%rbp),%rsi
400bb5: bf a1 0b mov $0x400ba1,%edi
400bba: e8 8e callq 400d4d <__cyg_profile_func_enter>
400bbf: e8 7a ff ff ff callq 400b3e <Crumble_make_apple_crumble>
400bc4: bb mov $0x0,%ebx
400bc9: 8b mov 0x8(%rbp),%rsi
400bcd: bf a1 0b mov $0x400ba1,%edi
400bd2: e8 callq 400d70 <__cyg_profile_func_exit>
400bd7: d8 mov %ebx,%eax
400bd9: c4 add $0x18,%rsp
400bdd: 5b pop %rbx
400bde: c9 leaveq
400bdf: c3 retq
[root@monitor src]# cat ptrace.c    //程序连接时加的额外代码

/*-------------------------------------------------------------------------*/
/**
@file ptrace.c
@author N. Devillard, V. Chudnovsky
@date March 2004
@version $Revision: 1.1.1.1 $
@brief Add tracing capability to any program compiled with gcc. This module is only compiled when using gcc and tracing has been
activated. It allows the compiled program to output messages whenever
a function is entered or exited. To activate this feature, your version of gcc must support
the -finstrument-functions flag. When using ptrace on a dynamic library, you must set the
PTRACE_REFERENCE_FUNCTION macro to be the name of a function in the
library. The address of this function when loaded will be the first
line output to the trace file and will permit the translation of the
other entry and exit pointers to their symbolic names. You may set
the macro PTRACE_INCLUDE with any #include directives needed for
that function to be accesible to this source file. The printed messages yield function addresses, not human-readable
names. To link both, you need to get a list of symbols from the
program. There are many (unportable) ways of doing that, see the
'etrace' project on freshmeat for more information about how to dig
the information.
*/
/*--------------------------------------------------------------------------*/ /*
$Id: ptrace.c,v 1.1.1.1 2004-03-16 20:00:07 ndevilla Exp $
$Author: ndevilla $
$Date: 2004-03-16 20:00:07 $
$Revision: 1.1.1.1 $
*/ #if (__GNUC__>2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ > 95)) /*---------------------------------------------------------------------------
Includes
---------------------------------------------------------------------------*/ #include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <sys/errno.h> /*---------------------------------------------------------------------------
User Macros
---------------------------------------------------------------------------*/
#define PTRACE_PIPENAME "TRACE" /* When using ptrace on a dynamic library, the following must be defined: #include "any files needed for PTRACE_REFERENCE_FUNCTION"
#define PTRACE_REFERENCE_FUNCTION functionName `*/ /*---------------------------------------------------------------------------
Defines
---------------------------------------------------------------------------*/ #define REFERENCE_OFFSET "REFERENCE:"
#define FUNCTION_ENTRY "enter"
#define FUNCTION_EXIT "exit"
#define END_TRACE "EXIT"
#define __NON_INSTRUMENT_FUNCTION__ __attribute__((__no_instrument_function__))
#define PTRACE_OFF __NON_INSTRUMENT_FUNCTION__
#define STR(_x) #_x
#define DEF(_x) _x
#define GET(_x,_y) _x(_y)
#define TRACE __GNU_PTRACE_FILE__
/*---------------------------------------------------------------------------
Function codes
---------------------------------------------------------------------------*/ /** Initial trace open */
static FILE *__GNU_PTRACE_FILE__; /** Final trace close */
static void
__NON_INSTRUMENT_FUNCTION__
gnu_ptrace_close(void)
{
fprintf(TRACE, END_TRACE " %ld\n", (long)getpid()); if (TRACE != NULL)
fclose(TRACE);
return ;
} /** Trace initialization */
static int
__NON_INSTRUMENT_FUNCTION__
gnu_ptrace_init(void)
{
struct stat sta;
__GNU_PTRACE_FILE__ = NULL; /* See if a trace file exists */
if (stat(PTRACE_PIPENAME, &sta) != )
{
/* No trace file: do not trace at all */
return ;
}
else
{
/* trace file: open up trace file */
if ((TRACE = fopen(PTRACE_PIPENAME, "a")) == NULL)
{
char *msg = strerror(errno);
perror(msg);
printf("[gnu_ptrace error]\n");
return ;
} #ifdef PTRACE_REFERENCE_FUNCTION
fprintf(TRACE,"%s %s %p\n",
REFERENCE_OFFSET,
GET(STR,PTRACE_REFERENCE_FUNCTION),
(void *)GET(DEF,PTRACE_REFERENCE_FUNCTION));
#endif /* Tracing requested: a trace file was found */
atexit(gnu_ptrace_close);
return ;
}
} /** Function called by every function event */
void
__NON_INSTRUMENT_FUNCTION__
gnu_ptrace(char * what, void * p)
{
static int first=;
static int active=; if (active == )
return; if (first)
{
active = gnu_ptrace_init();
first = ; if (active == )
return;
} fprintf(TRACE, "%s %p\n", what, p);
fflush(TRACE);
return;
} /** According to gcc documentation: called upon function entry */
void
__NON_INSTRUMENT_FUNCTION__
__cyg_profile_func_enter(void *this_fn, void *call_site)
{
gnu_ptrace(FUNCTION_ENTRY, this_fn);
(void)call_site;
} /** According to gcc documentation: called upon function exit */
void
__NON_INSTRUMENT_FUNCTION__
__cyg_profile_func_exit(void *this_fn, void *call_site)
{
gnu_ptrace(FUNCTION_EXIT, this_fn);
(void)call_site;
} #endif
/* vim: set ts=4 et sw=4 tw=75 */
执行原理:

test.c
#include <stdio.h>
fun()
{
printf("this is test\n");
}
main()
{ fun(); } gcc test.c -g -otest
00000000004004d4 <main>:
  4004d4:       55                      push   %rbp
  4004d5:       48 89 e5                mov    %rsp,%rbp
  4004d8:       b8 00 00 00 00          mov    $0x0,%eax
  4004dd:       e8 e2 ff ff ff          callq  4004c4 <fun>
  4004e2:       c9                      leaveq
  4004e3:       c3                      retq   
  4004e4:       90                      nop
  4004e5:       90                      nop
  4004e6:       90                      nop
  4004e7:       90                      nop
  4004e8:       90                      nop
  4004e9:       90                      nop
  4004ea:       90                      nop
  4004eb:       90                      nop
  4004ec:       90                      nop
  4004ed:       90                      nop
  4004ee:       90                      nop
  4004ef:       90                      nop gcc test.c -g  -finstrument-functions -otest 00000000004005a0 <main>:
  4005a0:       55                      push   %rbp
  4005a1:       48 89 e5                mov    %rsp,%rbp
  4005a4:       48 8b 75 08             mov    0x8(%rbp),%rsi
  4005a8:       bf a0 05 40 00          mov    $0x4005a0,%edi
  4005ad:       e8 be fe ff ff          callq  400470 <__cyg_profile_func_enter@plt>
  4005b2:       b8 00 00 00 00          mov    $0x0,%eax
  4005b7:       e8 b8 ff ff ff          callq  400574 <fun>
  4005bc:       48 8b 75 08             mov    0x8(%rbp),%rsi
  4005c0:       bf a0 05 40 00          mov    $0x4005a0,%edi
  4005c5:       e8 b6 fe ff ff          callq  400480 <__cyg_profile_func_exit@plt>
  4005ca:       c9                      leaveq
  4005cb:       c3                      retq   
  4005cc:       90                      nop
  4005cd:       90                      nop
  4005ce:       90                      nop
  4005cf:       90                      nop gcc -g -finstrument-functions --std=gnu99 -otest test.c ../src/ptrace.c //ptrace.c 写函数进入或退去点到TRACE文件中(如果存在的话)//函数的首地址 0000000000400840 <main>:
  400840:       55                      push   %rbp
  400841:       48 89 e5                mov    %rsp,%rbp
  400844:       53                      push   %rbx
  400845:       48 83 ec 08             sub    $0x8,%rsp
  400849:       48 8b 75 08             mov    0x8(%rbp),%rsi
  40084d:       bf 40 08 40 00          mov    $0x400840,%edi
  400852:       e8 96 01 00 00          callq  4009ed <__cyg_profile_func_enter>
  400857:       b8 00 00 00 00          mov    $0x0,%eax
  40085c:       e8 b3 ff ff ff          callq  400814 <fun>
  400861:       bb 00 00 00 00          mov    $0x0,%ebx
  400866:       48 8b 75 08             mov    0x8(%rbp),%rsi
  40086a:       bf 40 08 40 00          mov    $0x400840,%edi
  40086f:       e8 9c 01 00 00          callq  400a10 <__cyg_profile_func_exit>
  400874:       89 d8                   mov    %ebx,%eax
  400876:       48 83 c4 08             add    $0x8,%rsp
  40087a:       5b                      pop    %rbx
  40087b:       c9                      leaveq
  40087c:       c3                      retq   
  40087d:       90                      nop
  40087e:       90                      nop
  40087f:       90                      nop 00000000004009ed <__cyg_profile_func_enter>:
  4009ed:       55                      push   %rbp
  4009ee:       48 89 e5                mov    %rsp,%rbp
  4009f1:       48 83 ec 10             sub    $0x10,%rsp
  4009f5:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
  4009f9:       48 89 75 f0             mov    %rsi,-0x10(%rbp)
  4009fd:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  400a01:       48 89 c6                mov    %rax,%rsi
  400a04:       bf a1 0b 40 00          mov    $0x400ba1,%edi
  400a09:       e8 5a ff ff ff          callq  400968 <gnu_ptrace>
  400a0e:       c9                      leaveq
  400a0f:       c3                      retq   0000000000400968 <gnu_ptrace>:
  400968:       55                      push   %rbp
  400969:       48 89 e5                mov    %rsp,%rbp
  40096c:       53                      push   %rbx
  40096d:       48 83 ec 18             sub    $0x18,%rsp
  400971:       48 89 7d e8             mov    %rdi,-0x18(%rbp)
  400975:       48 89 75 e0             mov    %rsi,-0x20(%rbp)
  400979:       8b 05 4d 06 20 00       mov    0x20064d(%rip),%eax        # 600fcc <active.3459>
  40097f:       85 c0                   test   %eax,%eax
  400981:       74 5f                   je     4009e2 <gnu_ptrace+0x7a>
  400983:       8b 05 47 06 20 00       mov    0x200647(%rip),%eax        # 600fd0 <first.3458>
  400989:       85 c0                   test   %eax,%eax
  40098b:       74 1f                   je     4009ac <gnu_ptrace+0x44>
  40098d:       e8 36 ff ff ff          callq  4008c8 <gnu_ptrace_init>
  400992:       89 05 34 06 20 00       mov    %eax,0x200634(%rip)        # 600fcc <active.3459>
  400998:       c7 05 2e 06 20 00 00    movl   $0x0,0x20062e(%rip)        # 600fd0 <first.3458>
  40099f:       00 00 00
  4009a2:       8b 05 24 06 20 00       mov    0x200624(%rip),%eax        # 600fcc <active.3459>
  4009a8:       85 c0                   test   %eax,%eax
  4009aa:       74 39                   je     4009e5 <gnu_ptrace+0x7d>
  4009ac:       bb 9a 0b 40 00          mov    $0x400b9a,%ebx
  4009b1:       48 8b 05 30 06 20 00    mov    0x200630(%rip),%rax        # 600fe8 <__GNU_PTRACE_FILE__>
  4009b8:       48 8b 4d e0             mov    -0x20(%rbp),%rcx
  4009bc:       48 8b 55 e8             mov    -0x18(%rbp),%rdx
  4009c0:       48 89 de                mov    %rbx,%rsi
  4009c3:       48 89 c7                mov    %rax,%rdi
  4009c6:       b8 00 00 00 00          mov    $0x0,%eax
  4009cb:       e8 40 fd ff ff          callq  400710 <fprintf@plt>
  4009d0:       48 8b 05 11 06 20 00    mov    0x200611(%rip),%rax        # 600fe8 <__GNU_PTRACE_FILE__>
  4009d7:       48 89 c7                mov    %rax,%rdi
  4009da:       e8 41 fd ff ff          callq  400720 <fflush@plt>
  4009df:       90                      nop
  4009e0:       eb 04                   jmp    4009e6 <gnu_ptrace+0x7e>
  4009e2:       90                      nop
  4009e3:       eb 01                   jmp    4009e6 <gnu_ptrace+0x7e>
  4009e5:       90                      nop
  4009e6:       48 83 c4 18             add    $0x18,%rsp
  4009ea:       5b                      pop    %rbx
  4009eb:       c9                      leaveq
  4009ec:       c3                      retq   [root@monitor example]# touch TRACE
[root@monitor example]# ./test
this is test
[root@monitor example]# ll
total 52
-rwxr-xr-x 1 root root 16687 Jun  9 09:39 crumble
-rw-r--r-- 1 root root  1651 Jun  9 09:28 crumble.c
-rw-r--r-- 1 root root   244 Jun  9 09:05 Makefile
-rwxr-xr-x 1 root root 13627 Jun  9 09:47 test
-rw-r--r-- 1 root root    75 Jun  9 09:34 test.c
-rw-r--r-- 1 root root    68 Jun  9 09:52 TRACE
[root@monitor example]# cat TRACE
enter 0x400840 // main进入
enter 0x400814 // fun进入
exit 0x400814 // fun退去
exit 0x400840 // main退去
EXIT 6993