web-dev-qa-db-ja.com

実行時に呼び出される関数を印刷するツール?

私は、Unix/Linuxプラットフォームで次のことを実現できるような種類のツールを探しています。

  1. 私はソースファイルを持っていて、アプリケーションを自分でコンパイルしました(ソースコードはCですが、ここではそれほど重要ではないと思います)
  2. すべての関数呼び出しが標準出力/ファイルに出力/記録されている間にこのアプリケーションを実行したい

例えば:

#include <stdio.h>
int square(int x) { return x*x; }
int main(void) {
    square(2);
}

そして、私がこのプログラムを実行すると、それは印刷されます

  • メイン
  • 平方

gdbがある程度これを実行できること、またはvalgrindが実行できることは理解していますが、すべてが私が望むことを正確に実行しているわけではありません。そのようなツールが存在するかどうか疑問に思っていますか?ありがとう。

2
dorafmon

gcovの使用:

$ gcc -O0 --coverage square.c
$ ./a.out
$ gcov -i square.c
$ awk -F '[,:]' '$1 == "function" && $3 > 0 {print $3, $4}' square.c.gcov
1 square
1 main

(ここで、numberは関数が呼び出された回数です(awk部分の$3 > 0で呼び出されたことがないものはスキップします))。

これは通常、コードカバレッジ(テストされているコードの量)に使用されます。 gprofコードプロファイリングツール(通常、コードのさまざまな領域で費やされた時間を把握するために使用されます)を使用することもできます。

$ gcc -O0 -pg square.c
$ ./a.out
$ gprof -b -P
            Call graph


granularity: each sample hit covers 2 byte(s) no time propagated

index % time    self  children    called     name
        0.00    0.00       1/1           main [7]
[1]      0.0    0.00    0.00       1         square [1]
-----------------------------------------------

Index by function name

   [1] square
5

実行可能ファイルが呼び出されたときに関数名を出力するには、GNUシステムで、gcc-finstrument-functionsオプションとdladdr()を使用できます。アドレスを関数名に変換します。

次のようなinstrument.cを作成します。

#define _GNU_SOURCE
#include <dlfcn.h>

#include <stdlib.h>
#include <stdio.h>

#define TRACE_FD 3

void __cyg_profile_func_enter (void *, void *)
   __attribute__((no_instrument_function));

void __cyg_profile_func_enter (void *func,  void *caller)
{
  static FILE* trace = NULL;
  Dl_info info;

  if (trace == NULL) {
    trace = fdopen(TRACE_FD, "w");
    if (trace == NULL) abort();
    setbuf(trace, NULL);
  }
  if (dladdr(func, &info))
    fprintf (trace, "%p [%s] %s\n",
             func,
             info.dli_fname ? info.dli_fname : "?",
             info.dli_sname ? info.dli_sname : "?");
}

次に、実行可能ファイルを次のようにコンパイルします。

$ gcc -O0 -rdynamic -finstrument-functions square.c instrument.c -ldl
$ ./a.out 3>&1
0x400a8f [./a.out] main
0x400a4f [./a.out] square

(ここでは、fd 3を使用して関数名をダンプし、stdoutおよびstderrストリームから分離します)。

関数名のみが必要な場合は、dli_snameのみを出力するようにコードを調整できます。

5