How to track function call in C?

Without changing the source code, how can I track which functions are called and with which parameters when some function is called (for example, func100 in the following example). I would like the result to be as follows:

enter func100(p1001=xxx,p1002=xxx) enter func110(p1101=xxx,p1102=xxx) exit func110(p1101=xxx,p1102=xxx) enter func120(p1201=xxx,p1202=xxx,p1203=xxx) enter func121(p1211=xxx) exit func121(p1211=xxx) exit func120(p1201=xxx,p1202=xxx,p1203=xxx) exit func100(p1001=xxx,p1002=xxx) 

is it doable? or what is the solution with minimal modification of the source code?

+9
c trace call-hierarchy
Apr 29 '12 at 16:57
source share
6 answers

If you use gcc , you can use the compilation flag -finstrument-functions . It adds code that calls two functions, __cyg_profile_func_enter and __cyg_profile_func_exit , whenever a function enters / exits.

You will need to implement these functions in order to do what you want. Remember to compile them either without a flag, or using attribute((no_instrument_function)) so that they don't try to call themselves.

The second parameter of the function will be a pointer to the call site (i.e., the return address in the calling function). You can simply print it with %p , but it will be somewhat difficult to use. You can use nm to determine the real function that contains this address.

You cannot get function parameters this way.

+13
Apr 29 2018-12-12T00:
source share
β€” -

In the GNU C library, you can use the backtrace module. Here is an example for this:

 #include <stdio.h> #include <execinfo.h> #include <stdlib.h> void handler(char *caller) { void *array[10]; size_t size; printf("Stack Trace Start for %s\n",caller); size = backtrace(array, 10); backtrace_symbols_fd(array, size, 2); printf("Stack Trace End\n"); } void car() { handler("car()"); printf("Continue Execution"); } void baz() {car(); } void bar() { baz(); } void foo() { bar(); } int main(int argc, char **argv) { foo(); } 

compile with -g -rdynamic compiler to load characters

 gcc -g -rdynamic Test1.c -o Test 

You will see a conclusion similar to

 Stack Trace Start for car() ./Test(handler+0x2d)[0x80486f1] ./Test(car+0x12)[0x804872e] ./Test(baz+0xb)[0x8048747] ./Test(bar+0xb)[0x8048754] ./Test(foo+0xb)[0x8048761] ./Test(main+0xb)[0x804876e] /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x126e37] ./Test[0x8048631] Stack Trace End Continue Execution in car 

You can write this handler function and call from anywhere in your program at any time. Remember to increase the size of the array as needed.

+12
Apr 29 2018-12-12T00:
source share

If you were on Linux, callgrind can help. It basically collects statistics of what you are looking for, so it can provide a way to access its raw data.

+4
Apr 29 '12 at 17:01
source share

Use the debugger to set breakpoints with appropriate actions. For example, in gdb, you can set a breakpoint at the beginning and end of each of the functions that you want to track. You can give each of these control points a command to execute, for example:

 printf("Enter func100(p1001=%d, p1002=%d)", p1001, p1002) 

Then, when you run the program (in the debugger), it will print the text from each of your commands along with the corresponding parameters.

Take a look at the relevant documentation for gdb .

+3
Apr 29 '12 at 17:17
source share

You can look in log4cxx, a project created by the apache base. I know that log4j, a variant of java, allowed you to set the sensitivity, and you could track everything that was done in the program. The c ++ option may be the same, but there are several alternatives - there is a C ++ aspect-oriented compiler, and you can define the aspect in all functions and make it catch and print variables. Another option is to use a debugger.

To summarize: debugger, log4cxx or AOP

0
Apr 29 '12 at 17:02
source share

Sometimes I have to spend a lot of function calls, even for external libraries I don’t have any control, or I don’t want to change.

Once I realized that you can combine breakpoints of the gdb regular expression (the usual ones are also approved), and then just execute a set of commands that will be run every time these breakpoints are triggered. See: http://www.ofb.net/gnu/gdb/gdb_35.html

For example, if you want to track all the functions starting with the prefix "MPI_", you can do:

 (gdb) rbreak MPI_ [...] (gdb) command 1-XX (gdb) silent (gdb) bt 1 (gdb) echo \n\n (gdb) continue (gdb) end 

The Silent command is used to hide gdb messages when a breakpoint is detected. I usually print a couple of blank lines to make them easier to read.

Then you run the program: (gdb)

Once your program starts, gdb will display the N top levels of the backtrace.

 #0 0x000000000040dc60 in MPI_Initialized@plt () #0 PMPI_Initialized (flag=0x7fffffffba78) at ../../src/mpi/init/initialized.c:46 #0 0x000000000040d9b0 in MPI_Init_thread@plt () #0 PMPI_Init_thread (argc=0x7fffffffbe78, argv=0x7fffffffbde0, required=3, provided=0x7fffffffba74) at ../../src/mpi/init/initthread.c:946 #0 0x000000000040e390 in MPI_Comm_rank@plt () #0 PMPI_Comm_rank (comm=1140850688, rank=0x7fffffffba7c) at ../../src/mpi/comm/comm_rank.c:53 #0 0x000000000040e050 in MPI_Type_create_struct@plt () #0 PMPI_Type_create_struct (count=3, array_of_blocklengths=0x7fffffffba90, array_of_displacements=0x7fffffffbab0, array_of_types=0x7fffffffba80, newtype=0x69de20) at ../../src/mpi/datatype/type_create_struct.c:116 #0 0x000000000040e2a0 in MPI_Type_commit@plt () #0 PMPI_Type_commit (datatype=0x69de20) at ../../src/mpi/datatype/type_commit.c:75 

If you need more detailed information, you can also print the local variables of a given breakpoint, just insert more commands between command and end .

Bonus tip: add all this to your .gdbinit file and complete the execution in the file.

0
Aug 24 '16 at 13:11
source share



All Articles