65

実行可能ファイルでローカルに定義された関数をトレースできるltracestraceのようなツールを探しています。ltrace は動的ライブラリ呼び出しのみをトレースし、strace はシステム コールのみをトレースします。たとえば、次の C プログラムがあるとします。

#include <stdio.h>

int triple ( int x )
{
  return 3 * x;
}

int main (void)
{
  printf("%d\n", triple(10));
  return 0;
}

でプログラムを実行すると、これは標準ライブラリ関数 (私のシステムでは動的ライブラリ) であるため、へltraceの呼び出しが表示され、スタートアップ コードからのすべてのシステム コール、printf の実装に使用されるシステム コール、およびシャットダウン コードが表示されます。 、しかし、関数が呼び出されたことを示す何かが必要です。ローカル関数が最適化コンパイラによってインライン化されておらず、バイナリが削除されていない (シンボルが削除されていない) と仮定すると、これを実行できるツールはありますか?printfstracetriple

編集

いくつかの説明:

  • ツールが非ローカル関数のトレース情報も提供する場合は問題ありません。
  • 特定のツールをサポートするプログラムを再コンパイルする必要はありません。実行可能ファイルのシンボル情報で十分です。
  • このツールを使用して、ltrace/strace でできるように既存のプロセスにアタッチできれば、本当にうれしいです。
4

13 に答える 13

54

特定の機能についてのみ通知を受けたい場合は、次のようにできます。

デバッグ情報を使用してコンパイルします (既にシンボル情報があるため、おそらく十分なデバッグも含まれています)。

与えられた

#include <iostream>

int fac(int n) {
    if(n == 0)
        return 1;
    return n * fac(n-1);
}

int main()
{
    for(int i=0;i<4;i++)
        std::cout << fac(i) << std::endl;
}

gdb を使用してトレースします。

[js@HOST2 cpp]$ g++ -g3 test.cpp
[js@HOST2 cpp]$ gdb ./a.out
(gdb) b fac
Breakpoint 1 at 0x804866a: file test.cpp, line 4.
(gdb) commands 1
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>silent
>bt 1
>c
>end
(gdb) run
Starting program: /home/js/cpp/a.out
#0  fac (n=0) at test.cpp:4
1
#0  fac (n=1) at test.cpp:4
#0  fac (n=0) at test.cpp:4
1
#0  fac (n=2) at test.cpp:4
#0  fac (n=1) at test.cpp:4
#0  fac (n=0) at test.cpp:4
2
#0  fac (n=3) at test.cpp:4
#0  fac (n=2) at test.cpp:4
#0  fac (n=1) at test.cpp:4
#0  fac (n=0) at test.cpp:4
6

Program exited normally.
(gdb)

すべての関数のアドレスを収集するために私が行うことは次のとおりです。

tmp=$(mktemp)
readelf -s ./a.out | gawk '
{ 
  if($4 == "FUNC" && $2 != 0) { 
    print "# code for " $NF; 
    print "b *0x" $2; 
    print "commands"; 
    print "silent"; 
    print "bt 1"; 
    print "c"; 
    print "end"; 
    print ""; 
  } 
}' > $tmp; 
gdb --command=$tmp ./a.out; 
rm -f $tmp

現在の frame( bt 1) を印刷するだけでなく、グローバルの値を印刷したり、シェル コマンドを実行したり、関数にヒットした場合に何かをメール送信したりして、好きなことを行うことができることに注意してくださいfatal_bomb_exploded:) 残念ながら、gcc は「現在の言語が変更されました」と出力します。間のメッセージ。しかし、それは簡単に解決できます。大きな問題ではない。

于 2008-11-22T23:44:06.560 に答える
23

System Tapは最新の Linux ボックス (Fedora 10、RHEL 5 など) で使用できます。

まず、para-callgraph.stpスクリプトをダウンロードします。

次に実行します。

$ sudo stap para-callgraph.stp 'process("/bin/ls").function("*")' -c /bin/ls
0    ls(12631):->main argc=0x1 argv=0x7fff1ec3b038
276  ls(12631): ->human_options spec=0x0 opts=0x61a28c block_size=0x61a290
365  ls(12631): <-human_options return=0x0
496  ls(12631): ->clone_quoting_options o=0x0
657  ls(12631):  ->xmemdup p=0x61a600 s=0x28
815  ls(12631):   ->xmalloc n=0x28
908  ls(12631):   <-xmalloc return=0x1efe540
950  ls(12631):  <-xmemdup return=0x1efe540
990  ls(12631): <-clone_quoting_options return=0x1efe540
1030 ls(12631): ->get_quoting_style o=0x1efe540

