一位同事曾经告诉我,当Linux上所有调试都失败时,最后的选择是使用strace。
我试图学习这个奇怪工具背后的科学,但我不是系统管理专家,我没有真正得到结果。
So,
它到底是什么,有什么作用? 如何以及在哪些情况下使用它? 应该如何理解和处理输出?
简而言之,简单地说,这东西是怎么工作的?
一位同事曾经告诉我,当Linux上所有调试都失败时,最后的选择是使用strace。
我试图学习这个奇怪工具背后的科学,但我不是系统管理专家,我没有真正得到结果。
So,
它到底是什么,有什么作用? 如何以及在哪些情况下使用它? 应该如何理解和处理输出?
简而言之,简单地说,这东西是怎么工作的?
当前回答
Strace列出它所应用的进程所执行的所有系统调用。如果您不知道系统调用是什么意思,那么您将无法从中获得很多好处。
然而,如果您的问题涉及到文件、路径或环境值,在有问题的程序上运行strace并将输出重定向到一个文件,然后在该文件中获取path/file/env字符串,这可能有助于您了解程序实际试图做什么,而不是您期望它做什么。
其他回答
Strace可以用作调试工具,也可以用作原语分析器。
As a debugger, you can see how given system calls were called, executed and what they return. This is very important, as it allows you to see not only that a program failed, but WHY a program failed. Usually it's just a result of lousy coding not catching all the possible outcomes of a program. Other times it's just hardcoded paths to files. Without strace you get to guess what went wrong where and how. With strace you get a breakdown of a syscall, usually just looking at a return value tells you a lot.
剖析是另一个用途。您可以使用它来分别计时每个系统调用的执行,或者作为一个聚合。虽然这可能不足以解决您的问题,但至少可以大大缩小潜在嫌疑人的范围。如果您在单个文件上看到大量的fopen/close对,那么您可能在每次执行循环时都不必要地打开和关闭文件,而不是在循环之外打开和关闭它。
Ltrace是strace的近亲,也非常有用。你必须学会区分你的瓶颈在哪里。如果执行的总时间是8秒,而你在系统调用上只花了0.05秒,那么对程序进行分段不会有什么好处,问题出在你的代码中,这通常是一个逻辑问题,或者程序实际上需要花那么长时间来运行。
The biggest problem with strace/ltrace is reading their output. If you don't know how the calls are made, or at least the names of syscalls/functions, it's going to be difficult to decipher the meaning. Knowing what the functions return can also be very beneficial, especially for different error codes. While it's a pain to decipher, they sometimes really return a pearl of knowledge; once I saw a situation where I ran out of inodes, but not out of free space, thus all the usual utilities didn't give me any warning, I just couldn't make a new file. Reading the error code from strace's output pointed me in the right direction.
Strace列出它所应用的进程所执行的所有系统调用。如果您不知道系统调用是什么意思,那么您将无法从中获得很多好处。
然而,如果您的问题涉及到文件、路径或环境值,在有问题的程序上运行strace并将输出重定向到一个文件,然后在该文件中获取path/file/env字符串,这可能有助于您了解程序实际试图做什么,而不是您期望它做什么。
下面是我如何使用strace挖掘网站的一些例子。希望这对你有帮助。
检查时间到第一个字节,如下所示:
time php index.php > timeTrace.txt
看看有多少百分比的动作在做什么。大量的lstat和fstat可能表明是时候清理缓存了:
strace -s 200 -c php index.php > traceLstat.txt
输出trace.txt,以便您可以准确地看到正在进行的调用。
strace -Tt -o Fulltrace.txt php index.php
使用它来检查是否有东西在0.1到0.9秒之间加载:
cat Fulltrace.txt | grep "[<]0.[1-9]" > traceSlowest.txt
查看strace中捕获了哪些丢失的文件或目录。这将输出很多涉及我们系统的东西-唯一相关的位涉及客户的文件:
strace -vv php index.php 2>&1 | sed -n '/= -1/p' > traceFailures.txt
我一直使用strace来调试权限问题。技巧是这样的:
$ strace -e trace=open,stat,read,write gnome-calculator
其中gnome-calculator是您想要运行的命令。
最小可运行示例
如果一个概念不清楚,有一个你没有见过的更简单的例子可以解释它。
在本例中,这个例子是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用于控制环境变量:https://unix.stackexchange.com/questions/48994/how-to-run-a-program-in-a-clean-environment-in-bash -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 +++
在这样一个最小的例子中,输出的每个字符都是不言而喻的:
执行行:显示strace如何执行hello。包括CLI参数和man execve中记录的环境 写行:显示我们所做的写系统调用。6是字符串“hello\n”的长度。 = 6是系统调用的返回值,在man 2 write中记录的是写入的字节数。 退出行:显示我们所做的退出系统调用。没有返回值,因为程序退出了!
更复杂的例子
当然,strace的应用是为了查看复杂程序实际上执行了哪些系统调用,以帮助调试/优化程序。
值得注意的是,您在Linux中可能遇到的大多数系统调用都有glibc包装器,其中许多来自POSIX。
在内部,glibc包装器或多或少像这样使用内联汇编:如何在内联汇编中通过sysenter调用系统调用?
你应该学习的下一个例子是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 +++
因此我们得出结论,写POSIX函数使用,惊喜!, Linux写系统调用。
我们还观察到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中测试。