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

时间:2009-02-05 19:44:08

标签: c function call-graph

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

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

这样做的好工具(不一定是免费工具)?

7 个答案:

答案 0 :(得分:50)

答案 1 :(得分:25)

动态分析方法

这里我将介绍一些动态分析方法。

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

动态方法的反面是静态方法,它试图在不运行程序的情况下单独从源中确定它。

动态方法的优点:

  • 捕获函数指针和虚拟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通过轻量级“虚拟机”运行代码来实现这一目标。

在Ubuntu 18.04上测试。

gcc -finstrument-functions + etrace

https://github.com/elcritch/etrace

-finstrument-functions adds callbacks,etrace解析ELF文件并实现所有回调。

不幸的是,我无法让它工作:Why doesn't `-finstrument-functions` work for me?

声明的输出格式为:

\-- 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 :(得分:16)

Understand可以很好地创建调用图。

答案 3 :(得分:7)

我们的DMS Software Reengineering Toolkit已经static control/dataflow/points-to/call graph analysis已经应用于大型系统(~~ 2500万行)的C代码,并生成了这样的调用图,包括通过函数指针调用的函数

答案 4 :(得分:6)

您可以尝试使用CScope + tceetree + Graphviz

答案 5 :(得分:5)

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

答案 6 :(得分:3)

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