strace应该如何使用?

298

一位同事曾告诉我,当在Linux上调试失败时的最后选择是使用 strace

我尝试学习这个奇怪工具背后的科学知识,但我不是系统管理员大师,我没有真正的结果。

所以,

  • 它到底是什么,它是用来做什么的?
  • 在哪些情况下应该使用它?
  • 如何理解和处理输出结果?

简而言之,用简单的话,这个东西是如何工作的?


4
strace -p <pid> 命令可以告诉你你的程序正在做什么,比使用GDB更为快捷简便。 - ernesto
2
我个人认为man strace非常易于阅读和有用。(附注:昨天之前我不知道strace,也不是Linux专家) - Alexander Malakhov
1
"strace 是一个系统调用跟踪器,它可以显示你的程序所调用的内核函数(带有它们的参数)结果。" - Pithikos
12个回答

204

Strace概述
strace可以被视为一个轻量级的调试器。它允许程序员/用户快速了解程序与操作系统的交互方式。它通过监控系统调用和信号来实现这一功能。

用途
当您没有源代码或不想真正浏览源代码时,strace非常有用。
此外,如果您不想打开GDB,只是对外部交互感兴趣,那么它对于您自己的代码也很有用。

一个简单的介绍
这是一个使用strace调试进程挂起问题的简单介绍:strace介绍


那么,如果您使用的是strace监视器下面的某些内容呢? - Pacerier
在这种情况下,@Pacerier可以检查ltrace https://dev59.com/FHVC5IYBdhLWcg3w0EsD#52012215 - prosti
1
它非常适用于调试仅存在/主要用于进行有趣系统调用或尝试新的系统调用选项以查看操作系统执行情况的低级程序。它基本上省去了为一次性实验编写日志记录/错误检查代码的麻烦。(或者如果您正在使用汇编语言或其他可能会意外传递错误参数甚至调用号码的语言。)strace比GDB快得多,因为它为您查找errno代码,例如-EFAULT(糟糕,只读缓冲区)或-ENOENT(糟糕,从错误目录运行,相对路径无法工作)。 - Peter Cordes
链接似乎已经失效了,@John Mulder。你介意更新一下吗?我很想看看它。 - alegria
找不到原始页面,但我已将链接更改为另一个介绍性的如何操作页面。 - John Mulder

67

简单来说,strace跟踪程序发出的所有系统调用以及它们的返回代码。这些调用包括文件/套接字操作和其他更为晦涩的操作。

如果您有一些C语言的工作知识,那么使用strace会更加有用, 因为在这里系统调用更准确地表示标准C库调用。

假设您的程序是/usr/local/bin/cough。只需使用:

strace /usr/local/bin/cough <any required argument for cough here>
或者
strace -o <out_file> /usr/local/bin/cough <any required argument for cough here>

写入'out_file'。

所有的strace输出都会被发送到stderr(请注意,大量输出通常需要重定向到文件中)。在最简单的情况下,您的程序将因错误而中止,并且您将能够在strace输出中看到它与操作系统的最后交互。

使用以下命令可以获得更多信息:

man strace

38

strace列出了应用于进程的所有系统调用。如果您不知道什么是系统调用,那么您将无法从中获得太多收益。

但是,如果你的问题涉及到文件、路径或环境变量,那么在有问题的程序上运行strace并将输出重定向到文件中,然后在该文件中grep你的路径/文件/环境字符串,可能会帮助你看到你的程序实际尝试做什么,而不是你期望它做什么。


