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

68

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

#include <stdio.h>

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

int main (void)
{
  printf("%d\n", triple(10));
  return 0;
}
使用ltrace运行程序将显示对printf的调用,因为它是标准库函数(在我的系统上是动态库),而strace将显示所有启动代码中使用的系统调用,用于实现printf的系统调用以及关闭代码,但我想要一些可以显示被调用的函数triple的工具。假设本地函数未被优化编译器内联且二进制文件未被剥离(符号已删除),是否有这样的工具?
编辑:
一些澄清事项:
- 如果该工具也提供非本地函数的跟踪信息,则可以接受。 - 我不想必须重新编译具有特定工具支持的程序,可执行文件中的符号信息应该足够。 - 如果我可以像使用 ltrace / strace 一样连接到现有进程,那就太好了。

1
你有尝试使用GDB进行跟踪吗?它曾经告诉我只适用于远程目标。也许你可以让GDB与远程目标一起工作并连接到本地主机?不确定,只是一些随机的想法。 - Johannes Schaub - litb
我不想打断程序的流程,如果gdb能像ltrace一样不引人注目地跟踪程序,我愿意尝试一下,如果有人告诉我如何做。 - Robert Gamble
特别是对于GDB:https://dev59.com/questions/bGkw5IYBdhLWcg3w8O-o#31814519 - Ciro Santilli OurBigBook.com
14个回答

1

查看跟踪,一个适用于 Linux C/C++ 应用程序的跟踪框架: https://github.com/baruch/traces#readme

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


1
由于这个问题在搜索结果中非常突出,我将添加另一种方法,比其他列出的方法更简便的方法:uftrace。
它需要使用-pg -g(或者如果您只对函数名称感兴趣而不关心参数和返回值,则使用-finstrument-functions)编译相应的应用程序。 然后,您可以交互式地运行该命令:
uftrace -a --no-libcall -f none <cmd>

或者,也可以先记录追踪数据,然后单独输出。
uftrace record -a --no-libcall -f none <cmd>
uftrace replay

后者也可以跨平台工作,例如,您可以在A系统上运行记录阶段,将跟踪数据(目录uftrace.data)传输到B系统,然后在B机器上进行回放。
使用的选项:
  • -a 启用所有参数和返回值的输出
  • --no-libcall 隐藏所有标准libc函数
  • -f none 隐藏通常在前面打印的持续时间和线程ID列
另一个有用的选项是-N,例如,-N log_* 过滤掉所有以log_开头的函数调用。更多信息请参阅uftrace-replay的man页。
如果输出缺少返回值,请在禁用链接时优化后重试。由于某种原因,这在我的测试中使它们隐藏起来。
使用原始选项交互式运行OP的示例(注意:30是程序输出到stdout的结果):
# uftrace a.out 
30
# DURATION     TID     FUNCTION
   0.671 us [802533] | __monstartup();
   0.421 us [802533] | __cxa_atexit();
            [802533] | main() {
   0.060 us [802533] |   triple();
  11.882 us [802533] |   printf();
  12.263 us [802533] | } /* main */

C++ fac示例只是展示了要点:
# uftrace --no-libcall -a a.out
1
1
2
6
# DURATION     TID     FUNCTION
            [803250] | _GLOBAL__sub_I_fac() {
 107.551 us [803250] |   __static_initialization_and_destruction_0(1, 65535);
 108.473 us [803250] | } /* _GLOBAL__sub_I_fac */
            [803250] | main() {
   0.211 us [803250] |   fac(0) = 1;
            [803250] |   fac(1) {
   0.080 us [803250] |     fac(0) = 1;
   0.491 us [803250] |   } = 1; /* fac */
            [803250] |   fac(2) {
            [803250] |     fac(1) {
   0.070 us [803250] |       fac(0) = 1;
   0.340 us [803250] |     } = 1; /* fac */
   0.541 us [803250] |   } = 2; /* fac */
            [803250] |   fac(3) {
            [803250] |     fac(2) {
            [803250] |       fac(1) {
   2.464 us [803250] |         fac(0) = 1;
   2.725 us [803250] |       } = 1; /* fac */
   2.916 us [803250] |     } = 2; /* fac */
   3.086 us [803250] |   } = 6; /* fac */
  33.463 us [803250] | } = 0; /* main */

0

注意:这不是基于Linux内核的ftrace,而是我最近设计的一种工具,用于实现本地函数跟踪和控制流。公开支持Linux ELF x86_64 / x86_32。

https://github.com/leviathansecurity/ftrace


你能概括一下这里的工作原理吗?为什么不使用例如GDB的Python API呢? - Ciro Santilli OurBigBook.com

0

2
我已经研究了所有关于valgrind的工具,但没有一个能满足我的需求。 - Robert Gamble

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接