strace命令 linux下調試神器

man strace:

       strace - trace system calls and signals

DESCRIPTION
In the simplest case strace runs the specified command until it exits. It intercepts and records the system calls which are called by a process and the signals which are
received by a process. The name of each system call, its arguments and its return value are printed on standard error or to the file specified with the -o option.

strace is a useful diagnostic, instructional, and debugging tool. System administrators, diagnosticians and trouble-shooters will find it invaluable for solving problems
with programs for which the source is not readily available since they do not need to be recompiled in order to trace them. Students, hackers and the overly-curious will
find that a great deal can be learned about a system and its system calls by tracing even ordinary programs. And programmers will find that since system calls and signals
are events that happen at the user/kernel interface, a close examination of this boundary is very useful for bug isolation, sanity checking and attempting to capture race
conditions.

Each line in the trace contains the system call name, followed by its arguments in parentheses and its return value. An example from stracing the command ``cat /dev/null''
is:

open("/dev/null", O_RDONLY) = 3

Errors (typically a return value of -1) have the errno symbol and error string appended.

open("/foo/bar", O_RDONLY) = -1 ENOENT (No such file or directory)

Signals are printed as a signal symbol and a signal string. An excerpt from stracing and interrupting the command ``sleep 666'' is:

sigsuspend([] <unfinished ...>
--- SIGINT (Interrupt) ---
+++ killed by SIGINT +++

 

 

五種利用strace查故障的簡單方法

 

什麼是strace?

strace是一個非常簡單的工具,它可以跟蹤系統調用的執行。最簡單的方式,它可以從頭到尾跟蹤binary的執行,然後以一行文本輸出系統調用的名字,參數和返回值。

其實它可以做的更多:

  • 可以對特定的系統調用或者幾組系統調用進行過濾
  • 可以通過統計特定系統調用的調用次數、耗費的時間、成功和失敗的次數來配置(profile)系統調用的使用I
  • 跟蹤發送給進程的信號量
  • 可以通過pid附着(attach)到任何運行的進程

如果你使用的是其它Unix系統,它類似於"truss"。其它更復雜的是Sun的Dtrace.

怎麼使用它

1) 找出程序在startup的時候讀取的哪個config文件?

有沒有嘗過解決爲什麼某些程序不讀去你認爲它應該讀取的config文件的問題?

 

[cpp] view plain copy
 
  1. strace php 2>&1 | grep php.ini  
  2. open("/usr/local/bin/php.ini", O_RDONLY) = -1 ENOENT (No such file or directory)  
  3. open("/usr/local/lib/php.ini", O_RDONLY) = 4  
  4. lstat64("/usr/local/lib/php.ini", {st_mode=S_IFLNK|0777, st_size=27, ...}) = 0  
  5. readlink("/usr/local/lib/php.ini", "/usr/local/Zend/etc/php.ini", 4096) = 27  
  6. lstat64("/usr/local/Zend/etc/php.ini", {st_mode=S_IFREG|0664, st_size=40971, ...}) = 0  
可以看出這個版本的PHP從/usr/local/lib/php.init讀取config文件(但是先嚐試/usr/locl/bin)

 

如果只關心特定的系統調用,有更精緻的方法

[cpp] view plain copy
 
  1. $ strace -e open php 2>&1 | grep php.ini  
  2. open("/usr/local/bin/php.ini", O_RDONLY) = -1 ENOENT (No such file or directory)  
  3. open("/usr/local/lib/php.ini", O_RDONLY) = 4  
相同的方法適用於很多其它類似的問題。比如說,安裝了不同版本的library,不確定實際上加載了哪一個版本。

 

-e expr -- a qualifying expression: option=[!]all or option=[!]val1[,val2]...
options: trace, abbrev, verbose, raw, signal, read, write

 

2) 爲什麼這個程序沒有打開我的文件?

 

是否曾經碰到過一個程序拒絕讀取它沒有權限的文件,但是你發誓原因是它沒有真正找到那個文件?對程序跟蹤open,access調用,注意失敗的情況

[cpp] view plain copy
 
  1. $ strace -e open,access 2>&1 | grep your-filename  

3) 某個進程現在在做什麼?

某個進程突然佔用了很多CPU? 或者某個進程看起來像hanging了?

找到對應的pid,然後

 hang:

懸掛,掛起的意思

就是一個進程被暫時停止執行.

hang


