backtrace関数の使用


backtrace()はglibc(>=2.1)が提供する関数であり、関数の呼び出し関係を追跡するために使用されます.
以下のbacktrace()関数の説明および例は、man pageから来ています.
関数定義#include        int backtrace(void **buffer, int size);        char **backtrace_symbols(void *const *buffer, int size);        void backtrace_symbols_fd(void *const *buffer, int size, int fd); 関数の説明
backtrace()関数は、プログラム内の現在の関数の遡及情報、すなわち一連の関数呼び出し関係を取得するために使用され、取得された情報はパラメータbufferに格納される.bufferは配列ポインタであり、配列の各要素は各レベルで呼び出された関数の戻りアドレスを保存します.パラメータsizeはbufferに格納可能な戻りアドレスの数を指定します.関数の実際の遡及レベル数がsizeより大きい場合、bufferには最近の関数呼び出し関係しか格納されません.したがって、完全な遡及情報を得るには、sizeパラメータが十分大きいことを確認します.backtrace()関数の戻り値はbufferのエントリ数であり、この値は必ずしもsizeに等しいとは限らない.完全な遡及情報を得るためにsizeを十分に大きく設定すると、その関数の戻り値はbufferで実際に得られた戻りアドレス数であるからである.backtrace()関数でbufferを得た後、backtrace_Symbols()は、戻りアドレスを特定の関数名に対応させることができ、パラメータsizeはbufferのエントリ数である.backtrace_Symbols()関数は、各戻り値を「関数名+関数内オフセット量+関数戻り値」に翻訳することで、関数の呼び出し関係をより直感的に得ることができます.翻訳された関数遡及情報はbacktrace_に格納されます.Symbols()の戻り値で、失敗した場合はNULLを返します.戻り値自体がbacktrace_であることに注意してください.Symbols()関数の内部でmallocを行うため,後続で明示的にfreeを落とす必要がある.   backtrace_symbols_fd()のbufferとsizeパラメータとbacktrace_Symbols()関数は同じですが、翻訳された関数遡及情報は戻り値ではなく、ファイル記述子fdに対応するファイルに1行1行配置されます.
コンパイル時に-rdynamicオプションを追加して、リンクがすべてのシンボルを動的シンボルテーブルに追加する必要があります.これにより、関数アドレスを関数名に翻訳できます.また、このオプションはstatic関数を処理しないので、static関数の記号は得られません.
例:
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void
myfunc3(void)
{
   int j, nptrs;
#define SIZE 100
   void *buffer[100];
   char **strings;

   nptrs = backtrace(buffer, SIZE);
   printf("backtrace() returned %d addresses
", nptrs); /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) would produce similar output to the following: */ strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } for (j = 0; j < nptrs; j++) printf("%s
", strings[j]); free(strings); } static void /* "static" means don't export the symbol... */ myfunc2(void) { myfunc3(); } void myfunc(int ncalls) { if (ncalls > 1) myfunc(ncalls - 1); else myfunc2(); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "%s num-calls
", argv[0]); exit(EXIT_FAILURE); } myfunc(atoi(argv[1])); exit(EXIT_SUCCESS); }

コンパイル:
cc -rdynamic prog.c-o progコードの実行結果は:
$ ./prog 3
           backtrace() returned 8 addresses
           ./prog(myfunc3+0x5c) [0x80487f0]
           ./prog [0x8048871]
           ./prog(myfunc+0x21) [0x8048894]
           ./prog(myfunc+0x1a) [0x804888d]
           ./prog(myfunc+0x1a) [0x804888d]
           ./prog(main+0x65) [0x80488fb]
           /lib/libc.so.6(__libc_start_main+0xdc) [0xb7e38f9c]
           ./prog [0x8048711]