3

我见过一些工具,如PinDynInst,它们可以进行动态代码操作,以便在无需重新编译的情况下检测代码。这些似乎是对似乎应该是一个简单问题的重量级解决方案:从程序中检索准确的函数调用数据。

我想写一些东西,以便在我的代码中,我可以写

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

和执行后,检索有关在StartProfiler()StopProfiler()(整个调用树)之间调用了哪些函数以及每个函数花费了多长时间的数据。

最好我也可以读出调试符号,以获取函数名称而不是地址。

4

1 回答 1

3

这是我发现的解决方案的一个有趣提示。

gcc(和 llvm>=3.0)-pg在编译时有一个选项,传统上用于 gprof 支持。当您使用此标志编译代码时,编译器会将对该函数的调用添加到mcount每个函数定义的开头。您可以覆盖此函数,但您需要在汇编中执行此操作,否则mcount您定义的函数将被调用,mcount并且您将在调用之前快速耗尽堆栈空间main

这是一个小概念证明:

富.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, 4foo和 1 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 回答