strace 可以告诉我系统调用在我的代码中的什么位置被调用吗?
Can strace tell me where in my code a syscall is called?
我正在尝试调试为什么 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 是否可行,如果可行:如何实现?如果没有,您还有其他建议吗?
My first idea was that strace could help me locate what I believe is the offending call and trace it back to the source code.
您猜对了,但一定是在 strace
手册页中忽略了这一点:
<b>-i </b>打印系统调用时的指令指针
使用 gdb,您可以根据系统调用的参数设置条件系统调用捕获点(类似于您根据函数调用的参数在函数入口处设置条件断点的方式)。然后,当catchpoint被触发时,可以看到调用者在哪里(文件名、行号、源代码)。
这是 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)
事后看来很明显,但一个非常有用的标志是 -k
。来自手册页:
-k Print the execution stack trace of the traced processes after each system call.
这需要一个带有调试信息的二进制文件,它会变得非常嘈杂,但结合一个简单的过滤器(-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]
在跟踪一些函数内联优化后,我最终可以从中找到正确的位置。
我正在尝试调试为什么 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 是否可行,如果可行:如何实现?如果没有,您还有其他建议吗?
My first idea was that strace could help me locate what I believe is the offending call and trace it back to the source code.
您猜对了,但一定是在 strace
手册页中忽略了这一点:
<b>-i </b>打印系统调用时的指令指针
使用 gdb,您可以根据系统调用的参数设置条件系统调用捕获点(类似于您根据函数调用的参数在函数入口处设置条件断点的方式)。然后,当catchpoint被触发时,可以看到调用者在哪里(文件名、行号、源代码)。
这是 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)
事后看来很明显,但一个非常有用的标志是 -k
。来自手册页:
-k Print the execution stack trace of the traced processes after each system call.
这需要一个带有调试信息的二进制文件,它会变得非常嘈杂,但结合一个简单的过滤器(-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]
在跟踪一些函数内联优化后,我最终可以从中找到正确的位置。