【问题标题】:Can strace tell me where in my code a syscall is called?strace 可以告诉我在我的代码中调用系统调用的位置吗?
【发布时间】:2019-08-12 20:44:24
【问题描述】:

我正在尝试调试为什么 ngspice 在运行模拟时将烦人的换行符打印到 stderr。我试图在可追溯到 1993 年的 2400 个源文件之一中找到它,但这并不像听起来那么容易!然而,这确实意味着我有一个嵌入了所有调试信息的二进制文件。

我的第一个想法是strace 可以帮助我找到我认为有问题的调用并将其追溯到源代码。例如,我很确定这是有问题的系统调用:

   brk(0x55d1a84e9000)                     = 0x55d1a84e9000                                                                                                                                         
   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, {tv_sec=0, tv_nsec=61462905}) = 0                                                                                                                        
>> write(2, "\n", 1)                       = 1                                                                                                                                                      
   getrusage(RUSAGE_SELF, {ru_utime={tv_sec=0, tv_usec=26269}, ru_stime={tv_sec=0, tv_usec=35243}, ...}) = 0                                                                                        
   openat(AT_FDCWD, "/proc/self/statm", O_RDONLY) = 3                                   

我曾希望,如果我跟踪一个包含调试信息的可执行文件,strace 会显示源代码中的位置,但这并没有自动发生,而且手册有点让人不知所措。

我在手册中找到了一个名为 Tracing 的部分,但找不到任何具体内容。

strace 是否可以,如果可以:如何?如果没有,您还有其他建议吗?

【问题讨论】:

    标签: debugging strace


    【解决方案1】:

    事后看来很明显,但一个非常有用的标志是-k。从手册页:

    -k 打印每次系统调用后被跟踪进程的执行堆栈跟踪。

    这需要一个带有调试信息的二进制文件,它会变得非常嘈杂,但结合一个简单的过滤器(在本例中为-e write),您最终会得到如下所示的内容:

    write(2, "\n", 1
    )                       = 1
     > /lib/x86_64-linux-gnu/libc-2.28.so(__write+0x14) [0xea504]
     > /lib/x86_64-linux-gnu/libc-2.28.so(_IO_file_write+0x2d) [0x7b3bd]
     > /lib/x86_64-linux-gnu/libc-2.28.so(_IO_file_setbuf+0xef) [0x7a75f]
     > /lib/x86_64-linux-gnu/libc-2.28.so(_IO_do_write+0x19) [0x7c509]
     > /lib/x86_64-linux-gnu/libc-2.28.so(_IO_file_overflow+0x103) [0x7c8f3]
     > /home/pipe/src/ngspice/debug/src/ngspice(OUTendPlot+0x1ae) [0xd7643]
     > /home/pipe/src/ngspice/debug/src/ngspice(DCop+0x167) [0x4cd788]
     > /home/pipe/src/ngspice/debug/src/ngspice(CKTdoJob+0x428) [0x4c70dd]
     > /home/pipe/src/ngspice/debug/src/ngspice(if_run+0x3b9) [0xe5d3e]
     > /home/pipe/src/ngspice/debug/src/ngspice(dosim+0x428) [0xe02ee]
    

    在跟踪一些函数内联优化之后,我最终可以找到正确的位置。

    【讨论】:

      【解决方案2】:

      使用 gdb,您可以根据系统调用的参数设置条件系统调用捕获点(类似于您根据函数调用的参数在进入函数时设置条件断点的方式)。然后,当捕获点被触发时,您可以看到调用者在哪里(文件名、行号和源代码)。

      这是 x86_64 的示例。

      $ cat gtest.c
      #include <unistd.h>
      int main()
      {
              write(1, "text\n", 5);
              write(2, "text2\n", 6);
              write(2, "\n", 1);
              return 0;
      }
      
      $ cc gtest.c -g -o gtest
      


      $ gdb -q gtest
      Reading symbols from gtest...done.
      (gdb) list
      1       #include <unistd.h>
      2       int main()
      3       {
      4               write(1, "text\n", 5);
      5               write(2, "text2\n", 6);
      6               write(2, "\n", 1);
      7               return 0;
      8       }
      (gdb) catch syscall write
      Catchpoint 1 (syscall 'write' [1])
      (gdb) condition 1 $rdi == 2 && *(char *)$rsi == '\n' && $rdx == 1
      (gdb) r
      Starting program: /home/mp/gtest
      text
      text2
      
      Catchpoint 1 (call to syscall write), 0x00007fffff13b970 in __write_nocancel ()
          at ../sysdeps/unix/syscall-template.S:84
      84      ../sysdeps/unix/syscall-template.S: No such file or directory.
      (gdb) bt
      #0  0x00007fffff13b970 in __write_nocancel () at ../sysdeps/unix/syscall-template.S:84
      #1  0x00000000080006f6 in main () at gtest.c:6
      (gdb) up
      #1  0x00000000080006f6 in main () at gtest.c:6
      6               write(2, "\n", 1);
      (gdb)
      

      【讨论】:

      • 虽然这并不能从字面上回答我的问题,但它是 IMO 解决我问题的“最佳”解决方案,也是我最终从中获得最大价值的解决方案。在 gdb 中捕获系统调用更强大。
      【解决方案3】:

      我的第一个想法是 strace 可以帮助我找到我认为有问题的调用并将其追溯到源代码。

      您猜对了,但一定是在strace 手册页中忽略了这一点:

      <b>-i </b>Print the instruction pointer at the time of the system call.

      【讨论】:

      • 但是如何使用指令指针追踪到源代码呢?程序一结束,这些信息就会丢失,对吗?由于这是一个命令行工具,程序立即结束。测试时我看到例如[00007fb9b9bba504] write(2, "\n", 1) = 1
      • 啊,我错过了值可能因运行而异。 - 好吧,与此同时,您得到了更多有用的答案。
      猜你喜欢
      • 1970-01-01
      • 2013-03-28
      • 1970-01-01
      • 1970-01-01
      • 2016-05-16
      • 2020-05-18
      • 2011-02-20
      • 2016-02-02
      • 2015-07-30
      相关资源
      最近更新 更多