[cpp] view plain copy
 
  1. root@dev:~# strace -p 15427  
  2. Process 15427 attached - interrupt to quit  
  3. futex(0x402f4900, FUTEX_WAIT, 2, NULL   
  4. Process 15427 detached  

 

嗯,這個例子裏面,它在調用futex()的時候掛起了。

"strace -p"非常有用,它減少了很多猜測工作,也不需要重新啓動應用。

 -p pid -- trace process with process id PID, may be repeated

4) 是誰偷走了時間?

 

你可以重新編譯app,打開profiling,以獲取精確的信息。但是通常利用strace附着(attach)一個進程以快速地看一下當前時間花費在哪裏非常有用。可以看下是否90%的CPU用在真正的工作,或者用在其它方面了。

[cpp] view plain copy
 
  1. root@dev:~# strace -c -p 11084  
  2. Process 11084 attached - interrupt to quit  
  3. Process 11084 detached  
  4. % time     seconds  usecs/call     calls    errors syscall  
  5. ------ ----------- ----------- --------- --------- ----------------  
  6.  94.59    0.001014          48        21           select  
  7.   2.89    0.000031           1        21           getppid  
  8.   2.52    0.000027           1        21           time  
  9. ------ ----------- ----------- --------- --------- ----------------  
  10. 100.00    0.001072                    63           total  
  11. root@dev:~#   
 

-c -- count time, calls, and errors for each syscall and report summary
-C -- like -c but also print regular output

 

在執行strace -c -p命令以後,等到你關注的時間到了後,按ctrl-c退出,strace會列出如上的profiling數據。

在這個例子中,程序花了絕大部分時間在等待select()。它在每一個slect()調用這件調用getpid()和time(),這是一種典型的事件循環。

你也可以運行"start to finish",這裏是"ls"

 

[cpp] view plain copy
 
  1. root@dev:~# strace -c >/dev/null ls  
  2. % time     seconds  usecs/call     calls    errors syscall  
  3. ------ ----------- ----------- --------- --------- ----------------  
  4.  23.62    0.000205         103         2           getdents64  
  5.  18.78    0.000163          15        11         1 open  
  6.  15.09    0.000131          19         7           read  
  7.  12.79    0.000111           7        16           old_mmap  
  8.   7.03    0.000061           6        11           close  
  9.   4.84    0.000042          11         4           munmap  
  10.   4.84    0.000042          11         4           mmap2  
  11.   4.03    0.000035           6         6         6 access  
  12.   3.80    0.000033           3        11           fstat64  
  13.   1.38    0.000012           3         4           brk  
  14.   0.92    0.000008           3         3         3 ioctl  
  15.   0.69    0.000006           6         1           uname  
  16.   0.58    0.000005           5         1           set_thread_area  
  17.   0.35    0.000003           3         1           write  
  18.   0.35    0.000003           3         1           rt_sigaction  
  19.   0.35    0.000003           3         1           fcntl64  
  20.   0.23    0.000002           2         1           getrlimit  
  21.   0.23    0.000002           2         1           set_tid_address  
  22.   0.12    0.000001           1         1           rt_sigprocmask  
  23. ------ ----------- ----------- --------- --------- ----------------  
  24. 100.00    0.000868                    87        10 total  
正如你的預期,它耗費了大部分時間在兩次調用來讀取目錄條目上(因爲運行於一個小的目錄上,所有隻有兩次)

 

 

5) 爲什麼 無法連接到服務器?

 

調試進程無法連接到遠端服務器有時候是件非常頭痛的事。DNS會失敗,connect會掛起,server有可能返回一些意料之外的數據。可以使用tcpdump來分析這些情況,它是一個非常棒的工作。但是有時候你strace可以給你更簡單,耿直借的角度,因爲strace只返回你的進程相關的系統調用產生的數據。如果你要從100個連接到統一個數據服務器的運行進程裏面找出一個連接所做的事情,用strace就比tcpdump簡單得多。

下面是跟蹤"nc"連接到www.news.com 80端口的例子

