3

PinDynInstなど、再コンパイルせずにコードを計測するために動的なコード操作を行うツールをいくつか見てきました。これらは、プログラムから正確な関数呼び出しデータを取得するという、単純な問題のように思われる問題に対する重量級のソリューションのように思えます。

自分のコードで次のように書けるようなものを書きたい

void SomeFunction() {
  StartProfiler();
  ...
  StopProfiler();
}

StartProfiler()実行後、とStopProfiler()(コール ツリー全体) の間で呼び出された関数と、それぞれにかかった時間に関するデータを取得します。

アドレスの代わりに関数名を取得するために、デバッグシンボルも読み取ることができれば幸いです。

4

1 に答える 1

3

これが私が発見した解決策の興味深いヒントです。

gcc(およびllvm> = 3.0)には、-pgコンパイル時にオプションがあります。これは、従来、gprofをサポートするためのものです。このフラグを使用してコードをコンパイルすると、コンパイラはmcountすべての関数定義の先頭に関数の呼び出しを追加します。この関数をオーバーライドすることはできますが、アセンブリで行う必要があります。そうしないと、mcount定義した関数がへの呼び出しでインストルメント化され、呼び出されるmcount前にスタックスペースがすぐに不足しますmain

これが概念実証です。

foo.c:

int total_calls = 0;
void foo(int c) {
  if (c > 0)
    foo(c-1);
}
int main() {
  foo(4);
  printf("%d\n", total_calls);
}

foo.s:

.globl mcount
mcount:
  movl  _total_calls(%rip), %eax
  addl  $1, %eax
  movl  %eax, _total_calls(%rip)
  ret

でコンパイルしclang -pg foo.s foo.c -o fooます。結果:

$ ./foo
6

これは、の場合は1、のmain場合は4、の場合はfoo1ですprintf

これがclangが出力するasmですfoo

_foo:
  pushq %rbp
  movq  %rsp, %rbp
  subq  $16, %rsp
  movl  %edi, -8(%rbp)          ## 4-byte Spill
  callq mcount
  movl  -8(%rbp), %edi          ## 4-byte Reload
  ...
于 2012-09-03T06:46:14.797 に答える