在 Linux 中跟踪本地函数调用的工具

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/311840/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 16:45:17  来源:igfitidea点击:

Tool to trace local function calls in Linux

linuxdebuggingtrace

提问by Robert Gamble

I am looking for a tool like ltraceor stracethat can trace locally defined functions in an executable. ltrace only traces dynamic library calls and strace only traces system calls. For example, given the following C program:

我正在寻找像ltracestrace这样的工具,可以在可执行文件中跟踪本地定义的函数。ltrace 只跟踪动态库调用,strace 只跟踪系统调用。例如,给定以下 C 程序:

#include <stdio.h>

int triple ( int x )
{
  return 3 * x;
}

int main (void)
{
  printf("%d\n", triple(10));
  return 0;
}

Running the program with ltracewill show the call to printfsince that is a standard library function (which is a dynamic library on my system) and stracewill show all the system calls from the startup code, the system calls used to implement printf, and the shutdown code, but I want something that will show me that the function triplewas called. Assuming that the local functions have not been inlined by an optimizing compiler and that the binary has not been stripped (symbols removed), is there a tool that can do this?

运行程序ltrace将显示调用,printf因为它是一个标准库函数(这是我系统上的动态库),strace并将显示来自启动代码的所有系统调用、用于实现 printf 的系统调用和关闭代码,但我想要一些可以告诉我该函数triple被调用的东西。假设本地函数没有被优化编译器内联并且二进制文件没有被剥离(删除符号),是否有工具可以做到这一点?

Edit

编辑

A couple of clarifications:

几点说明:

  • It is okay if the tool also provides trace information for non-local functions.
  • I don't want to have to recompile the program(s) with support for specific tools, the symbol information in the executable should be enough.
  • I would be really nice if I could use the tool to attach to existing processes like I can with ltrace/strace.
  • 如果该工具还提供非本地函数的跟踪信息,那也没关系。
  • 我不想在支持特定工具的情况下重新编译程序,可执行文件中的符号信息应该足够了。
  • 如果我可以像使用 ltrace/strace 一样使用该工具附加到现有进程,我会非常高兴。

采纳答案by Johannes Schaub - litb

Assuming you only want to be notified for specific functions, you can do it like this:

假设您只想收到特定功能的通知,您可以这样做:

compile with debug informations (as you already have symbol informations, you probably also have enough debugs in)

用调试信息编译(因为你已经有了符号信息,你可能也有足够的调试信息)

given

给予

#include <iostream>

int fac(int n) {
    if(n == 0)
        return 1;
    return n * fac(n-1);
}

int main()
{
    for(int i=0;i<4;i++)
        std::cout << fac(i) << std::endl;
}

Use gdb to trace:

使用 gdb 进行跟踪:

[js@HOST2 cpp]$ g++ -g3 test.cpp
[js@HOST2 cpp]$ gdb ./a.out
(gdb) b fac
Breakpoint 1 at 0x804866a: file test.cpp, line 4.
(gdb) commands 1
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>silent
>bt 1
>c
>end
(gdb) run
Starting program: /home/js/cpp/a.out
#0  fac (n=0) at test.cpp:4
1
#0  fac (n=1) at test.cpp:4
#0  fac (n=0) at test.cpp:4
1
#0  fac (n=2) at test.cpp:4
#0  fac (n=1) at test.cpp:4
#0  fac (n=0) at test.cpp:4
2
#0  fac (n=3) at test.cpp:4
#0  fac (n=2) at test.cpp:4
#0  fac (n=1) at test.cpp:4
#0  fac (n=0) at test.cpp:4
6

Program exited normally.
(gdb)

Here is what i do to collect all function's addresses:

这是我收集所有函数地址的方法:

tmp=$(mktemp)
readelf -s ./a.out | gawk '
{ 
  if( == "FUNC" &&  != 0) { 
    print "# code for " $NF; 
    print "b *0x" ; 
    print "commands"; 
    print "silent"; 
    print "bt 1"; 
    print "c"; 
    print "end"; 
    print ""; 
  } 
}' > $tmp; 
gdb --command=$tmp ./a.out; 
rm -f $tmp

Note that instead of just printing the current frame(bt 1), you can do anything you like, printing the value of some global, executing some shell command or mailing something if it hits the fatal_bomb_explodedfunction :) Sadly, gcc outputs some "Current Language changed" messages in between. But that's easily grepped out. No big deal.

