我正在尝试生成一个调用图,用它来找出所有可能的执行路径(这样我就不必手动找出所有路径,因为有很多路径会导致这个功能)。例如:
path 1: A -> B -> C -> D
path 2: A -> B -> X -> Y -> D
path 3: A -> G -> M -> N -> O -> P -> S -> D
...
path n: ...
我尝试过CodeViz和Doxygen。不知何故,这两个结果只显示目标函数 D 的被调用者。在我的例子中,D 是一个类的成员函数,该类的对象将被包装在智能指针中。客户端始终会通过工厂获取智能指针对象,以便调用 D。
我怎样才能实现这个目标?
static void D() { }
static void Y() { D(); }
static void X() { Y(); }
static void C() { D(); X(); }
static void B() { C(); }
static void S() { D(); }
static void P() { S(); }
static void O() { P(); }
static void N() { O(); }
static void M() { N(); }
static void G() { M(); }
static void A() { B(); G(); }
int main() {
A();
}
然后
$ clang++ -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
$ dot -Tpng -ocallgraph.png callgraph.dot
产生一些闪亮的图片(有一个“外部节点”,因为
main
具有外部链接,也可能从该翻译单元外部调用):
您可能需要使用
c++filt
对其进行后处理,以便获得所涉及的函数和类的完整名称。就像下面这样
#include <vector>
struct A {
A(int);
void f(); // not defined, prevents inlining it!
};
int main() {
std::vector<A> v;
v.push_back(42);
v[0].f();
}
$ clang++ -S -emit-llvm main1.cpp -o - |
opt -analyze -std-link-opts -dot-callgraph
$ cat callgraph.dot |
c++filt |
sed 's,>,\\>,g; s,-\\>,->,g; s,<,\\<,g' |
gawk '/external node/{id=$1} $1 != id' |
dot -Tpng -ocallgraph.png
产生这种美丽(天啊,没有打开优化的尺寸太大了!)
那个神秘的未命名函数,
Node0x884c4e0
,是一个占位符,假定由任何定义未知的函数调用。
静态计算准确的 C++ 调用图很困难,因为您需要精确的语言解析器、正确的名称查找和正确尊重语言语义的良好指向分析器。 Doxygen 没有这些,我不知道为什么人们声称喜欢它的 C++;构建 Doxygen 错误分析的 10 行 C++ 示例很容易)。
您可能最好运行一个时序分析器,它动态收集调用图(这描述了我们的)并简单地练习很多案例。这样的分析器将向您显示实际执行的调用图。
编辑:我突然想起了理解C++,它声称可以构造调用图。我不知道他们使用什么解析器,也不知道他们的详细分析是否正确;我对他们的产品几乎没有具体的经验。我的几次遭遇表明它不进行点分析。
Schaub 使用 Clang 的回答给我留下了深刻的印象;我希望 Clang 拥有正确的所有元素。
为了让
clang++
命令找到像 mpi.h
这样的标准头文件,应使用两个附加选项 -### -fsyntax-only
,即完整命令应如下所示:
clang++ -### -fsyntax-only -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
“C++ Bsc 分析器”可以通过读取 bscmake 实用程序生成的文件来显示调用图。
doxygen + graphviz可以解决我们想要生成调用图时的大多数问题,接下来交给人力。
Scitools Understanding 是一个神奇工具,比我所知道的所有逆向工程都要好,并且生成高质量的图表。
但请注意,它相当昂贵,并且试用版的蝴蝶调用图仅限于一级调用(恕我直言,我相信他们不会帮助自己这样做......)
GNU cflow
cflow --tree --number main.c a.c b.c