我见过一些像Pin和DynInst这样的工具,它们可以进行动态代码操作,无需重新编译即可检测代码。这些似乎是重量级解决方案似乎应该是一个简单的问题:从程序中检索准确的函数调用数据。
我想写一些东西,在我的代码中,我可以写
void SomeFunction() {
StartProfiler();
...
StopProfiler();
}
和执行后,检索有关StartProfiler()
和StopProfiler()
(整个调用树)之间调用的函数的数据以及每个函数花费的时间。
最好我也可以读出调试符号,以获取函数名而不是地址。
答案 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
...