how to trace function call in C?

后端 未结 8 1681
遇见更好的自我
遇见更好的自我 2020-12-02 13:45

Without modifying the source code, how can i trace which functions are called and with what parameters, when some function(say func100 in the following example) is in

相关标签:
8条回答
  • 2020-12-02 14:15

    If you use dynamic modules you can get this info with the command ltrace. You can even specify the watched library with the -l flag

    0 讨论(0)
  • 2020-12-02 14:17

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

    You'll need to implement these functions, to do what you want. Make sure to compile them either without the flag, or with __attribute__((no_instrument_function)), so they won't try to call themselves.

    The functions' second parameter would be a pointer to the call site (i.e. the return address within the calling function). You can just print it with %p, but it will be somewhat hard to use. You can use nm to figure out the real function which contains this address.

    You can't get the function parameters this way.

    0 讨论(0)
  • 2020-12-02 14:22

    Use a debugger to set breakpoints with associated actions. For example, in gdb you could set a breakpoint at the beginning and end of each of the functions you want to trace. You can give each of those breakpoints a command to execute, such as:

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

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

    Take a look at the relevant documentation for gdb.

    0 讨论(0)
  • 2020-12-02 14:24

    If you were on linux, callgrind might help. It basically collects statistics of what you're looking for, so, it might provide a way to access its raw data.

    0 讨论(0)
  • 2020-12-02 14:26

    I also encountered this problem of having good function call traces. Therefore, I wrote a Python GDB script (https://gist.github.com/stettberger/e6f2fe61206471e22e9e6f1926668093) that sets a breakpoint on every interesting function (defined by the environment variable TRACE_FUNCTION). GDB then invokes the python function, which decodes the frame and all its arguments. If it encounters a pointer it tries to dereference it and so print a function call trace to TRACE_FILE (default: /tmp/log) with arguments. For the following program

    #include <stdio.h>
    
    struct foo {
        int a;
        struct foo * next;
    };
    
    int fib(int a, struct foo *b) {
        if (a <= 1) return 1;
        printf("%d\n", a);
        return fib(a-1, 0)+fib(a-2, 0);
    }
    
    int main() {
        struct foo b = {23, 0};
        return fib(5, &b);
    }
    

    I get a detailed trace, where every line is a python tuple that can be read with eval():

    ('call', None, 1, 'main', 'main', {})
    ('call', 1, 2, 'fib', 'fib', {'a': {'type': 'int', 'value': 5}, 'b': {'type': 'struct foo *', 'value': 140737488344320, 'deref': {'type': 'struct foo', 'value': {'a': {'type': 'int', 'value': 23}, 'next': {'type': 'struct foo *', 'value': 0, 'deref': None}}}}})
    ('call', 2, 3, 'fib', 'fib', {'a': {'type': 'int', 'value': 4}, 'b': {'type': 'struct foo *', 'value': 0, 'deref': None}})
    ....
    ('return', 'fib', 2, {'type': 'int', 'value': 8})
    ('exit', 8)
    

    The gist contains more information on the log file format.

    0 讨论(0)
  • 2020-12-02 14:28

    Sometimes I have to trace lots of function calls, even for external libraries I don't have any control, or I don't want to modify.

    Sometime ago, I realized that you can combine gdb's regular expression breakpoints (regular ones are ok, too) and then just execute a set of commands to be run each time those breakpoints are triggered. See: http://www.ofb.net/gnu/gdb/gdb_35.html

    For example, if you want to trace all the functions which start with "MPI_" prefix, you can do:

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

    Silent command is used to hide gdb messages when a breakpoint is found. I usually print a couple of empty lines, so that it is easier to read.

    Then, you just run the program: (gdb) run

    Once your program starts running, gdb will print the N topmost backtrace levels.

    #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 want more detailed information, printing local variables of a given breakpoint is also possible, just insert more commands between command and end.

    Bonus tip: add all of these to your .gdbinit file and pipe the execution into a file.

    0 讨论(0)
提交回复
热议问题