我测试的ANSI C应用构建工具。 简单地负载代码,视图控制流图,运行试验,其中标记被击中的所有顶点。 我试图通过解析代码构建CFG全部由我自己。 不幸的是它被弄乱了,如果代码嵌套。 GCC提供了能力从编译的代码获得CFG。 我可能会写解析器的输出,但我需要行号设置断点。 是否有与输出控制流图时得到行号的方式-fdump-tree-cfg
或-fdump-tree-vcg
?
Answer 1:
对于C程序,你可以看一下现有的Python解析器C的控制流图:
- PyCParser
- pycparser
- pyclibrary (的叉pyclibrary )
- joern
- CoFlo C / C ++控制流图发生器和分析器
调用图是密切相关的结构,以控制流图。 有可用来创建用于C代码调用图(函数依赖)的几种方法。 这可能与控制流图生成进展证明的帮助。 方式C创建的依赖关系图:
使用cflow的 :
- cflow的+ pycflow2dot + 点 (GPL,BSD)cflow的是稳健的,因为它可以处理代码不能编译,例如缺失包括。 如果预处理器指令大量使用,它可能需要
--cpp
选项来预处理代码。 - cflow的+ cflow2dot +点(GPL V2,V3 GPL,Eclipse公共许可协议(EPL)V1)(注意,cflow2dot需要一些路径固定它的工作原理之前)
- cflow的+ cflow2dot.bash (GPL V2,?)
- cflow的+ cflow2vcg (GPL V2,GPL v2)的
- 增强的cflow (GPL V2)与列表中排除来自图形符号
- cflow的+ pycflow2dot + 点 (GPL,BSD)cflow的是稳健的,因为它可以处理代码不能编译,例如缺失包括。 如果预处理器指令大量使用,它可能需要
使用cscope的 :
- cscope的(BSD)
- cscope的+ callgraphviz +点+ XDOT
- cscope的+ VIM CCTree (C调用树浏览器)
- cscope的+ ccglue
- cscope的+ CodeQuery为C,C ++,Python的&爪哇
- cscope的+ Python的HTML制作
- cscope的+ calltree.sh
NCC (CFLOW喜欢)
- KCachegrind (KDE依赖浏览器)
- Calltree
下面的工具不幸的是需要将代码编译,因为它们依赖于输出GCC:
- CodeViz (GPL V2)(薄弱点:需要编译源,因为它使用GCC转储文件cdepn)
- GCC + 埃及 +点(GPL V *,Perl的= GPL |艺术许可证,EPL V1)(
egypt
使用gcc
产生RTL
,所以失败对于任何越野车源代码,甚至如果你只想把重点放在一个单一的文件相比于更强大的一个更大的项目,因此,它不是非常有用cflow
基础的工具链。需要注意的是埃及在默认情况下很好的支持排除从图形库调用,使之清洁。
此外,对于C / C ++文件依赖图可以与创建crowfood
。
Answer 2:
所以我做了一些调查研究,这是不难得到行号节点。 只需添加lineno
选项,这些选项中的一个得到它。 因此,使用-fdump-tree-cfg-lineno
或-fdump-tree-vcg-lineno
。 我花了一些时间来检查,如果这些数字是可靠的 。 在每个节点的VCG格式标签图形的情况下,包含两个数字 。 这些是用于起动和此节点所表示的代码部分的端部的行号。
Answer 3:
Dynamic analysis methods
In this answer I describe a few dynamic analysis methods.
Dynamic methods actually run the program to determine the call graph.
The opposite of dynamic methods are static methods, which try to determine it from the source alone without running the program.
Advantages of dynamic methods:
- catches function pointers and virtual C++ calls. These are present in large numbers in any non-trivial software.
Disadvantages of dynamic methods:
- you have to run the program, which might be slow, or require a setup that you don't have, e.g. cross-compilation
- only functions that were actually called will show. E.g., some functions could be called or not depending on the command line arguments.
KcacheGrind
https://kcachegrind.github.io/html/Home.html
Test program:
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;
}
Usage:
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
You are now left inside an awesome GUI program that contains a lot of interesting performance data.
On the bottom right, select the "Call graph" tab. This shows an interactive call graph that correlates to performance metrics in other windows as you click the functions.
To export the graph, right click it and select "Export Graph". The exported PNG looks like this:
From that we can see that:
- the root node is
_start
, which is the actual ELF entry point, and contains glibc initialization boilerplate f0
,f1
andf2
are called as expected from one anotherpointed
is also shown, even though we called it with a function pointer. It might not have been called if we had passed a command line argument.not_called
is not shown because it didn't get called in the run, because we didn't pass an extra command line argument.
The cool thing about valgrind
is that it does not require any special compilation options.
Therefore, you could use it even if you don't have the source code, only the executable.
valgrind
manages to do that by running your code through a lightweight "virtual machine".
Tested on Ubuntu 18.04.
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
adds callbacks, etrace parses the ELF file and implements all callbacks.
I couldn't get it working however unfortunately: Why doesn't `-finstrument-functions` work for me?
Claimed output is of format:
\-- 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
Likely the most efficient method besides specific hardware tracing support, but has the downside that you have to recompile the code.