【问题标题】:Tracing all functions calls and printing out their parameters (userspace)跟踪所有函数调用并打印出它们的参数(用户空间)
【发布时间】:2011-11-02 02:20:51
【问题描述】:

我想看看在我的用户空间 C99 程序中调用了哪些函数以及调用的顺序。另外,给出了哪些参数。

我可以用 DTrace 做到这一点吗?

例如对于程序

int g(int a, int b) { puts("I'm g"); }
int f(int a, int b) { g(5+a,b);g(8+b,a);}
int main() {f(5,2);f(5,3);}

我想看到一个文本文件:

main(1,{"./a.out"})
 f(5,2);
  g(10,2);
   puts("I'm g");
  g(10,5);
   puts("I'm g");
 f(5,3);
  g(10,3);
   puts("I'm g");
  g(11,5);
   puts("I'm g");

我不想修改我的源代码,而且程序真的很庞大——9000 个函数。

我有所有来源;我有一个编译了调试信息的程序,gdb 能够在回溯中打印函数参数。

DTrace 可以解决这个任务吗?

我的操作系统是 BSD、Linux、MacOS、Solaris 之一。我更喜欢 Linux,但我可以使用任何列出的操作系统。

【问题讨论】:

  • dtrace 无法做到这一点,除非您可以以智能方式放置一些静态探针。

标签: debugging gdb trace dtrace


【解决方案1】:

是的,您可以使用 dtrace 执行此操作。但是你可能永远无法在 linux 上做到这一点。我已经尝试了多个版本的 dtrace 的 linux 端口,但它从来没有完成我想要的。事实上,它曾经引起过 CPU 恐慌。从http://www.brendangregg.com/dtrace.html 下载 dtrace 工具包。然后相应地设置您的 PATH 。然后执行这个:

 dtruss -a yourprogram args...

【讨论】:

  • Jeff,所以这 - dtruss - 是我需要的唯一命令吗?我的问题不仅限于 Linux,所以如果它适用于 Solaris、Mac OS 或 FreeBSD - 这很好。但是 dtruss 的手册页说它只会显示系统调用,而不是来自 ./myprogram 本身的常用函数。
  • dtruss 不只跟踪系统调用吗?
  • 如果您还想跟踪非系统调用,您可以改用fbt 提供程序。要描述每个用户空间函数的进入和退出,请在探测说明符中使用 fbt:::entry, fbt:::return。不过,我不确定您将如何将参数打印到所有内容 - 我建议您查看 dtruss 脚本是如何做到的。如果它枚举所有系统调用并按共享参数类型签名对它们进行分组,您可能会想找到另一种方法。
  • @osgx 不,dtruss 只会像 gerty3000 所说的那样跟踪系统调用,但 fbt 提供者也不会这样做。您别无选择,只能使用 dtrace 探针检测您的代码。在我搜索时,这是我找到的第一个向您展示如何操作的链接:ibm.com/developerworks/aix/library/au-dtraceprobes.html。如果您想在非 dtrace 平台上运行检测代码,那么我建议您抽象宏,以便在 dtrace 平台上使用 dtrace,而在其他平台上使用其他东西。
  • jeff6times7,谢谢。那么,您的回答实际上是“不,没有仪器就无法做到”吗?
【解决方案2】:

以下是使用 DTrace 的方法:

script='pid$target:a.out::entry,pid$target:a.out::return { trace(arg1); }'
dtrace -F -n "$script" -c ./a.out

此命令在 FreeBSD 14.0-CURRENT 上的输出如下:

dtrace: description 'pid$target:a.out::entry,pid$target:a.out::return ' matched 17 probes
I'm g
I'm g
I'm g
I'm g
dtrace: pid 39275 has exited
CPU FUNCTION
  3  -> _start                                      34361917680
  3    -> handle_static_init                    140737488341872
  3    <- handle_static_init                            2108000
  3    -> main                                  140737488341872
  3      -> f                                                 2
  3        -> g                                               2
  3        <- g                                           32767
  3        -> g                                               5
  3        <- g                                           32767
  3      <- f                                                 0
  3      -> f                                                 3
  3        -> g                                               3
  3        <- g                                           32767
  3        -> g                                               5
  3        <- g                                           32767
  3      <- f                                                 0
  3    <- main                                                0
  3    -> __do_global_dtors_aux                 140737488351184
  3    <- __do_global_dtors_aux                               0

烦人的是我还没有找到打印所有函数参数的方法(请参阅How do you print an associative array in DTrace?)。一个 hacky 解决方法是添加 trace(arg2)trace(arg3) 等。问题是对于不存在的参数,将会打印出垃圾。

【讨论】:

    【解决方案3】:

    您的问题极有可能被误导。对于任何重要的程序,打印所有使用其参数执行的函数调用的序列将导致多 MB 甚至多 GB 的输出,您将无法理解(人类无法理解的太多细节明白)。

    也就是说,我不相信你可以通过dtrace 实现你想要的。

    您可以从使用 GCC -finstrument-functions 标志开始,这将很容易让您在进入/退出每个函数时打印函数地址。然后,您可以使用 addr2line 将地址简单地转换为函数名称。这将为您提供您所要求的(参数除外)。

    如果结果证明不是太详细,您可以在 GDB 中的每个函数上设置一个断点(使用rb . 命令),并将continue 命令附加到每个断点。这将导致断点源源不断地被命中(带参数),但执行速度可能会慢至少 100 到 1000 倍。

    【讨论】:

    • 实际上我已经使用 gdb (calltrace) 做了类似的事情。输出非常有帮助。可以过滤多 100MB 文件并与另一个多 100MB 文件进行比较。 stackoverflow.com/questions/311840/…
    猜你喜欢
    • 2016-02-13
    • 2014-05-23
    • 2019-04-16
    • 2015-11-07
    • 2011-05-10
    • 2017-04-16
    • 1970-01-01
    • 2013-10-05
    • 2020-11-13
    相关资源
    最近更新 更多