为 C/C++ 编写检测分析器的最简单方法是啥?

Posted

技术标签:

【中文标题】为 C/C++ 编写检测分析器的最简单方法是啥?【英文标题】:What's the easiest way to write an instrumenting profiler for C/C++?为 C/C++ 编写检测分析器的最简单方法是什么? 【发布时间】:2012-09-03 05:40:13 【问题描述】:

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

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

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

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

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

【问题讨论】:

如果您可以使用 Linux,请尝试使用 callgrind(来自 valgrind)来获取调用树。它将动态翻译和检测您的程序,并以树格式显示所有函数调用(+check kcachgrind GUI)。还有一些系统范围的分析器能够绘制调用树(如 linux pref 或 google-perftools),但它们的调用图仅在某个采样间隔(例如每 1 毫秒)获取并且不准确。 我在 OS X 上,但我认为(部分)valgrind 最近已经移植到那里? valgrind 仅适用于 OSX 10.6/10.7; 10.8 支持有限。此外,callgrind 没有“START”/“STOP”宏(只有 --instr-atstart=no 和 callgrind_control 实用程序),它会在不转储完整调用跟踪的情况下绘制树的摘要(但在内部它有一个)。此外,如果您想跟踪所有调用(大多数答案只需要脚本和 gdb),请检查此线程 ***.com/questions/311840/…。我通常使用这个解决方案:blog.superadditive.com/2007/12/01/… 【参考方案1】:

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

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

【讨论】:

为什么要在汇编中这样做,当我们可以用 C 语言编写它并放入单独的源文件时,该文件将在没有 -pg 的情况下进行编译。 这是个好主意 :) 另一种可能性是用 C 语言编写它并编写一个小的汇编 shim,它会在生成的 callq mcount 之后跳转到函数中。

以上是关于为 C/C++ 编写检测分析器的最简单方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 Win/Linux 上使用 C/C++ 为大页面分配内存的最简单方法是啥? [关闭]

在 .NET 应用程序中查看所有调用的最简单方法(分析/检测)

检测图像中矩形的最简单*正确*方法是啥?

从终端检测脚本中键盘输入的最简单方法是啥?

在 Linux 中检测 CDROM 介质移除/插入的最简单方法是啥

使用 Fast/Faster-RCNN 在 C++ 上制作对象检测器的最简单方法是啥?