strace 指令是一個集診斷、調試、統計與一體的工具,我們可以使用 strace 對應用的系統調用和信号傳遞的跟蹤結果來對應用進行分析,以達到解決問題或者是了解應用工作過程的目的。
strace 的最簡單的用法就是執行一個指定的指令,在指定的指令結束之後它也就退出了。在指令執行的過程中,strace 會記錄和解析指令程序的所有系統調用以及這個程序所接收到的所有的信号值。
通常我們調試 PHP 可以通過 PHP 函數 echo、var_dump、print_r 就能解決大多數問題。亦或通過 XDebug 單點調試調試 PHP。
那麼,我們為何需要使用 strace 來調試 PHP 呢?
調試性能問題。檢視系統調用的頻率,找出耗時的程式段(磁盤 IO、網絡 IO)。
檢視程式讀取的配置檔案。以此定位配置檔案加載是否錯誤。
檢視某個 PHP 腳本長時間運作“假死”的情況。
1 磁盤 IO
PHP 性能問題通常出現在磁盤 IO、網絡 IO。而磁盤 IO 通常是檔案加載過多過大,以及日志寫入磁盤有關。通過 strace 指令我們就知道這些對系統磁盤 IO 操作的位置耗時。得知是否出現性能問題需要優化。網絡 IO 通常指連接配接資料庫、調用第三方接口、其他服務連接配接等。如果一個網絡連接配接太久就會造成我們的 PHP-FPM 程序不能得到有效利用,進而不能快速釋放而加重伺服器負載。
現有 fileIo.php 如下類似代碼:
$handle = fopen(__FILE__, 'r');
$content = '';
while ($str = fgets($handle)) {
$content .= $str;
}
fclose($handle);
我們通過 strace 執行它。
$ strace -T php fileIo.php
會輸出如下資訊(部分):
execve("/usr/local/php71/bin/php", ["php", "cli.php", "index/fileIo"], []) = 0 <0.000818>
brk(NULL) = 0x2d69000 <0.000041>
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f64f76bd000 <0.000046>
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) <0.000092>
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 <0.000090>
fstat(3, {st_mode=S_IFREG|0644, st_size=43150, ...}) = 0 <0.000041>
mmap(NULL, 43150, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f64f76b2000 <0.000091>
close(3) = 0 <0.000040>
open("/lib64/libcrypt.so.1", O_RDONLY|O_CLOEXEC) = 3 <0.000240>
根據對輸出的日志,我們可以大緻分為如下幾部分:
1.1指令啟動
execve("/usr/local/php71/bin/php", ["php", "fileIo.php"], []) = 0
1.2 加載系統類庫
日志當中類似以下資訊都在加載 PHP 啟動時要加載的系統類庫。
open("/lib64/libcrypt.so.1", O_RDONLY|O_CLOEXEC) = 3 <0.000240>
open("/lib64/libz.so.1", O_RDONLY|O_CLOEXEC) = 3 <0.000063>
open("/usr/local/lib/libmcrypt.so.4", O_RDONLY|O_CLOEXEC) = 3 <0.000060>
1.3 PHP 指令查找
因為,我們在用 PHP 指令執行腳本的時候,并沒有用絕對路徑。是以,系統在執行的時候,會嘗試去尋找 php 指令所在的位置。如下日志所示:
lstat("/usr/local/erlang/bin/php", 0x7fff6ef8e590) = -1 ENOENT (No such file or directory)
lstat("/usr/local/nginx/sbin/php", 0x7fff6ef8e590) = -1 ENOENT (No such file or directory)
lstat("/usr/local/php71/bin/php", {st_mode=S_IFREG|0755, st_size=40414496, ...}) = 0
是以,我們想優化腳本執行的速度,那麼在運作腳本的時候可以使用絕對路徑。這樣系統就不需要去這些目錄中遍列尋找。
1.4 PHP 配置檔案加載
在日志中有如下資訊:
open("/usr/local/php71/bin/php-cli.ini", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/php71/etc/php-cli.ini", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/php71/bin/php.ini", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/php71/etc/php.ini", O_RDONLY) = 3
可以看到,我們的 PHP 會優先去 PHP 安裝目錄下的 bin 目錄加載 php-cli.ini 配置。不存在,接着去 etc 目錄下加載 php-cli.ini 配置。不存在,接着又去 bin 下讀取 php.ini 檔案。不存在,又去 etc 目錄下找 php.ini 配置。
通過這些加載的順序以及檔案路徑,我們可以清晰知道配置的加載順序,以及加載的配置是否有錯。進而驗證我們的 PHP 是否正确配置。
1.5 加載 PHP 擴充
當配置檔案加載成功之後,PHP 會解析配置檔案。然後,加載配置檔案當中注冊的擴充。如下日志所示:
open("/usr/local/php71/lib/php/extensions/no-debug-non-zts-20160303/xdebug.so", O_RDONLY|O_CLOEXEC) = 3
open("/usr/local/php71/lib/php/extensions/no-debug-non-zts-20160303/redis.so", O_RDONLY|O_CLOEXEC) = 3
open("/usr/local/php71/lib/php/extensions/no-debug-non-zts-20160303/yaf.so", O_RDONLY|O_CLOEXEC) = 3
注意這裡調用系統底層的 C 函數 open。-1 代表檔案或目錄不存在,0 代表标準轉入,1 代表标準輸出,2 代表标準出錯,那麼 3 就代表打開檔案成功之後傳回的句柄 ID。
是以,我們經常會建議 PHP 裡面加載的擴充不要太多。夠用就行。這樣在系統加載的時候 IO 壓力就會小。當然,現在采用有了 PHP-FPM 之後,完全不用擔心這個加載的開銷了。
1.6 TRACE 部分
write(4, "TRACE START [2018-07-20 07:45:47"..., 34) = 34 <0.000046>
write(4, " 0.0026 349704 -> {main"..., 55) = 55 <0.000022>
write(4, " 0.0029 349704 -> fop"..., 77) = 77 <0.000044>
open("/root/fileIo.php", O_RDONLY) = 5 <0.000070>
fstat(5, {st_mode=S_IFREG|0644, st_size=126, ...}) = 0 <0.000011>
lseek(5, 0, SEEK_CUR) = 0 <0.000012>
write(3, "fl=(1) php:internal\nfn=(1) php::"..., 38) = 38 <0.000019>
write(3, "2 473 608\n\n", 11) = 11 <0.000012>
write(4, " 0.0038 350312 -> fge"..., 84) = 84 <0.000017>
read(5, "<?php \n$handle = fopen(__FILE__, "..., 8192) = 126 <0.000017>
write(3, "fl=(1)\nfn=(2) php::fgets\n", 25) = 25 <0.000020>
write(3, "4 137 8224\n\n", 12) = 12 <0.000040>
write(4, " 0.0042 358568 -> fge"..., 84) = 84 <0.000018>
write(3, "fl=(1)\nfn=(2)\n", 14) = 14 <0.000015>
write(3, "4 5 64\n\n", 8) = 8 <0.000011>
write(4, " 0.0044 358632 -> fge"..., 84) = 84 <0.000013>
write(3, "fl=(1)\nfn=(2)\n", 14) = 14 <0.000013>
write(3, "4 1 40\n\n", 8) = 8 <0.000019>
write(4, " 0.0046 358624 -> fge"..., 84) = 84 <0.000016>
write(3, "fl=(1)\nfn=(2)\n", 14) = 14 <0.000013>
write(3, "4 1 64\n\n", 8) = 8 <0.000010>
write(4, " 0.0048 358680 -> fge"..., 84) = 84 <0.000016>
write(3, "fl=(1)\nfn=(2)\n", 14) = 14 <0.000011>
write(3, "4 2 48\n\n", 8) = 8 <0.000018>
write(4, " 0.0050 358712 -> fge"..., 84) = 84 <0.000063>
write(3, "fl=(1)\nfn=(2)\n", 14) = 14 <0.000015>
write(3, "4 2 32\n\n", 8) = 8 <0.000011>
write(4, " 0.0052 358696 -> fge"..., 84) = 84 <0.000014>
write(3, "fl=(1)\nfn=(2)\n", 14) = 14 <0.000013>
write(3, "4 0 48\n\n", 8) = 8 <0.000010>
write(4, " 0.0054 358712 -> fge"..., 84) = 84 <0.000011>
read(5, "", 8192) = 0 <0.000024>
write(3, "fl=(1)\nfn=(2)\n", 14) = 14 <0.000066>
write(3, "4 124 0\n\n", 9) = 9 <0.000015>
write(4, " 0.0058 358664 -> fcl"..., 85) = 85 <0.000012>
close(5) = 0 <0.000014>
write(3, "fl=(1)\nfn=(3) php::fclose\n", 26) = 26 <0.000059>
write(3, "7 64 -8664\n\n", 12) = 12 <0.000014>
write(3, "fl=(2) /root/fileIo.php\nfn=(4) {"..., 58) = 58 <0.000011>
write(3, "1 2352 -168\ncfl=(1)\ncfn=(1)\ncall"..., 375) = 375 <0.000012>
close(2) = 0 <0.000023>
close(1) = 0 <0.000008>
close(0) = 0 <0.000009>
write(4, " 0.0063 184\nTRACE END "..., 57) = 57 <0.000016>
從上面的日志,我們可以發現如下字眼:
...TRACE START...
...TRACE END...
這确實也是我們 PHP 腳本真正邏輯執行的部分。
通過這部分資訊,我們可以看到,我們确實通過系統底層的 open 方法打開了我們的腳本檔案,并且調用了 read 方法讀取腳本中的内容。
注意:
通過 open 函數打開 fileIo.php 腳本時傳回的句柄 ID 的值是 5。是以,對該檔案的讀寫操作在下面全部是與 5 相關。是以, write(3, ...) 與 write(4, ..) 這樣的代碼僅僅與系統底層相關。
2 網絡 IO
在第 1 小節,我們通過快速檢視了檔案 IO 的相關 PHP 執行的調試。本小節,我們主要講網絡 IO 的調試。
2.1 TRACE 部分日志
write(4, "TRACE START [2018-07-20 08:31:43"..., 34) = 34 <0.000034>
write(4, " 0.0055 350160 -> {main"..., 54) = 54 <0.000057>
write(4, " 0.0057 350208 -> Red"..., 68) = 68 <0.000030>
stat("/etc/sysconfig/64bit_strstr_via_64bit_strstr_sse2_unaligned", 0x7ffc8db09af0) = -1 ENOENT (No such file or directory) <0.000029>
write(3, "fl=(1) php:internal\nfn=(1) php::"..., 51) = 51 <0.000032>
write(3, "2 5 0\n\n", 7) = 7 <0.000032>
write(4, " 0.0064 350208 -> Red"..., 79) = 79 <0.000029>
socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP) = 5 <0.000035>
close(5) = 0 <0.000033>
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 5 <0.000030>
fcntl(5, F_GETFL) = 0x2 (flags O_RDWR) <0.000024>
fcntl(5, F_SETFL, O_RDWR|O_NONBLOCK) = 0 <0.000032>
connect(5, {sa_family=AF_INET, sin_port=htons(6379), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress) <0.000128>
poll([{fd=5, events=POLLIN|POLLOUT|POLLERR|POLLHUP}], 1, 60000) = 1 ([{fd=5, revents=POLLOUT}]) <0.000047>
getsockopt(5, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 <0.000043>
fcntl(5, F_SETFL, O_RDWR) = 0 <0.000043>
setsockopt(5, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000044>
write(3, "fl=(1)\nfn=(2) php::Redis->connec"..., 34) = 34 <0.000075>
write(3, "3 1884 656\n\n", 12) = 12 <0.000045>
write(4, " 0.0089 350864 -> Red"..., 67) = 67 <0.000048>
poll([{fd=5, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout) <0.000043>
sendto(5, "*2\r\n$6\r\nSELECT\r\n$1\r\n1\r\n", 23, MSG_DONTWAIT, NULL, 0) = 23 <0.000084>
poll([{fd=5, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 1 ([{fd=5, revents=POLLIN}]) <0.000042>
recvfrom(5, "+", 1, MSG_PEEK, NULL, NULL) = 1 <0.000043>
poll([{fd=5, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 1 ([{fd=5, revents=POLLIN}]) <0.000058>
recvfrom(5, "+OK\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 5 <0.000054>
write(3, "fl=(1)\nfn=(3) php::Redis->select"..., 33) = 33 <0.000052>
write(3, "4 1643 8192\n\n", 13) = 13 <0.000042>
write(4, " 0.0111 359056 -> Red"..., 83) = 83 <0.000049>
poll([{fd=5, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout) <0.000044>
sendto(5, "*3\r\n$5\r\nLPUSH\r\n$8\r\nlist_key\r\n$12"..., 48, MSG_DONTWAIT, NULL, 0) = 48 <0.000058>
poll([{fd=5, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 1 ([{fd=5, revents=POLLIN}]) <0.000044>
recvfrom(5, ":", 1, MSG_PEEK, NULL, NULL) = 1 <0.000043>
poll([{fd=5, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 1 ([{fd=5, revents=POLLIN}]) <0.000043>
recvfrom(5, ":1\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 4 <0.000053>
write(3, "fl=(1)\nfn=(4) php::Redis->lPush\n", 32) = 32 <0.000088>
write(3, "5 1183 0\n\n", 10) = 10 <0.000044>
write(4, " 0.0129 359056 -> Red"..., 70) = 70 <0.000052>
poll([{fd=5, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout) <0.000045>
sendto(5, "*2\r\n$4\r\nLPOP\r\n$8\r\nlist_key\r\n", 28, MSG_DONTWAIT, NULL, 0) = 28 <0.000078>
poll([{fd=5, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 1 ([{fd=5, revents=POLLIN}]) <0.000046>
recvfrom(5, "$", 1, MSG_PEEK, NULL, NULL) = 1 <0.000045>
poll([{fd=5, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 1 ([{fd=5, revents=POLLIN}]) <0.000049>
recvfrom(5, "$12\r\nhello world~\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 19 <0.000039>
write(3, "fl=(1)\nfn=(5) php::Redis->lPop\n", 31) = 31 <0.000082>
write(3, "6 1334 40\n\n", 11) = 11 <0.000043>
write(1, "hello world~", 12) = 12 <0.000046>
write(3, "fl=(2) /root/netIo.php\nfn=(6) {m"..., 58) = 58 <0.000091>
write(3, "1 3368 40\ncfl=(1)\ncfn=(1)\ncalls="..., 199) = 199 <0.000045>
write(4, " 0.0156 357168 -> Redis"..., 58) = 58 <0.000046>
close(5) = 0 <0.000059>
close(2) = 0 <0.000040>
close(1) = 0 <0.000040>
close(0) = 0 <0.000047>
write(4, " 0.0165 160\nTRACE END "..., 57) = 57 <0.000062>
我們發現,其中有一行日志如下:
connect(5, {sa_family=AF_INET, sin_port=htons(6379), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress) <0.000128>
這一行就是我們在連接配接 Redis 伺服器。用時 0.000128 秒。
如果我們在運作的腳本當中懷疑連接配接 Redis 比較慢。那麼就可以通過這樣的方式确診問題是否真正出在這裡。
2.2 向隊列發送資料
sendto(5, "*2\r\n$4\r\nLPOP\r\n$8\r\nlist_key\r\n", 28, MSG_DONTWAIT, NULL, 0) = 28 <0.000078>
Redis lPush 指令是通過系統底層 sendto 方法封裝實作。
2.3 從接收隊列資料
recvfrom(5, "$12\r\nhello world~\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 19 <0.000039>
2.4 輸出資料
write(1, "hello world~", 12) = 12 <0.000046>
我們向指令行輸出的時候就是調用了系統底層的 write 方法。第一個參數 1 代表标準輸出的意思。在 C 語言裡面,0 代表标準輸入,1 代表标準輸出。所謂标準輸出指的就是指令行輸出。
3 Strace 指令解讀
3.1 普通使用
$ strace php xxx.php
通常使用直接在前面加上 strace 即可。就會列印出腳本執行中所有的 TRACE 資訊。
3.2 顯示運作時間
$ strace -T php xxx.php
有時我們除了要檢視 TRACE 資訊。可能還想知道每次系統調用所花費的系統時間。此時可以加 -T 參數顯示花費的時間。
花費的時間機關要秒。能精确表示時間到納紗。1 秒等于 1 百萬納秒。
3.3 TRACE 資訊儲存到檔案
$ strace -T -o strace.log php xxx.php
一個複雜的腳本往往會輸出大量的 TRACE 日志。如果直接輸出到螢幕會給排查問題帶來極大的不友善。這裡我們可以把這些日志儲存到指定的檔案當中。
- o 後面空格之後緊張檔案名即可。
3.4 統計執行的時間
我們可能想快速知曉我們的 PHP 腳本運作花費的總時間,在哪些位置花費時間的比例,以及哪些地方系統底層調用次數。通過這些資料可以大緻推斷出目前腳本可能出現的問題。
如下指令:
$ strace -c php xxx.php
如輸出如下資料:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
29.08 0.001196 7 184 1 mmap
20.54 0.000845 7 115 mprotect
14.64 0.000602 7 83 10 open
6.95 0.000286 4 79 fstat
6.15 0.000253 4 69 read
5.98 0.000246 6 43 munmap
5.42 0.000223 3 80 close
2.02 0.000083 28 3 sendto
1.82 0.000075 75 1 1 connect
1.56 0.000064 3 24 write
1.43 0.000059 5 11 poll
0.92 0.000038 1 43 brk
0.61 0.000025 0 80 rt_sigaction
0.56 0.000023 23 1 ftruncate
0.39 0.000016 2 7 3 access
0.19 0.000008 3 3 recvfrom
0.17 0.000007 1 9 2 lstat
0.15 0.000006 2 3 fcntl
0.12 0.000005 1 6 3 lseek
0.12 0.000005 2 3 socket
0.12 0.000005 5 1 execve
0.12 0.000005 3 2 getdents
0.10 0.000004 0 9 7 stat
0.10 0.000004 1 4 getcwd
0.10 0.000004 1 5 futex
0.07 0.000003 2 2 madvise
0.07 0.000003 3 1 setsockopt
0.07 0.000003 3 1 getsockopt
0.07 0.000003 2 2 umask
0.05 0.000002 1 2 rt_sigprocmask
0.05 0.000002 1 2 2 ioctl
0.05 0.000002 1 2 uname
0.05 0.000002 1 3 getrlimit
0.05 0.000002 1 2 getuid
0.05 0.000002 1 2 1 openat
0.02 0.000001 1 1 geteuid
0.02 0.000001 1 1 arch_prctl
0.00 0.000000 0 3 flock
0.00 0.000000 0 1 getgid
0.00 0.000000 0 1 getegid
0.00 0.000000 0 2 2 statfs
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 1 set_robust_list
0.00 0.000000 0 1 dup3
------ ----------- ----------- --------- --------- ----------------
100.00 0.004113 899 32 total
通過上述輸出的資訊。我們知道了如下資訊:
1)腳本運作花費 0.004113 秒。
2)耗時最高的三個底層系統調用為:mmap、mprotect、open。
3)每個系統調用的次數/錯誤次數。
4)腳本運作過程中調用了哪些系統底層函數。
腳本優化可以根據這些時間名額進行對應的優化。
比如,表格當中 errors 錯誤過多,有可能說明我們的環境嘗試了過多的檔案加載。導緻系統遍列檔案時做了過多錯誤的嘗試。這就是為什麼我們在寫程式的時候推薦使用絕對路徑的原因所在。
其次,如果一個腳本在執行過程中調用了太多次的系統 open 方法,說明我們的腳本有可能存在不合理的磁盤或網絡 IO。通常是磁盤 IO。當出現磁盤 IO 問題的時候,就要引起足夠的重視。
總之,任何異常的時間開銷,我們都要引起足夠的重視。
3.5 TRACE 已有程序
當一個程序已經啟動了,我們想 TRACE 它。也是非常簡單的。
$ strace -p 程序ID
嗯。就是這麼簡單。剩下的跟我們之前上面講過的差不多。一樣一樣的。
3.6 更多可選參數說明
以上 5 小點是我們通常用得最多的參數。當然,strace 的功能遠不僅如此。我們可以通過更多的可選參數發揮它的功能。
-c 統計每一系統調用的所執行的時間,次數和出錯的次數等.
-d 輸出 strace 關于标準錯誤的調試資訊.
-f 跟蹤由 fork 調用所産生的子程序.
-ff 如果提供 -o filename,則所有程序的跟蹤結果輸出到相應的filename.pid中,pid是各程序的程序号.
-F 嘗試跟蹤vfork調用.在-f時,vfork不被跟蹤.
-h 輸出簡要的幫助資訊.
-i 輸出系統調用的入口指針.
-q 禁止輸出關于脫離的消息.
-r 列印出相對時間關于,每一個系統調用.
-t 在輸出中的每一行前加上時間資訊.
-tt 在輸出中的每一行前加上時間資訊,微秒級.
-ttt 微秒級輸出,以秒了表示時間.
-T 顯示每一調用所耗的時間.
-v 輸出所有的系統調用.一些調用關于環境變量,狀态,輸入輸出等調用由于使用頻繁,預設不輸出.
-V 輸出strace的版本資訊.
-x 以十六進制形式輸出非标準字元串
-xx 所有字元串以十六進制形式輸出.
-a column 設定傳回值的輸出位置.預設 為40.
-e expr 指定一個表達式,用來控制如何跟蹤.格式:[qualifier=][!]value1[,value2]...
qualifier隻能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用來限定的符号或數字.預設的 qualifier是 trace.感歎号是否定符号.例如:-eopen等價于 -e trace=open,表示隻跟蹤open調用.而-etrace!=open 表示跟蹤除了open以外的其他調用.有兩個特殊的符号 all 和 none. 注意有些shell使用!來執行曆史記錄裡的指令,是以要使用\\.
-e trace=set 隻跟蹤指定的系統 調用.例如:-e trace=open,close,rean,write表示隻跟蹤這四個系統調用.預設的為set=all.
-e trace=file 隻跟蹤有關檔案操作的系統調用.
-e trace=process 隻跟蹤有關程序控制的系統調用.
-e trace=network 跟蹤與網絡有關的所有系統調用.
-e strace=signal 跟蹤所有與系統信号有關的 系統調用
-e trace=ipc 跟蹤所有與程序通訊有關的系統調用
-e abbrev=set 設定strace輸出的系統調用的結果集.-v 等與 abbrev=none.預設為abbrev=all.
-e raw=set 将指定的系統調用的參數以十六進制顯示.
-e signal=set 指定跟蹤的系統信号.預設為all.如 signal=!SIGIO(或者signal=!io),表示不跟蹤SIGIO信号.
-e read=set 輸出從指定檔案中讀出 的資料.例如: -e read=3,5
-e write=set 輸出寫入到指定檔案中的資料.
-o filename 将strace的輸出寫入檔案filename
-p pid 跟蹤指定的程序pid.
-s strsize 指定輸出的字元串的最大長度.預設為32.檔案名一直全部輸出.
-u username 以username的UID和GID執行被跟蹤的指令
4 總結
總的來說,我們可以使用 strace 指令得知我們的 PHP 腳本運作時調用了底層哪些方法,以及運作的時間等資訊。讓我們可以判斷腳本當中到底出了什麼病況而後進行優化。
但是,strace 有一個不足:調試 PHP 的時候無法獲知每個系統調用對應了 PHP 哪個腳本的哪個方法或哪一行。這樣即使我們知道腳本存在時間上的異常耗時,也無法快速獲知是哪裡出現了問題。
要熟悉掌握 strace,還需要對底層系統方法有一定的了解。