8
对于非平凡的程序而言,这就像从消防栓里喝水一样难以应付,因此你需要费力地查看结果... - dmckee --- ex-moderator kitten
18
使用命令 strace <prog_name> 追踪一个程序。使用命令 strace -o <out_file> <prog_name> 将输出保存到一个文件中。 - Jestin Joy
8
使用strace命令追踪程序的系统调用,并使用管道将输出重定向到grep命令,过滤出以"open("开头的行。具体命令为:strace prog 2>&1 | grep ^open\( - eisbaw
10
或者只需输入:strace -e open myprog,或者对于所有文件相关的系统调用:**strace -e file myprog**。 - Amit Naidu

19

Strace是一种用于调查生产系统的工具,其中您无法负担在调试器下运行这些程序。特别是,我们在以下两种情况下使用了strace:

  • 程序foo似乎处于死锁状态并且已经失去响应。这可能是gdb的目标;但是,我们并不总是有源代码,有时还要处理脚本语言,这些语言不容易在调试器下运行。在这种情况下,您在已经运行的程序上运行strace,然后您将得到正在进行的系统调用列表。如果您正在调查客户端/服务器应用程序或与数据库交互的应用程序,则此功能特别有用
  • 调查程序为什么很慢。特别是,我们刚刚转移到了一个新的分布式文件系统,系统的新吞吐量非常慢。您可以使用'-T'选项指定strace,该选项将告诉您每个系统调用花费了多少时间。这有助于确定为什么文件系统会导致事情变慢。

有关使用strace进行分析的示例,请参见这个问题的答案。


17

我经常使用strace来调试权限问题。这种技术的步骤如下:

$ strace -e trace=open,stat,read,write gnome-calculator

你希望运行的命令是gnome-calculator


10

strace -tfp PID可以监控PID进程的系统调用,因此我们可以调试/监控我们的进程/程序状态。


8

Strace可以用作调试工具或基本分析器。

作为调试器,您可以查看如何调用、执行和返回给定的系统调用。这非常重要,因为它不仅允许您看到程序失败了,而且还能知道为什么程序失败了。通常这只是糟糕编码的结果,没有捕获程序所有可能的结果。其他时候,这只是文件的硬编码路径。如果没有strace,则必须猜测出错的地方和原因。使用strace,您可以得到系统调用的详细信息,通常仅查看返回值就可以了解很多。

另一个用途是分析。您可以使用它来计时每个系统调用的执行时间,单独或作为聚合。虽然这可能不足以解决您的问题,但至少它将大大缩小潜在嫌疑人列表。如果您看到在单个文件上有很多fopen/close对,那么您可能会在每次循环执行时不必要地打开和关闭文件,而不是在循环之外打开和关闭文件。

Ltrace是strace的近亲,也非常有用。您必须学会区分瓶颈所在。如果总执行时间为8秒,并且您仅花费0.05秒钟处理系统调用,则跟踪程序不会对您有太多好处,问题在于您的代码,这通常是一个逻辑问题,或者程序需要花费那么长时间才能运行。

strace/ltrace最大的问题是阅读它们的输出。如果您不知道如何进行调用,或者至少不知道系统调用/函数的名称,则很难解释其含义。了解函数返回值也可能非常有益,尤其是对于不同的错误代码。虽然解释它们有时很麻烦,但它们确实有时会返回一些有用的信息;我曾经遇到过一种情况,当时我的inode用尽了,但是没有用完所有的空间,因此所有通常的实用程序都没有给我任何警告,我只是无法创建新文件。从strace的输出中读取错误代码让我朝着正确的方向前进。


8

最小可运行示例

如果一个概念不清楚,那么可能有一个更简单的示例你还没有看到,可以解释它。

在这种情况下,这个示例是Linux x86_64汇编无libc的hello world程序:

hello.S

.text
.global _start
_start:
    /* write */
    mov $1, %rax    /* syscall number */
    mov $1, %rdi    /* stdout */
    mov $msg, %rsi  /* buffer */
    mov $len, %rdx  /* buffer len */
    syscall

    /* exit */
    mov $60, %rax   /* syscall number */
    mov $0, %rdi    /* exit status */
    syscall
msg:
    .ascii "hello\n"
len = . - msg

GitHub 的上游

组装并运行:

as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out

输出预期结果:
hello

现在让我们在这个例子上使用strace:

env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log

我们使用:

strace.log 现在包含:

execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6)                  = 6
exit(0)                                 = ?
+++ exited with 0 +++

通过这个简单的例子,输出的每一个字符都是不言自明的:

  • execve 行:显示了 strace 如何执行 hello.out,包括 CLI 参数和环境变量,如 man execve 中所述。

  • write 行:显示了我们所做的写入系统调用。 6 是字符串 "hello\n" 的长度。

    = 6 是系统调用的返回值,根据 man 2 write 中的文档,它是写入的字节数。

  • exit 行:显示了我们所做的退出系统调用。由于程序已经退出,因此没有返回值!

更复杂的例子

当然,strace 的应用是查看复杂程序实际执行的系统调用,以帮助调试/优化您的程序。

值得注意的是,你在 Linux 中遇到的大多数系统调用都有 glibc 包装器,其中许多来自 POSIX
在内部,glibc 包装器使用内联汇编来实现类似于这样的操作:如何通过 syscall 或 sysenter 在内联汇编中调用系统调用? 下一个你应该学习的例子是 POSIX 的 write “hello world”:

main.c

#define _XOPEN_SOURCE 700
#include <unistd.h>

int main(void) {
    char *msg = "hello\n";
    write(1, msg, 6);
    return 0;
}

编译并运行:

gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out

这次,您将看到在 main 之前由 glibc 进行大量系统调用以为 main 设置良好的环境。
这是因为我们现在不再使用独立程序,而是使用更常见的 glibc 程序,它允许 libc 功能。
然后,在最后,strace.log 包含了:
write(1, "hello\n", 6)                  = 6
exit_group(0)                           = ?
+++ exited with 0 +++

所以我们得出结论,write POSIX函数使用的是Linux内核的write系统调用。

我们还观察到return 0导致了一个exit_group调用而不是exit。哈,我不知道这个!这就是为什么strace如此酷炫。man exit_group解释道:

这个系统调用相当于exit(2),除了它不仅终止调用线程,还终止调用进程的所有线程组中的所有线程。

以下是我研究dlopen使用哪个系统调用的另一个示例:https://unix.stackexchange.com/questions/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710

在Ubuntu 16.04、GCC 6.4.0、Linux内核4.4.0上进行测试。


4

Strace是一种工具,可以告诉您的应用程序与操作系统如何交互。

它通过告诉您应用程序使用了哪些OS系统调用以及使用什么参数来调用它们来实现此目的。

因此,例如,您可以看到您的程序尝试打开哪些文件,以及调用是否成功。

您可以使用此工具调试各种问题。例如,如果应用程序说找不到您已安装的库,则strace会告诉您应用程序正在寻找该文件的位置。

这只是冰山一角。


这是非常精确的。 - prosti

4

strace是一个很好的工具,可以帮助您了解程序如何进行各种系统调用(对内核的请求),并报告失败的调用以及与该失败相关联的错误值。并非所有失败都是错误。例如,试图搜索文件的代码可能会收到ENOENT(没有这样的文件或目录)错误,但在代码逻辑中可能是可接受的情况。

使用strace的一个很好的用例是调试临时文件创建期间的竞态条件。例如,通过将进程ID(PID)附加到某些预先确定的字符串来创建文件的程序可能会在多线程场景下遇到问题。[PID + TID(进程ID + 线程ID)或更好的系统调用,例如mkstemp,可以解决此问题]。

它也非常适合调试崩溃。您可能会发现这篇关于strace和调试崩溃的文章对您有用。


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