获取代码函数调用图的工具[已关闭]

问题描述 投票:0回答:7

我有一个很大的工作空间,里面有很多C代码的源文件。虽然我可以使用对象浏览器看到从 MS VS2005 中的函数调用的函数,在 MSVC 6.0 中也可以,但这仅以非图形显示方式显示从特定函数调用的函数。此外,它没有显示从

main()
开始调用的函数,然后从它调用的函数,依此类推,深入叶级函数。

我需要一个工具,它可以给我一个函数调用图,其中函数

callee
caller
通过箭头或类似的东西连接,从
main()
开始到函数的最后一级,或者至少显示一个调用图以图形方式将所有函数包含在一个 C 源文件中。如果我能打印这个图表就太好了。

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

c function call-graph
7个回答
69
投票

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初始化样板
  • f0
    f1
    f2
    按预期相互调用
  • 即使我们使用函数指针调用它,也会显示
  • 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

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


18
投票

理解在创建调用图方面做得非常好。


12
投票

我们的DMS软件重组工具包具有静态控制/数据流/指向/调用图分析,已应用于庞大的C代码系统(〜2500万行),并生成这样的调用图,包括函数通过函数指针调用


11
投票

您可以尝试 CScope + tceetree + Graphviz


6
投票

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


5
投票

Astrée 是目前最强大、最复杂的工具,恕我直言。

© www.soinside.com 2019 - 2024. All rights reserved.