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

发布于 2024-07-12 12:21:02 字数 1542 浏览 10 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

早茶月光 2024-07-19 12:21:02
小ぇ时光︴ 2024-07-19 12:21:02

动态分析方法

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

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

与动态方法相反的是静态方法,它试图仅从源头确定它,而不运行程序。

动态方法的优点:

  • 捕获函数指针和虚拟 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、f1f2 按预期相互调用,
  • pointed 也显示出来,即使我们使用函数指针调用它。 如果我们传递了命令行参数,它可能不会被调用。
  • not_used 未显示,因为它在运行中没有被调用,因为我们没有传递额外的命令行参数。

valgrind 的一个很酷的事情是它不需要任何特殊的编译选项。

因此,即使您没有源代码,只有可执行文件,您也可以使用它。

valgrind 设法通过轻量级“虚拟机”运行代码来做到这一点。 与本机执行相比,这也使得执行速度极其缓慢。

从图中可以看出,还获得了有关每个函数调用的计时信息,这可以用于分析程序,这可能是此设置的原始用例,而不仅仅是查看调用图:如何分析在 Linux 上运行的 C++ 代码?

在 Ubuntu 18.04 上测试。

gcc -finstrument-functions + etrace

https://github。 com/elcritch/etrace

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

然而不幸的是,我无法让它工作: 为什么不 `-finstrument-函数对我有用吗?

声明的输出格式为:

\-- 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

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

Dynamic analysis methods

Here I describe a few dynamic analysis methods.

Dynamic methods actually run the program to determine the call graph.

The opposite of dynamic methods are static methods, which try to determine it from the source alone without running the program.

Advantages of dynamic methods:

  • catches function pointers and virtual C++ calls. These are present in large numbers in any non-trivial software.

Disadvantages of dynamic methods:

  • you have to run the program, which might be slow, or require a setup that you don't have, e.g. cross-compilation
  • only functions that were actually called will show. E.g., some functions could be called or not depending on the command line arguments.

KcacheGrind

https://kcachegrind.github.io/html/Home.html

Test program:

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;
}

Usage:

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

You are now left inside an awesome GUI program that contains a lot of interesting performance data.

On the bottom right, select the "Call graph" tab. This shows an interactive call graph that correlates to performance metrics in other windows as you click the functions.

To export the graph, right click it and select "Export Graph". The exported PNG looks like this:

From that we can see that:

  • the root node is _start, which is the actual ELF entry point, and contains glibc initialization boilerplate
  • f0, f1 and f2 are called as expected from one another
  • pointed is also shown, even though we called it with a function pointer. It might not have been called if we had passed a command line argument.
  • not_called is not shown because it didn't get called in the run, because we didn't pass an extra command line argument.

The cool thing about valgrind is that it does not require any special compilation options.

Therefore, you could use it even if you don't have the source code, only the executable.

valgrind manages to do that by running your code through a lightweight "virtual machine". This also makes execution extremely slow compared to native execution.

As can be seen on the graph, timing information about each function call is also obtained, and this can be used to profile the program, which is likely the original use case of this setup, not just to see call graphs: How can I profile C++ code running on Linux?

Tested on Ubuntu 18.04.

gcc -finstrument-functions + etrace

https://github.com/elcritch/etrace

-finstrument-functions adds callbacks, etrace parses the ELF file and implements all callbacks.

I couldn't get it working however unfortunately: Why doesn't `-finstrument-functions` work for me?

Claimed output is of format:

\-- 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

Likely the most efficient method besides specific hardware tracing support, but has the downside that you have to recompile the code.

毁虫ゝ 2024-07-19 12:21:02

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

Understand does a very good job of creating call graphs.

小糖芽 2024-07-19 12:21:02

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

Our DMS Software Reengineering Toolkit has static control/dataflow/points-to/call graph analysis that has been applied to huge systems (~~25 million lines) of C code, and produced such call graphs, including functions called via function pointers.

小巷里的女流氓 2024-07-19 12:21:02

您可以尝试 CScope + tceetree + Graphviz。

You may try CScope + tceetree + Graphviz.

等往事风中吹 2024-07-19 12:21:02

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

You can check out my bash-based C call tree generator here. It lets you specify one or more C functions for which you want caller and/or called information, or you can specify a set of functions and determine the reachability graph of function calls that connects them... I.e. tell me all the ways main(), foo(), and bar() are connected. It uses graphviz/dot for a graphing engine.

滥情稳全场 2024-07-19 12:21:02

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

Astrée is the most robust and sophisticated tool out there, IMHO.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文