[轉] http://www.syyong.com/php/Using-strace-GDB-and-tcpdump-debugging-tools-in-PHP.htmlphp
在php中咱們最常使用調試方式是輸出打印方式,好比經過echo、var_dump輸出信息到終端或者經過fwrite、file_put_contents將信息寫入到文件中。這種普通方式能幫咱們解決絕大部分調試問題。但仍然有些問題是須要藉助其餘工具來分析的,好比死循環,程序執行時間超預期,佔用cpu太高,php內核或者擴展錯誤等場景,這時若是藉助strace、gdb、tcpdump這樣的工具就能很好的去幫助咱們定位問題。html
strace是Linux環境下的一款程序調試工具,用來監察一個應用程序所使用的系統調用及它所接收的系統信息。linux
在Linux中,進程是不能直接去訪問硬件設備(好比讀取磁盤文件,接收網絡數據等等),但能夠將用戶態模式切換至內核態模式,經過系統調用來訪問硬件設備。這時strace就能夠跟蹤到一個進程產生的系統調用,包括參數,返回值,執行消耗的時間,調用次數,成功和失敗的次數。git
好比咱們使用strace來跟蹤cat查看一個文件作了什麼:github
[root@syyong home]$ strace cat index.php execve("/bin/cat", ["cat", "index.php"], [/* 25 vars */]) = 0 brk(0) = 0x21b0000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5fd02fd000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=41783, ...}) = 0 mmap(NULL, 41783, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5fd02f2000 close(3) = 0 open("/lib64/libc.so.6", O_RDONLY) = 3 ... fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0 open("index.php", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0664, st_size=27, ...}) = 0 read(3, "<?php\necho 'hello world';\n\n", 32768) = 27 write(1, "<?php\necho 'hello world';\n\n", 27<?php echo 'hello world'; ) = 27 read(3, "", 32768) = 0 close(3) = 0 close(1) = 0 close(2) = 0 exit_group(0) = ?
[root@syyong home]$ strace -e read cat index.php read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\356\1\0\0\0\0\0"..., 832) = 832 read(3, "<?php\necho 'hello world';\n\n", 32768) = 27 <?php echo 'hello world'; read(3, "", 32768) = 0 +++ exited with 0 +++
[root@syyong home]$ strace -c cat index.php <?php echo "hello world"; % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 0.00 0.000000 0 3 read 0.00 0.000000 0 1 write 0.00 0.000000 0 4 open 0.00 0.000000 0 6 close 0.00 0.000000 0 5 fstat 0.00 0.000000 0 9 mmap 0.00 0.000000 0 3 mprotect 0.00 0.000000 0 1 munmap 0.00 0.000000 0 3 brk 0.00 0.000000 0 1 1 access 0.00 0.000000 0 1 execve 0.00 0.000000 0 1 arch_prctl ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000000 38 1 total
[root@syyong home]$ strace -T cat index.php 2>&1|grep read read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\356\1\0\0\0\0\0"..., 832) = 832 <0.000015> read(3, "<?php\necho 'hello world';\n\n", 32768) = 27 <0.000019> read(3, "", 32768) = 0 <0.000014>
默認返回的結果每一行表明一條系統調用,規則爲「系統調用的函數名及其參數=函數返回值」。也能夠外加一些條件好比:-e指定返回的調用函數,-c對結果進行統計,-T查看絕對耗時,-p經過pid附着(attach)到任何運行的進程等等。shell
strace的使用方法這裏就不作具體介紹了,能夠經過strace --help去詳細瞭解使用方法。express
那麼經過strace拿到了全部程序去調用系統過程所產生的痕跡後,咱們能用來定位哪些問題呢?swoole
另外由於strace拿到的是系統調用相關信息,通常也便是IO操做信息,這個對於排查好比cpu佔用100%問題是無能爲力的。這個時候就可使用GDB工具了。網絡
phptrace
由於strace只能追蹤到系統調用信息,而拿不到php代碼層的調用信息。phptrace擴展就是爲了解決這個問題,phptrace包含兩個功能:1. 打印當前PHP調用棧,2. 實時追蹤PHP調用。這樣就能更方便咱們去查看到咱們須要的信息。phptrace wiki➫tcp
gdb是一個由GNU開源組織發佈運行在UNIX/LINUX操做系統下功能強大的程序調試工具。使用gdb能夠在程序運行時觀察程序的內部結構和內存的使用狀況,當程序dump時還能夠經過gdb觀察到程序dump前發生了什麼。主要來講gdb具備如下2個功能:
由於php語言是c寫的,那麼使用gdb也就能很方便的去調試php代碼。舉例,咱們經過gdb來調試一個簡單的php程序index.php:
// 程序代碼: <?php for ($i = 0; $i < 3; $i ++) { echo $i . PHP_EOL; if ($i == 2) { $j = $i + 1; var_dump($j); } sleep(1); }
gdb開始調試:
[root@syyong home]$ sudo gdb php
(gdb)run index.php ... 0 1 2 int(3) [Inferior 1 (process 577) exited normally]
注:若是mac下使用gdb時報:「...please check gdb is codesigned - see taskgated(8)...」時可參考https://leandre.cn/search/gdb/➫。gdb在調試程序時,若是ulimit打開則會把錯誤信息打印到當前目錄下的core.*文件中。ulimit -c若是爲0則表示沒打開,能夠執行ulimit -c unlimited或者ulimit -c 大於0的數字。
使用.gdbinit腳本:
除了在gdb shell裏輸入命令,也能夠預先編寫好腳本讓gdb執行。當gdb啓動的時候會在當前目錄下查找「.gdbinit」文件並加載,做爲gdb命令進行執行。這樣就能夠不用在命令行中作一些重複的事,好比設定多個斷點等操做。另外在gdb運行時也能夠經過執行「(gdb) source [-s] [-v] filename」來解釋gdb命令腳本文件。一個.gdbinit文件例子:
file index.php set args hello b main b foo r
其餘gdb經常使用命令能夠參考:
gdb有3種使用方式:
php在解釋執行過程當中,zend引擎用executor_globals變量保存了執行過程當中的各類數據,包括執行函數、文件、代碼行等。zend虛擬機是使用C編寫,gdb來打印PHP的調用棧時,實際是打印的虛擬機的執行信息。
使用zbacktrace更簡單的調試:
php源代碼中還提供了zbacktrace這樣的方便的對gdb命令的封裝的工具。zbacktrace是PHP源碼包提供的一個gdb自定義指令,功能與bt指令相似,與bt不一樣的是zbacktrace看到的調用棧是PHP函數調用棧,而不是c函數。zbacktrace能夠直接看到當前執行函數、文件名和行數,簡化了直接使用gdb命令的不少步驟。在php-src➫的根目錄中有一個.gdbinit文件,下載後再gdb shell中輸入:
(gdb) source .gdbinit (gdb) zbacktrace
基於gdb的功能特色,咱們可使用gdb來排查好比這些問題:
一些使用gdb排查問題例子:
即dump the traffic on a network,是一個功能強大的,命令行的包分析器。它能夠將網絡中傳送的數據包徹底截獲下來提供分析。它支持針對網絡層、協議、主機、網絡或端口的過濾,並提供and、or、not等邏輯語句來幫助去掉無用的信息。這樣咱們就能詳細看到網絡通訊的過程,能幫助咱們解決不少網絡問題。好比能夠經過tcpdump知道何時發起的3次握手何時發送FIN包,何時發送RST包。
命令格式爲: tcpdump [-aAbdDefhHIJKlLnNOpqRStuUvxX#] [ -B size ] [ -c count ] [ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ] [ -i interface ] [ -j tstamptype ] [ -M secret ] [ -Q metadata-filter-expression ] [ -r file ] [ -s snaplen ] [ -T type ] [ --version ] [ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z command ] [ -Z user ] [ 表達式 ]