请注意,除了打印当前帧(bt 1)之外,您还可以做任何您喜欢的事情,打印一些全局的值,执行一些 shell 命令或在遇到fatal_bomb_exploded函数时发送一些东西:) 遗憾的是,gcc 输出了一些“当前语言已更改”之间的消息。但这很容易解决。没什么大不了。

回答by activout.se

Hopefully the callgrind or cachegrind toolsfor Valgrindwill give you the information you seek.

希望在callgrind或cachegrind工具用于Valgrind的会给你你寻找的信息。

回答by Sergey Golovchenko

Gprofmight be what you want

Gprof可能就是你想要的

回答by Kent Fredric

If you externalize that function into an external library, you should also be able to see it getting called, ( with ltrace ).

如果您将该函数外部化到外部库中,您还应该能够看到它被调用(使用 ltrace )。

The reason this works is because ltrace puts itself between your app and the library, and when all the code is internalized with the one file it can't intercept the call.

这样做的原因是因为 ltrace 将自己置于您的应用程序和库之间,并且当所有代码都与一个文件内化时,它无法拦截调用。

ie: ltrace xterm

即:ltrace xterm

spews stuff from X libraries, and X is hardly system.

从 X 库中喷涌而出的东西,X 几乎不是系统。

Outside this, the only real way to do it is compile-time intercept via prof flags or debug symbols.

除此之外,唯一真正的方法是通过 prof 标志或调试符号进行编译时拦截。

I just ran over this app, which looks interesting:

我刚刚浏览了这个看起来很有趣的应用程序:

http://www.gnu.org/software/cflow/

http://www.gnu.org/software/cflow/

But I dont think thats what you want.

但我不认为那是你想要的。

回答by Kent Fredric

$ sudo yum install frysk
$ ftrace -sym:'*' -- ./a.out

More: ftrace.1

更多:ftrace.1

回答by philant

Assuming you can re-compile (no source change required) the code you want to trace with the gcc option -finstrument-functions, you can use etraceto get the function call graph.

假设您可以使用 gcc 选项重新编译(无需更改源代码)要跟踪的代码-finstrument-functions,则可以使用etrace获取函数调用图。

Here is what the output looks like:

这是输出的样子:

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

On Solaris, truss (strace equivalent) has the ability to filter the library to be traced. I'm was surprised when I discovered strace doesn't have such a capability.

在 Solaris 上,truss(等效于 strace)能够过滤要跟踪的库。当我发现 strace 没有这样的能力时,我感到很惊讶。

回答by philant

System Tapcan be used on a modern Linux box (Fedora 10, RHEL 5, etc.).

System Tap可用于现代 Linux 机器(Fedora 10、RHEL 5 等)。

First download the para-callgraph.stpscript.

首先下载para-callgraph.stp脚本。

Then run:

然后运行:

$ sudo stap para-callgraph.stp 'process("/bin/ls").function("*")' -c /bin/ls
0    ls(12631):->main argc=0x1 argv=0x7fff1ec3b038
276  ls(12631): ->human_options spec=0x0 opts=0x61a28c block_size=0x61a290
365  ls(12631): <-human_options return=0x0
496  ls(12631): ->clone_quoting_options o=0x0
657  ls(12631):  ->xmemdup p=0x61a600 s=0x28
815  ls(12631):   ->xmalloc n=0x28
908  ls(12631):   <-xmalloc return=0x1efe540
950  ls(12631):  <-xmemdup return=0x1efe540
990  ls(12631): <-clone_quoting_options return=0x1efe540
1030 ls(12631): ->get_quoting_style o=0x1efe540

See also: Observe, systemtap and oprofile updates

另请参阅:观察、systemtap 和 oprofile 更新

回答by Tom

If the functions aren't inlined, you might even have luck using objdump -d <program>.

如果函数没有内联,你甚至可能会幸运地使用objdump -d <program>.

For an example, let's take a loot at the beginning of GCC 4.3.2's mainroutine:

举个例子,让我们在 GCC 4.3.2 的main例程开始时拿个 loot :

$ objdump `which gcc` -d | grep '\(call\|main\)' 