[cpp] view plain copy
 
  1. $ strace -e poll,select,connect,recvfrom,sendto nc www.news.com 80  
  2. sendto(3, "\\24\\0\\0\\0\\26\\0\\1\\3\\255\\373NH\\0\\0\\0\\0\\0\\0\\0\\0", 20, 0, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 20  
  3. connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)  
  4. connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)  
  5. connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("62.30.112.39")}, 28) = 0  
  6. poll([{fd=3, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1  
  7. sendto(3, "\\213\\321\\1\\0\\0\\1\\0\\0\\0\\0\\0\\0\\3www\\4news\\3com\\0\\0\\34\\0\\1", 30, MSG_NOSIGNAL, NULL, 0) = 30  
  8. poll([{fd=3, events=POLLIN, revents=POLLIN}], 1, 5000) = 1  
  9. recvfrom(3, "\\213\\321\\201\\200\\0\\1\\0\\1\\0\\1\\0\\0\\3www\\4news\\3com\\0\\0\\34\\0\\1\\300\\f"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("62.30.112.39")}, [16]) = 153  
  10. connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("62.30.112.39")}, 28) = 0  
  11. poll([{fd=3, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1  
  12. sendto(3, "k\\374\\1\\0\\0\\1\\0\\0\\0\\0\\0\\0\\3www\\4news\\3com\\0\\0\\1\\0\\1", 30, MSG_NOSIGNAL, NULL, 0) = 30  
  13. poll([{fd=3, events=POLLIN, revents=POLLIN}], 1, 5000) = 1  
  14. recvfrom(3, "k\\374\\201\\200\\0\\1\\0\\2\\0\\0\\0\\0\\3www\\4news\\3com\\0\\0\\1\\0\\1\\300\\f"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("62.30.112.39")}, [16]) = 106  
  15. connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("62.30.112.39")}, 28) = 0  
  16. poll([{fd=3, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1  
  17. sendto(3, "\\\\\\2\\1\\0\\0\\1\\0\\0\\0\\0\\0\\0\\3www\\4news\\3com\\0\\0\\1\\0\\1", 30, MSG_NOSIGNAL, NULL, 0) = 30  
  18. poll([{fd=3, events=POLLIN, revents=POLLIN}], 1, 5000) = 1  
  19. recvfrom(3, "\\\\\\2\\201\\200\\0\\1\\0\\2\\0\\0\\0\\0\\3www\\4news\\3com\\0\\0\\1\\0\\1\\300\\f"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("62.30.112.39")}, [16]) = 106  
  20. connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("216.239.122.102")}, 16) = -1 EINPROGRESS (Operation now in progress)  
  21. select(4, NULL, [3], NULL, NULL)        = 1 (out [3])  

發生了什麼?

注意到嘗試連接/var/run/nscd/socket?這意味着nc首先嚐試連接NSCD--the Name Service Cache Daemon--它通常用來基於NIS,YP,LDAP或者類似的目錄協議提供域名查詢。在這裏它失敗了。

然後它連接DNS(DNS是port 53,所以"sin_port=htons(53)")。然後它調用了"sendto()“,發送包含www.news.com的DNS 包。然後讀迴響應。不知爲何,它嘗試了三次,最後一次有細微的卻別,我猜是它www.news.com十一個CNAME(別名),多次請求可能是nc故意的。

最後,它發起一個connect()請求到得到的IP地址,注意到返回值是EINPROGRESS。這意味這connect是非阻塞的,nc希望繼續處理,然後它調用slect(),連接建立後,select返回成功。

添加"read","write"到過濾系統調用列表中,連接時輸入一個字串,可能會得到如下

Notice the connection attempts to /var/run/nscd/socket? They mean nc first tries to connect to NSCD - the Name Service Cache Daemon - which is usually used in setups that rely on NIS, YP, LDAP or similar directory protocols for name lookups. In this case the connects fails.

