How to use etrace with a dynamic library for chronological traceing of function calls in C++?

前端 未结 1 852
感情败类
感情败类 2021-01-08 00:35

Background:

I have one big simulation tool, and I need to understand its logical behavior. In order to do that, the most of help I would get if I ha

相关标签:
1条回答
  • 2021-01-08 00:50

    Fixing the Perl Script

    Hexadecimal number > 0xffffffff non-portable"

    This is a warning from hex because it's detecting a possibly non-portable value (something > 32bits).

    At the very top of the script, add this:

    use bigint qw/hex oct/;
    

    When this tool was written, I suspect the people were on 32-bit machines. You can compile the program using 32-bit with the flag -m32, but if you change the perl script as mentioned above you won't need to.

    Note, if you're on a Mac, you can't use mknod the way it's used in the script to create a pipe; you need to use mkfifo with no arguments instead.

    On Linux, adding the bigint fix above works. You then need to run both commands from the same directory, I did this using example2:

    ../src/etrace.pl crumble
    # Switch to a different terminal
    ./crumble
    

    and I get this on the Mac and Linux

    \-- main
    |   \-- Crumble_make_apple_crumble
    |   |   \-- Crumble_buy_stuff
    |   |   |   \-- Crumble_buy
    |   |   |   \-- Crumble_buy
    |   |   |   \-- Crumble_buy
    |   |   |   \-- Crumble_buy
    |   |   |   \-- Crumble_buy
    |   |   \-- Crumble_prepare_apples
    |   |   |   \-- Crumble_skin_and_dice
    |   |   \-- Crumble_mix
    |   |   \-- Crumble_finalize
    |   |   |   \-- Crumble_put
    |   |   |   \-- Crumble_put
    |   |   \-- Crumble_cook
    |   |   |   \-- Crumble_put
    |   |   |   \-- Crumble_bake
    

    About the Dynamic Library...

    When you load a dynamic library, the address in the object file is not the address that will be used when running. What etrace does is take a function name from a header you specify. For example, in the case of example2, this would be the following:

    #include "crumble.h"
    #define PTRACE_REFERENCE_FUNCTION Crumble_buy
    

    You would then edit the makefile to make sure that the header file can be found:

    CFLAGS = -g -finstrument-functions -I.
    

    Note the addition of the include -I.. The address of the symbol from the header (in our case, Crumble_buy) is used to calculate the offset between the object file and the actual address; this allows the program to calculate the correct address to find the symbol.

    If you look at the output of nm, you get something like the following:

    0000000100000960 T _Crumble_bake
    00000001000005b0 T _Crumble_buy
    0000000100000640 T _Crumble_buy_stuff
    00000001000009f0 T _Crumble_cook
    

    The addresses on the left are relative, that is, at runtime, these addresses actually change. The etrace.pl program is storing these in a hash like this:

    $VAR1 = {
              '4294969696' => '_Crumble_bake',
              '4294969424' => '_Crumble_put',
              '4294970096' => '_main',
              '4294969264' => '_Crumble_mix',
              '4294970704' => '_gnu_ptrace_close',
              '4294967296' => '__mh_execute_header',
              '4294968752' => '_Crumble_buy',
              '4294968896' => '_Crumble_buy_stuff',
              '4294969952' => '_Crumble_make_apple_crumble',
              '4294969184' => '_Crumble_prepare_apples',
              '4294971512' => '___GNU_PTRACE_FILE__',
              '4294971504' => '_gnu_ptrace.first',
              '4294970208' => '_gnu_ptrace',
              '4294970656' => '___cyg_profile_func_exit',
              '4294970608' => '___cyg_profile_func_enter',
              '4294969552' => '_Crumble_finalize',
              '4294971508' => '_gnu_ptrace.active',
              '4294969840' => '_Crumble_cook',
              '4294969088' => '_Crumble_skin_and_dice',
              '4294970352' => '_gnu_ptrace_init'
            };
    

    Note the leading underscore because this is on a Mac using clang. At runtime, these addresses are not correct, but their relative offsets are. If you can work out what the offset is, you can adjust the addresses you get at runtime to find the actual symbol. The code that does this follows:

     if ($offsetLine =~ m/^$REFERENCE_OFFSET\s+($SYMBOL_NAME)\s+($HEX_NUMBER)$/) {
        # This is a dynamic library; need to calculate the load offset
        my $offsetSymbol  = "_$1";
        my $offsetAddress = hex $2; 
    
        my %offsetTable = reverse %SYMBOLTABLE;
    
        print Dumper(\%offsetTable);
        $baseAddress = $offsetTable{$offsetSymbol} - $offsetAddress;
        #print("offsetSymbol == $offsetSymbol\n");
        #print("offsetAddress == $offsetAddress\n");
        #print("baseoffsetAddress == $offsetAddress\n");
        $offsetLine = <CALL_DATA>;
      } else {
        # This is static
        $baseAddress = 0;
      }
    

    This is what the line #define PTRACE_REFERENCE_FUNCTION Crumble_buy is for. The C code in ptrace is using that MACRO, and if defined, outputting the address of that function as the first thing. It then calculates the offset, and for all subsequent addresses, adjusts them by this amount, looking up the correct symbol in the hash.

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