関連項目: Observe、systemtap、および oprofile の更新

于 2008-11-27T21:54:57.853 に答える
11

Uprobes の使用(Linux 3.5 以降)

~/Desktop/datalog-2.2/datalogパラメータで呼び出すときにすべての関数をトレースしたいと仮定します-l ~/Desktop/datalog-2.2/add.lua ~/Desktop/datalog-2.2/test.dl

  1. cd /usr/src/linux-`uname -r`/tools/perf
  2. for i in `./perf probe -F -x ~/Desktop/datalog-2.2/datalog`; do sudo ./perf probe -x ~/Desktop/datalog-2.2/datalog $i; done
  3. sudo ./perf record -agR $(for j in $(sudo ./perf probe -l | cut -d' ' -f3); do echo "-e $j"; done) ~/Desktop/datalog-2.2/datalog -l ~/Desktop/datalog-2.2/add.lua ~/Desktop/datalog-2.2/test.dl
  4. sudo ./perf report -G

データログ バイナリの関数のリスト dl_pushlstring を選択したときのコール ツリー。ロードファイルを呼び出したメインが dl_load を呼び出したプログラムを呼び出し、リテラルを呼び出した後、他の関数を呼び出して dl_pushlstring を呼び出し、スキャン (親: プログラム、つまり上から 3 番目のスキャン) を呼び出した様子を示していますdl_pushstring など

于 2012-09-12T13:51:49.177 に答える
9

gccオプションを使用してトレースするコードを再コンパイルできる(ソースを変更する必要がない)と仮定すると、etrace-finstrument-functionsを使用して関数呼び出しグラフを取得できます。

出力は次のようになります。

\-- 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

Solarisでは、truss(straceと同等)には、トレースするライブラリをフィルタリングする機能があります。straceにそのような機能がないことに驚いた。

于 2008-11-23T14:42:37.450 に答える
4
$ sudo yum install frysk
$ ftrace -sym:'*' -- ./a.out

詳細: ftrace.1

于 2008-11-23T02:56:49.527 に答える
3

KcacheGrind

https://kcachegrind.github.io/html/Home.html

テストプログラム:

int f2(int i) { return i + 2; }
int f1(int i) { return f2(2) + i + 1; }
int f0(int i) { return f1(1) + f2(2); }
int pointed(int i) { return i; }
int not_called(int i) { return 0; }

int main(int argc, char **argv) {
    int (*f)(int);
    f0(1);
    f1(1);
    f = pointed;
    if (argc == 1)
        f(1);
    if (argc == 2)
        not_called(1);
    return 0;
}

使用法:

sudo apt-get install -y kcachegrind valgrind

# Compile the program as usual, no special flags.
gcc -ggdb3 -O0 -o main -std=c99 main.c

# Generate a callgrind.out.<PID> file.
valgrind --tool=callgrind ./main

# Open a GUI tool to visualize callgrind data.
kcachegrind callgrind.out.1234

これで、多くの興味深いパフォーマンス データを含む素晴らしい GUI プログラム内に取り残されました。

右下にある「コールグラフ」タブを選択します。これは、関数をクリックすると、他のウィンドウのパフォーマンス メトリックに相関する対話型の呼び出しグラフを示します。

グラフをエクスポートするには、グラフを右クリックして [グラフのエクスポート] を選択します。エクスポートされた PNG は次のようになります。

そこから、次のことがわかります。

  • ルート ノードは_startであり、これは実際の ELF エントリ ポイントであり、glibc 初期化ボイラープレートが含まれています。
  • f0、期待どおりf1f2相互に呼び出されます
  • pointed関数ポインターで呼び出したにもかかわらず、 も表示されます。コマンドライン引数を渡した場合、呼び出されなかった可能性があります。
  • not_called追加のコマンドライン引数を渡さなかったため、実行中に呼び出されなかったため、表示されていません。

素晴らしい点valgrindは、特別なコンパイル オプションを必要としないことです。

したがって、ソース コードがなくても、実行可能ファイルだけを使用できます。

valgrind軽量の「仮想マシン」を介してコードを実行することで、なんとかそれを行うことができます。

Ubuntu 18.04 でテスト済み。

于 2015-08-04T16:25:35.520 に答える
2

gdbを使用してトレース関数呼び出しを自動化するためのシェルスクリプトがあります。ただし、実行中のプロセスにアタッチすることはできません。

blog.superadditive.com/2007/12/01/call-graphs-using-the-gnu-project-debugger/

ページのコピー-http ://web.archive.org/web/20090317091725/http://blog.superadditive.com/2007/12/01/call-graphs-using-the-gnu-project-debugger/