It then moves on to DNS (DNS is port 53, hence the "sin_port=htons(53)" in the following connect. You can see it then does a "sendto()" call, sending a DNS packet that contains www.news.com. It then reads back a packet. For whatever reason it tries three times, the last with a slightly different request. My best guess why in this case is that www.news.com is a CNAME (an "alias"), and the multiple requests may just be an artifact of how nc deals with that.

Then in the end, it finally issues a connect() to the IP it found. Notice it returns EINPROGRESS. That means the connect was non-blocking - nc wants to go on processing. It then calls select(), which succeeds when the connection was successful.

Try adding "read" and "write" to the list of syscalls given to strace and enter a string when connected, and you'll get something like this:

[cpp] view plain copy
 
  1. read(0, "test\\n", 1024)                 = 5  
  2. write(3, "test\\n", 5)                   = 5  
  3. poll([{fd=3, events=POLLIN, revents=POLLIN}, {fd=0, events=POLLIN}], 2, -1) = 1  
  4. read(3, "  
這表示從標準輸入讀入"test"+換行符,並寫到網絡連接中,然後調用poll等待響應,讀取響應,寫回標準輸出。

一切看起來都正常工作。

 

 

原文地址:http://www.hokstad.com/5-simple-ways-to-troubleshoot-using-strace

 
 
原文出處: 火丁筆記(@火丁筆記)   

早些年,如果你知道有個 strace 命令,就很牛了,而現在大家基本都知道 strace 了,如果你遇到性能問題求助別人,十有八九會建議你用 strace 掛上去看看,不過當你掛上去了,看着滿屏翻滾的字符,卻十有八九看不出個所以然。本文通過一個簡單的案例,向你展示一下在用 strace 診斷問題時的一些套路。

如下真實案例,如有雷同,實屬必然!讓我們看一臺高負載服務器的 top 結果:

top

技巧:運行 top 時,按「1」打開 CPU 列表,按「shift+p」以 CPU 排序。

在本例中大家很容易發現 CPU 主要是被若干個 PHP 進程佔用了,同時 PHP 進程佔用的比較多的內存,不過系統內存尚有結餘,SWAP 也不嚴重,這並不是問題主因。

不過在 CPU 列表中能看到 CPU 主要消耗在內核態「sy」,而不是用戶態「us」,和我們的經驗不符。Linux 操作系統有很多用來跟蹤程序行爲的工具,內核態的函數調用跟蹤用「strace」,用戶態的函數調用跟蹤用「ltrace」,所以這裏我們應該用「strace」:

不過如果直接用 strace 跟蹤某個進程的話,那麼等待你的往往是滿屏翻滾的字符,想從這裏看出問題的癥結並不是一件容易的事情,好在 strace 可以按操作彙總時間:

通過「c」選項用來彙總各個操作的總耗時,運行後的結果大概如下圖所示:

strace -cp

很明顯,我們能看到 CPU 主要被 clone 操作消耗了,還可以單獨跟蹤一下 clone:

通過「T」選項可以獲取操作實際消耗的時間,通過「e」選項可以跟蹤某個操作:

strace -T -e clone -p(查看大圖

很明顯,一個 clone 操作需要幾百毫秒,至於 clone 的含義,參考 man 文檔:

clone() creates a new process, in a manner similar to fork(2). It is actually a library function layered on top of the underlying clone() system call, hereinafter referred to as sys_clone. A description of sys_clone is given towards the end of this page.

Unlike fork(2), these calls allow the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors, and the table of signal handlers. (Note that on this manual page, “calling process” normally corresponds to “parent process”. But see the description of CLONE_PARENT below.)

簡單來說,就是創建一個新進程。那麼在 PHP 裏什麼時候會出現此類系統調用呢?查詢業務代碼看到了 exec 函數,通過如下命令驗證它確實會導致 clone 系統調用:

最後再考大家一個題:如果我們用 strace 跟蹤一個進程,輸出結果很少,是不是說明進程很空閒?其實試試 ltrace,可能會發現別有洞天。記住有內核態和用戶態之分。

 

 

 

strace -f -e trace=read,write -p 17151 -o log #跟蹤進程17151及子進程中read和write系統調用,輸出到log文件.

-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= 
只跟蹤指定的系統 調用.例如:-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= 
設定 strace輸出的系統調用的結果集.-v 等與 abbrev=none.默認爲abbrev=all. 
-e raw= 
將指 定的系統調用的參數以十六進制顯示. 
-e signal= 
指定跟蹤的系統信號.默認爲all.如 signal=!SIGIO(或者signal=!io),表示不跟蹤SIGIO信號. 
-e read= 
輸出從指定文件中讀出 的數據.例如: 
-e read=, 
-e write= 


通用的完整用法:

strace -o output.txt -T -tt -e trace=all -p 28979

上面的含義是 跟蹤28979進程的所有系統調用(-e trace=all),並統計系統調用的花費時間,以及開始時間(並以可視化的時分秒格式顯示),最後將記錄結果存在output.txt文件裏面。

語法

 strace [ -dffhiqrtttTvxx ] [ -acolumn ] [ -eexpr ] ... [ -ofile ] [-ppid ] ... [ -sstrsize ] [ -uusername ] [ -Evar=val ] ... [ -Evar ]... [ command [ arg ... ] ]

 strace -c [ -eexpr ] ... [ -Ooverhead ] [ -Ssortby ] [ command [ arg... ] ]

 

轉自:https://www.cnblogs.com/youxin/p/8837771.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章