113

我有一个很大的工作空间,里面有很多 C 代码的源文件。虽然我可以使用对象浏览器在 MS VS2005 中以及在 MSVC 6.0 中看到从函数调用的函数,但这仅在非图形类型的显示中显示从特定函数调用的函数。此外,它没有显示从 say 开始调用main()的函数,然后是从它调用的函数,等等,更深入到叶级函数。

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

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

4

7 回答 7

61
于 2009-02-05T20:34:26.233 回答
33

动态分析方法

这里我介绍几种动态分析方法。

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

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

动态方法的优点:

  • 捕获函数指针和虚拟 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设法通过轻量级“虚拟机”运行您的代码来做到这一点。与本机执行相比,这也使得执行速度极慢。

从图中可以看出,还获得了每个函数调用的时序信息,这可以用来分析程序,这很可能是这个设置的原始用例,而不仅仅是查看调用图:How can I profile在 Linux 上运行的 C++ 代码?

在 Ubuntu 18.04 上测试。

gcc -finstrument-functions+ 跟踪

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

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

于 2015-07-02T16:39:39.757 回答
18

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

于 2009-02-05T19:49:49.427 回答
11

我们的DMS Software Reengineering Toolkit具有静态控制/数据流/指向/调用图分析,已应用于庞大的 C 代码系统(~2500 万行),并生成了此类调用图,包括通过函数指针调用的函数

于 2010-12-04T20:00:51.050 回答
9

您可以尝试 CScope + tceetree + Graphviz

于 2014-08-21T21:26:35.503 回答
6

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

于 2015-07-03T03:42:16.020 回答
5

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

于 2009-02-17T13:32:46.740 回答