-fdump-tree-cfg
或 -fdump-tree-vcg
输出控制流图时,是否有办法获取行号?对于C程序的控制流程图,您可以查看现有的Python解析器,如:
调用图是与控制流程图密切相关的构造。 有多种方法可用于为C代码创建调用图(函数依赖关系)。 这可能对控制流程图生成的进展有所帮助。 创建C中依赖关系图的方法:
使用cflow:
cflow + pycflow2dot + dot (GPL,BSD) cflow非常强大,因为它可以处理无法编译的代码,例如缺少包含的代码。如果经常使用预处理器指令,则可能需要--cpp
选项来预处理代码。
cflow + cflow2dot + dot(GPL v2,GPL v3,Eclipse Public License(EPL)v1)(请注意,在其工作之前,cflow2dot需要一些路径修正)
cflow + cflow2dot.bash (GPL v2, ?)
cflow + cflow2vcg (GPL v2, GPL v2)
增强版cflow(GPL v2),可排除图中的符号列表
使用cscope:
cscope (BSD)
cscope + callgraphviz +dot +xdot
cscope +vim CCTree(C调用树浏览器)
cscope + ccglue
cscope +CodeQuery用于C、C++、Python和Java
cscope + Python html生产工具
cscope + calltree.sh
ncc (类似cflow)
KCachegrind (KDE依赖项查看器)
以下工具需要可编译源代码,因为它们依赖于gcc的输出:
egypt
使用gcc
生成RTL
,因此对于任何有缺陷的源代码或者即使你只想关注一个大型项目中的单个文件,都会失败。因此,与更健壮的基于cflow
的工具链相比,它不是非常有用。请注意,默认情况下,egypt具有排除图中的库调用的良好支持,使其更清晰)此外,可以使用crowfood
创建C/C++文件依赖关系图。
我进行了更多的研究,发现获取节点行号并不困难。只需在以下选项之一中添加 lineno
选项即可获取。因此使用 -fdump-tree-cfg-lineno
或 -fdump-tree-vcg-lineno
。我花了一些时间检查这些数字是否可靠。在VCG格式的图形中,每个节点的标签包含两个数字。这些数字是表示该节点所代表的代码部分的开始和结束行号。
动态分析方法
在这个答案中,我描述了几种动态分析方法。
动态方法实际上运行程序以确定调用图。
与动态方法相反的是静态方法,它们试图仅通过源代码而不运行程序来确定调用图。
动态方法的优点:
动态方法的缺点:
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
\-- 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
除了特定的硬件跟踪支持之外,这可能是最有效的方法,但缺点是您必须重新编译代码。