我有一个很大的工作空间,里面有很多C代码的源文件。虽然我可以使用对象浏览器看到从 MS VS2005 中的函数调用的函数,在 MSVC 6.0 中也可以,但这仅以非图形显示方式显示从特定函数调用的函数。此外,它没有显示从
main()
开始调用的函数,然后从它调用的函数,依此类推,深入叶级函数。
我需要一个工具,它可以给我一个函数调用图,其中函数
callee
和caller
通过箭头或类似的东西连接,从main()
开始到函数的最后一级,或者至少显示一个调用图以图形方式将所有函数包含在一个 C 源文件中。如果我能打印这个图表就太好了。
有什么好的工具可以做到这一点(不需要是免费工具)?
埃及(免费软件)
KcacheGrind(GPL)
Graphviz(CPL)
CodeViz(GPL)
动态分析方法
这里介绍几种动态分析方法。
动态方法实际上运行程序来确定调用图。
与动态方法相反的是静态方法,它尝试仅从源头确定它,而不运行程序。
动态方法的优点:
动态方法的缺点:
KcacheGrind
https://kcachegrind.github.io/html/Home.html
测试程序:
int f2(int i) { return i + 2; }
int f1(int i) { return f2(2) + i + 1; }
int f0(int i) { return f1(1) + f2(2); }
int pointed(int i) { return i; }
int not_called(int i) { return 0; }
int main(int argc, char **argv) {
int (*f)(int);
f0(1);
f1(1);
f = pointed;
if (argc == 1)
f(1);
if (argc == 2)
not_called(1);
return 0;
}
用途:
sudo apt-get install -y kcachegrind valgrind
# Compile the program as usual, no special flags.
gcc -ggdb3 -O0 -o main -std=c99 main.c
# Generate a callgrind.out.<PID> file.
valgrind --tool=callgrind ./main
# Open a GUI tool to visualize callgrind data.
kcachegrind callgrind.out.1234
您现在处于一个很棒的 GUI 程序中,其中包含许多有趣的性能数据。
在右下角,选择“调用图”选项卡。这显示了一个交互式调用图,当您单击函数时,该调用图与其他窗口中的性能指标相关联。
要导出图表,请右键单击它并选择“导出图表”。导出的PNG看起来像这样:
从中我们可以看出:
_start
,它是实际的ELF入口点,包含glibc初始化样板f0
、f1
和 f2
按预期相互调用pointed
。如果我们传递了命令行参数,它可能不会被调用。not_called
未显示,因为它在运行中没有被调用,因为我们没有传递额外的命令行参数。valgrind
最酷的一点是它不需要任何特殊的编译选项。
因此,即使您没有源代码,只有可执行文件,您也可以使用它。
valgrind
设法通过轻量级“虚拟机”运行代码来做到这一点。与本机执行相比,这也使得执行速度极其缓慢。
如图所示,还获得了每个函数调用的时序信息,这可以用于分析程序,这可能是此设置的原始用例,而不仅仅是查看调用图:如何我分析在 Linux 上运行的 C++ 代码?
在 Ubuntu 18.04 上测试。
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
添加回调,etrace解析ELF文件并实现所有回调。
不幸的是,我无法让它工作:为什么“-finstrument-functions”对我不起作用?
声明的输出格式为:
\-- main
| \-- Crumble_make_apple_crumble
| | \-- Crumble_buy_stuff
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | \-- Crumble_prepare_apples
| | | \-- Crumble_skin_and_dice
| | \-- Crumble_mix
| | \-- Crumble_finalize
| | | \-- Crumble_put
| | | \-- Crumble_put
| | \-- Crumble_cook
| | | \-- Crumble_put
| | | \-- Crumble_bake
可能是除了特定硬件跟踪支持之外最有效的方法,但缺点是您必须重新编译代码。
理解在创建调用图方面做得非常好。
我们的DMS软件重组工具包具有静态控制/数据流/指向/调用图分析,已应用于庞大的C代码系统(〜2500万行),并生成这样的调用图,包括函数通过函数指针调用。
您可以在此处查看我的基于 bash 的 C 调用树生成器。它允许您指定一个或多个您想要调用者和/或被调用信息的 C 函数,或者您可以指定一组函数并确定连接它们的函数调用的可达性图...即告诉我 main()、foo() 和 bar() 的所有连接方式。它使用 graphviz/dot 作为图形引擎。
Astrée 是目前最强大、最复杂的工具,恕我直言。