为C / C ++编写仪器分析器的最简单方法是什么?

时间:2012-09-03 05:40:14

标签: c++ c profiling

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

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

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

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

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

1 个答案:

答案 0 :(得分: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

main为1,foo为4,printf为1。

这是clang为foo发出的asm:

_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
  ...