strace应该如何使用?

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

我试图学习这个奇怪工具背后的科学,但我不是系统管理专家,我没有真正得到结果。

所以,

  • 它到底是什么,有什么作用?
  • 如何以及在哪些情况下使用它?
  • 应该如何理解和处理输出?

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

297165 次浏览

strace列出它所应用的进程所做的所有系统调用。如果您不知道系统调用是什么意思,那么您将无法从中获得很多好处。

尽管如此,如果你的问题涉及到文件或路径或环境值,在有问题的程序上运行strace并将输出重定向到一个文件,然后grepping该文件为你的path/file/env字符串可能会帮助你看到你的程序实际上试图做什么,而不是你期望它做什么。

< p > Strace概述
Strace可以看作是一个轻量级调试器。它允许程序员/用户快速发现程序是如何与操作系统交互的。它通过监控系统调用和信号来做到这一点 < p > 使用
当你没有源代码或者不想被打扰去真正浏览它的时候,这很好。
如果您不喜欢打开GDB,而只是对理解外部交互感兴趣,那么对于您自己的代码也很有用 < p > 这是一个很好的介绍
下面是一个使用strace来调试进程挂起的温和介绍

简单地说,strace跟踪程序发出的所有系统调用及其返回码。想想诸如文件/套接字操作和许多更模糊的操作。

如果你有一些C的工作知识,这是最有用的,因为这里的系统调用更准确地代表标准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

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

它通过告诉你应用程序使用什么操作系统调用以及调用它们的参数来做到这一点。

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

您可以使用此工具调试各种问题。例如,如果应用程序说它找不到你知道你已经安装的库,你strace会告诉你应用程序在哪里寻找那个文件。

而这只是冰山一角。

Strace是一种突出的工具,用于研究无法在调试器下运行这些程序的生产系统。具体来说,我们在以下两种情况下使用了strace:

  • 程序foo似乎处于死锁状态,没有响应。这可能是gdb的一个目标;然而,我们并不总是有源代码,或者有时要处理脚本语言,这些语言不能直接在调试器下运行。在本例中,您在一个已经运行的程序上运行strace,将得到正在执行的系统调用的列表。如果您正在研究客户机/服务器应用程序或与数据库交互的应用程序,这将特别有用
  • 调查为什么程序很慢。特别是,我们刚刚转移到一个新的分布式文件系统,系统的新吞吐量非常慢。您可以使用'-T'选项指定strace,该选项将告诉您在每个系统调用中花费了多少时间。这有助于确定文件系统导致速度变慢的原因。

有关使用strace进行分析的示例,请参阅我对这个问题的回答。

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

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

它还适用于调试崩溃。你可能会发现这篇(我的)关于strace和调试崩溃的文章很有用。

Strace可以用作调试工具,也可以用作原语分析器。

作为调试器,您可以看到如何调用、执行给定的系统调用以及它们返回什么。这是非常重要的,因为它不仅可以让你看到程序失败了,而且还可以看到程序失败的原因。通常情况下,这只是由于糟糕的编码没有捕捉到程序的所有可能结果。其他时候,它只是硬编码的文件路径。没有strace,你只能猜哪里出了问题,怎么出的问题。使用strace,您可以获得系统调用的分解,通常只查看返回值就可以了解很多信息。

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

Ltrace是strace的近亲,也非常有用。你必须学会区分你的瓶颈在哪里。如果执行的总时间是8秒,而你在系统调用上只花了0.05秒,那么对程序进行分段不会有什么好处,问题出在你的代码中,这通常是一个逻辑问题,或者程序实际上需要花那么长时间来运行。

strace/ltrace最大的问题是读取它们的输出。如果您不知道调用是如何进行的,或者至少不知道系统调用/函数的名称,那么将很难破译其含义。了解函数返回什么也非常有用,特别是对于不同的错误代码。虽然破译起来很痛苦,但它们有时真的会带来知识的珍珠;一旦我看到一个情况,我用完了inodes,但没有空闲空间,因此所有常用的实用程序都没有给我任何警告,我只是不能创建一个新文件。从strace的输出中读取错误代码为我指明了正确的方向。

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

我一直使用strace来调试权限问题。技巧是这样的:

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

其中gnome-calculator是你想要运行的命令。

我喜欢一些答案,它读strace检查你如何与操作系统交互。

这正是我们所看到的。系统调用。如果你比较straceltrace,区别会更明显。

$>strace -c cd
Desktop  Documents  Downloads  examples.desktop  Music  Pictures  Public  Templates  Videos
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00    0.000000           0         7           read
0.00    0.000000           0         1           write
0.00    0.000000           0        11           close
0.00    0.000000           0        10           fstat
0.00    0.000000           0        17           mmap
0.00    0.000000           0        12           mprotect
0.00    0.000000           0         1           munmap
0.00    0.000000           0         3           brk
0.00    0.000000           0         2           rt_sigaction
0.00    0.000000           0         1           rt_sigprocmask
0.00    0.000000           0         2           ioctl
0.00    0.000000           0         8         8 access
0.00    0.000000           0         1           execve
0.00    0.000000           0         2           getdents
0.00    0.000000           0         2         2 statfs
0.00    0.000000           0         1           arch_prctl
0.00    0.000000           0         1           set_tid_address
0.00    0.000000           0         9           openat
0.00    0.000000           0         1           set_robust_list
0.00    0.000000           0         1           prlimit64
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                    93        10 total

