我有一个很大的工作空间,里面有许多C代码的源文件。虽然我可以使用Object浏览器看到从MS VS2005中的函数调用的函数,但是在MSVC 6.0中,这只显示了在非图形显示中从特定函数调用的函数。此外,它没有显示从say main()
开始调用的函数,然后是从它调用的函数,依此类推,更深入到叶级函数内部。
我需要一个工具,它会给我一个函数调用图,其中函数callee
和caller
用箭头或类似的东西连接起来,从main()
开始到最后一级函数,或至少以图形方式显示一个C源文件中所有函数的调用图。如果我能打印这张图表会很棒。
这样做的好工具(不一定是免费工具)?
答案 0 :(得分:50)
Egypt(自由软件)
KcacheGrind(GPL)
Graphviz(CPL)
CodeViz(GPL)
答案 1 :(得分:25)
动态分析方法
这里我将介绍一些动态分析方法。
动态方法实际运行程序以确定调用图。
动态方法的反面是静态方法,它试图在不运行程序的情况下单独从源中确定它。
动态方法的优点:
动态方法的缺点:
<强> 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
通过轻量级“虚拟机”运行代码来实现这一目标。
在Ubuntu 18.04上测试。
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
adds callbacks,etrace解析ELF文件并实现所有回调。
不幸的是,我无法让它工作:Why doesn't `-finstrument-functions` work for me?
声明的输出格式为:
\-- 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
除了特定的硬件跟踪支持之外,它可能是最有效的方法,但是你必须重新编译代码。
答案 2 :(得分:16)
Understand可以很好地创建调用图。
答案 3 :(得分:7)
我们的DMS Software Reengineering Toolkit已经static control/dataflow/points-to/call graph analysis已经应用于大型系统(~~ 2500万行)的C代码,并生成了这样的调用图,包括通过函数指针调用的函数
答案 4 :(得分:6)
答案 5 :(得分:5)
您可以查看我的基于bash的C调用树生成器here。它允许您指定一个或多个您想要调用者和/或被调用信息的C函数,或者您可以指定一组函数并确定连接它们的函数调用的可达性图... I.e。告诉我main(),foo()和bar()的所有连接方式。它使用graphviz / dot作为图形引擎。
答案 6 :(得分:3)
Astrée是最强大和最复杂的工具,恕我直言。