ツールのコピー-callgraph.tar.gz

http://web.archive.org/web/20090317091725/http://superadditive.com/software/callgraph.tar.gz

プログラムからすべての関数をダンプし、各関数にブレークポイントを含むgdbコマンドファイルを生成します。各ブレークポイントで、「backtrace2」と「continue」が実行されます。

このスクリプトは大きなプロジェクト(数千の関数)ではかなり遅いので、関数リストにフィルターを追加します(egrep経由)。とても簡単で、私はこのスクリプトをほぼ毎日使用しています。

于 2009-12-30T15:37:13.157 に答える
2

その関数を外部ライブラリに外部化すると、それが呼び出されることも確認できるはずです ( ltrace を使用)。

これが機能する理由は、ltrace がアプリとライブラリの間に配置され、すべてのコードが 1 つのファイルで内部化されると、呼び出しをインターセプトできないためです。

例: ltrace xterm

Xライブラリからのものを吐き出し、Xはほとんどシステムではありません。

これ以外では、これを行う唯一の実際の方法は、prof フラグまたはデバッグ シンボルを介したコンパイル時のインターセプトです。

私はこのアプリに出くわしました。これは面白そうです:

http://www.gnu.org/software/cflow/

しかし、それがあなたの望むものだとは思いません。

于 2008-11-22T23:44:36.747 に答える
2

関数がインライン化されていない場合は、objdump -d <program>.

main例として、GCC 4.3.2 のルーチンの最初にある戦利品を見てみましょう。

$ objdump `which gcc` -d | grep '\(call\|main\)' 

08053270 <main>:
8053270:    8d 4c 24 04             lea    0x4(%esp),%ecx
--
8053299:    89 1c 24                mov    %ebx,(%esp)
805329c:    e8 8f 60 ff ff          call   8049330 <strlen@plt>
80532a1:    8d 04 03                lea    (%ebx,%eax,1),%eax
--
80532cf:    89 04 24                mov    %eax,(%esp)
80532d2:    e8 b9 c9 00 00          call   805fc90 <xmalloc_set_program_name>
80532d7:    8b 5d 9c                mov    0xffffff9c(%ebp),%ebx
--
80532e4:    89 04 24                mov    %eax,(%esp)
80532e7:    e8 b4 a7 00 00          call   805daa0 <expandargv>
80532ec:    8b 55 9c                mov    0xffffff9c(%ebp),%edx
--
8053302:    89 0c 24                mov    %ecx,(%esp)
8053305:    e8 d6 2a 00 00          call   8055de0 <prune_options>
805330a:    e8 71 ac 00 00          call   805df80 <unlock_std_streams>
805330f:    e8 4c 2f 00 00          call   8056260 <gcc_init_libintl>
8053314:    c7 44 24 04 01 00 00    movl   $0x1,0x4(%esp)
--
805331c:    c7 04 24 02 00 00 00    movl   $0x2,(%esp)
8053323:    e8 78 5e ff ff          call   80491a0 <signal@plt>
8053328:    83 e8 01                sub    $0x1,%eax

すべてのアセンブラーを調べるには少し手間がかかりますが、特定の関数から可能なすべての呼び出しを確認できます。gprof言及されている他のユーティリティのいくつかほど使いやすいわけではありませんが、いくつかの明確な利点があります。

  • 通常、アプリケーションを使用するためにアプリケーションを再コンパイルする必要はありません
  • 可能なすべての関数呼び出しがgprof表示されますが、実行された関数呼び出しのみが表示されます。
于 2008-11-28T03:15:34.057 に答える
1

Linux C/C++ アプリケーションのトレース フレームワークである traces を参照してください: https://github.com/baruch/traces#readme

コードをインストゥルメントで再コンパイルする必要がありますが、すべての関数、それらのパラメーター、および戻り値のリストが提供されます。大規模なデータ サンプルの簡単なナビゲーションを可能にする対話型があります。

于 2012-04-13T18:46:56.833 に答える
1

Gprofはあなたが望むものかもしれません

于 2008-11-22T22:27:05.233 に答える
0

注: これは Linux カーネル ベースの ftrace ではなく、ローカル関数のトレースと制御フローを実現するために最近設計したツールです。Linux ELF x86_64/x86_32 は公式にサポートされています。

https://github.com/leviathansecurity/ftrace

于 2014-03-13T20:06:44.687 に答える
0

うまくいけば、Valgrindのcallgrind または cachegrind ツールが、求める情報を提供してくれるでしょう。

于 2008-11-22T22:20:32.517 に答える