另一方面,有ltrace跟踪函数。

$>ltrace -c cd
Desktop  Documents  Downloads  examples.desktop  Music  Pictures  Public  Templates  Videos
% time     seconds  usecs/call     calls      function
------ ----------- ----------- --------- --------------------
15.52    0.004946         329        15 memcpy
13.34    0.004249          94        45 __ctype_get_mb_cur_max
12.87    0.004099        2049         2 fclose
12.12    0.003861          83        46 strlen
10.96    0.003491         109        32 __errno_location
10.37    0.003303         117        28 readdir
8.41    0.002679         133        20 strcoll
5.62    0.001791         111        16 __overflow
3.24    0.001032         114         9 fwrite_unlocked
1.26    0.000400         100         4 __freading
1.17    0.000372          41         9 getenv
0.70    0.000222         111         2 fflush
0.67    0.000214         107         2 __fpending
0.64    0.000203         101         2 fileno
0.62    0.000196         196         1 closedir
0.43    0.000138         138         1 setlocale
0.36    0.000114         114         1 _setjmp
0.31    0.000098          98         1 realloc
0.25    0.000080          80         1 bindtextdomain
0.21    0.000068          68         1 opendir
0.19    0.000062          62         1 strrchr
0.18    0.000056          56         1 isatty
0.16    0.000051          51         1 ioctl
0.15    0.000047          47         1 getopt_long
0.14    0.000045          45         1 textdomain
0.13    0.000042          42         1 __cxa_atexit
------ ----------- ----------- --------- --------------------
100.00    0.031859                   244 total

虽然我检查了几次手册,我还没有找到名字strace的起源,但它可能是系统调用跟踪,因为这是显而易见的。

关于strace有三个更重要的说明。

注1:这两个函数straceltrace都使用了系统调用ptrace。因此,ptrace系统调用就是strace有效工作的方式。

ptrace()系统调用提供了一种方法,通过该方法,一个进程 “跟踪程序”)可以观察和控制另一个进程的执行 (“被追踪者”),检查和改变被追踪者的记忆和 寄存器。它主要用于实现断点调试 和系统调用跟踪

注2:strace可以使用不同的参数,因为strace可能非常冗长。我喜欢用-c做实验,它就像一个东西的总结。基于-c,你可以选择一个系统调用,比如-e trace=open,在那里你只能看到这个调用。如果您正在检查在您正在跟踪的命令期间将打开哪些文件,这可能会很有趣。 当然,你可以使用grep来达到同样的目的,但请注意,你需要像这样重定向2>&1 | grep etc来理解在发出命令时引用配置文件。< / p > 注3:我发现这一点非常重要。您不局限于特定的体系结构。strace会让你大吃一惊,因为它可以跟踪不同体系结构的二进制文件。 enter image description here < / p >

最小可运行示例

如果一个概念不清楚,有一个你没有见过的更简单的例子可以解释它。

在本例中,这个例子是Linux x86_64程序集独立(无libc) hello world:

你好。年代

.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   /* exit status */
mov $0, %rdi    /* syscall number */
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

我们使用:

  • env -i ASDF=qwer来控制环境变量
  • -s999 -v来显示更完整的日志信息

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,包括在man execve中记录的CLI参数和环境

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

    = 6是系统调用的返回值,如man 2 write中所述,返回值是写入的字节数

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

更复杂的例子

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

值得注意的是,你在Linux中可能遇到的大多数系统调用都有glibc包装器其中很多来自POSIX

在内部,glibc包装器使用内联汇编或多或少像这样:如何调用系统调用通过系统在内联汇编?

你应该学习的下一个例子是POSIX write hello world:

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

这一次,你将看到glibc在main之前进行了一系列系统调用,为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中测试。

下面是我如何使用strace挖掘网站的一些例子。希望这对你有帮助。

检查时间到第一个字节,如下所示:

time php index.php > timeTrace.txt

看看有多少百分比的动作在做什么。大量的lstatfstat可能表明是时候清理缓存了:

strace -s 200 -c php index.php > traceLstat.txt

输出一个trace.txt,这样你就可以确切地看到正在进行的调用。

strace -Tt -o Fulltrace.txt php index.php

使用这个函数检查是否有东西在.1.9的秒内加载:

cat Fulltrace.txt | grep "[<]0.[1-9]" > traceSlowest.txt

查看在strace中捕获了哪些丢失的文件或目录。这将输出很多涉及我们系统的东西-唯一相关的位涉及客户的文件:

strace -vv php index.php 2>&1 | sed -n '/= -1/p' > traceFailures.txt