获取代码函数调用图的工具

125

我有一个较大的工作空间,其中有许多C代码的源文件。虽然我可以在MS VS2005中使用对象浏览器看到从函数调用的函数,并且在MSVC 6.0中也可以,但这只显示了以非图形化的方式从特定函数调用的函数。此外,它不会显示从例如main()开始调用的函数,然后是从它调用的函数,依此类推,更深入地到达叶级别函数。

我需要一个工具,可以给我一个函数调用图,用calleecaller之间的箭头或类似于箭头的东西进行连接,从main()开始到最后一级函数,或者至少以图形方式显示一个C源文件中所有函数的调用图。如果我可以打印这张图就太好了。

有没有好的工具可以做到这一点(不需要是免费工具)?


4
相关链接:https://dev59.com/5WQo5IYBdhLWcg3wBLYo#17844310 - 0 _
http://en.wikipedia.org/wiki/Call_graph#Software - 象嘉道
相关链接:*如何为C++代码生成调用图* - undefined
7个回答

68

2
关于CodeViz的问题,如果您将代码传递给它,它会生成代码吗?还是您需要自己使用CodeViz制作图形? - Mohammad Reza Rezwani
5
我刚刚尝试了埃及,它的图形非常糟糕。我不确定其他游戏的情况。 - ar2015

34

动态分析方法

这里我描述一些动态分析方法。

动态方法实际上运行程序以确定调用图。

与动态方法相反的是静态方法,它们试图在不运行程序的情况下仅从源代码中确定它。

动态方法的优点:

  • 捕获函数指针和虚拟 C++ 调用。这些在任何非平凡软件中都存在大量。

动态方法的缺点:

  • 您必须运行程序,这可能会很慢,或者需要您没有的设置,例如交叉编译
  • 只有实际调用的函数才会显示。例如,某些函数可能会根据命令行参数被调用或不被调用。

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初始化样板
  • f0f1f2按预期彼此调用
  • 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

除了特定的硬件跟踪支持之外,这可能是最有效的方法,但缺点是您必须重新编译代码。


2
请注意,动态调用图仅覆盖程序的一次运行。 - smwikipedia
1
@smwikipedia 是的,我已经更新了答案以使其更清晰。 - Ciro Santilli OurBigBook.com
此外,这里也有解释 - https://dev59.com/_nVC5IYBdhLWcg3wYQAp - tauseef_CuriousGuy

18

12

1
啊,好的,现在是2016年,一个踩票者出现了。我相信他的踩票是基于准确的评估,即这个工具不能做到这一点。嗯,也许不是。它确实做到了OP所请求的。 - Ira Baxter
2
接受一枚赞成票来抵消那个反对的声音。只要它能完成工作,我不在乎它是你的软件还是专有软件 :-) - Ciro Santilli OurBigBook.com

10

如何使用这个组合来获得图形,是否有教程或至少一些解释。 - Muhammad Yusuf
1
@Muhammad Yusuf 是的,有一个维基页面 - BillyJoe
对于通过谷歌找到这篇文章的人,这个工具链也适用于其他编程语言,包括Processing和Java,尽管它似乎不喜欢Java的对象初始化,并且无法识别new作为函数调用。 - John Harlan

6
您可以在这里查看我基于Bash的C函数调用树生成器。它允许您指定一个或多个C函数,以获取调用者和/或被调用信息,或者您可以指定一组函数并确定连接它们的函数调用的可达性图...即告诉我 main()、foo() 和 bar() 之间的所有连接方式。它使用graphviz/dot作为绘图引擎。

5

Astrée是我认为目前最强大和先进的工具。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接