strace 能否告诉我在我的代码中调用了系统调用?

pip*_*ipe 2 debugging strace

我正在尝试调试为什么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                                   
Run Code Online (Sandbox Code Playgroud)

我曾希望,如果我跟踪一个具有调试信息的可执行文件,strace 会显示源代码中的位置,但这并没有自动发生,而且手册有点压倒性。

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

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

pip*_*ipe 5

事后看来很明显,但一个非常有用的标志是-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]
Run Code Online (Sandbox Code Playgroud)

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


Mar*_*ick 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
Run Code Online (Sandbox Code Playgroud)


$ 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)
Run Code Online (Sandbox Code Playgroud)