08053270 <main>:
8053270:    8d 4c 24 04             lea    0x4(%esp),%ecx
--
8053299:    89 1c 24                mov    %ebx,(%esp)
805329c:    e8 8f 60 ff ff          call   8049330 <strlen@plt>
80532a1:    8d 04 03                lea    (%ebx,%eax,1),%eax
--
80532cf:    89 04 24                mov    %eax,(%esp)
80532d2:    e8 b9 c9 00 00          call   805fc90 <xmalloc_set_program_name>
80532d7:    8b 5d 9c                mov    0xffffff9c(%ebp),%ebx
--
80532e4:    89 04 24                mov    %eax,(%esp)
80532e7:    e8 b4 a7 00 00          call   805daa0 <expandargv>
80532ec:    8b 55 9c                mov    0xffffff9c(%ebp),%edx
--
8053302:    89 0c 24                mov    %ecx,(%esp)
8053305:    e8 d6 2a 00 00          call   8055de0 <prune_options>
805330a:    e8 71 ac 00 00          call   805df80 <unlock_std_streams>
805330f:    e8 4c 2f 00 00          call   8056260 <gcc_init_libintl>
8053314:    c7 44 24 04 01 00 00    movl   ##代码##x1,0x4(%esp)
--
805331c:    c7 04 24 02 00 00 00    movl   ##代码##x2,(%esp)
8053323:    e8 78 5e ff ff          call   80491a0 <signal@plt>
8053328:    83 e8 01                sub    ##代码##x1,%eax

It takes a bit of effort to wade through all of the assembler, but you can see all possible calls from a given function. It's not as easy to use as gprofor some of the other utilities mentioned, but it has several distinct advantages:

遍历所有汇编程序需要一些努力,但您可以看到来自给定函数的所有可能调用。它不像gprof提到的其他一些实用程序那么容易使用,但它有几个明显的优点:

  • You generally don't need to recompile an application to use it
  • It shows all possible function calls, whereas something like gprofwill only show the executed function calls.
  • 您通常不需要重新编译应用程序即可使用它
  • 它显示所有可能的函数调用,而 likegprof只会显示已执行的函数调用。

回答by osgx

There is a shell script for automatizating tracing function calls with gdb. But it can't attach to running process.

有一个 shell 脚本,用于使用 gdb 自动化跟踪函数调用。但它不能附加到正在运行的进程。

blog.superadditive.com/2007/12/01/call-graphs-using-the-gnu-project-debugger/

blog.superadditive.com/2007/12/01/call-graphs-using-the-gnu-project-debugger/

Copy of the page - http://web.archive.org/web/20090317091725/http://blog.superadditive.com/2007/12/01/call-graphs-using-the-gnu-project-debugger/

页面副本 - http://web.archive.org/web/20090317091725/http://blog.superadditive.com/2007/12/01/call-graphs-using-the-gnu-project-debugger/

Copy of the tool - callgraph.tar.gz

工具副本 - callgraph.tar.gz

http://web.archive.org/web/20090317091725/http://superadditive.com/software/callgraph.tar.gz

http://web.archive.org/web/20090317091725/http://superadditive.com/software/callgraph.tar.gz

It dumps all functions from program and generate a gdb command file with breakpoints on each function. At each breakpoint, "backtrace 2" and "continue" are executed.

它从程序中转储所有函数并生成一个 gdb 命令文件,每个函数上都有断点。在每个断点处,执行“backtrace 2”和“continue”。

This script is rather slow on big porject (~ thousands of functions), so i add a filter on function list (via egrep). It was very easy, and I use this script almost evry day.

这个脚本在大型项目(〜数千个函数)上相当慢,所以我在函数列表上添加了一个过滤器(通过 egrep)。这很容易,我几乎每天都使用这个脚本。

回答by Greythorn

See traces, a tracing framework for Linux C/C++ applications: https://github.com/baruch/traces#readme

参见 traces,Linux C/C++ 应用程序的跟踪框架:https: //github.com/baruch/traces#readme

It requires recompiling your code with its instrumentor, but will provide a listing of all functions, their parameters and return values. There's an interactive to allow easy navigation of large data samples.

它需要使用其检测器重新编译您的代码,但会提供所有函数、它们的参数和返回值的列表。有一个交互式可以轻松导航大型数据样本。