在php中使用strace、gdb、tcpdump調試工具

[轉] 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

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

  1. 調試性能問題,查看系統調用的頻率,找出耗時的程序段
  2. 查看程序讀取的是哪些文件從而定位好比配置文件加載錯誤問題
  3. 查看某個php腳本長時間運行「假死」狀況
  4. 當程序出現「Out of memory」時被系統發出的SIGKILL信息所kill

另外由於strace拿到的是系統調用相關信息,通常也便是IO操做信息,這個對於排查好比cpu佔用100%問題是無能爲力的。這個時候就可使用GDB工具了。網絡

phptrace
由於strace只能追蹤到系統調用信息,而拿不到php代碼層的調用信息。phptrace擴展就是爲了解決這個問題,phptrace包含兩個功能:1. 打印當前PHP調用棧,2. 實時追蹤PHP調用。這樣就能更方便咱們去查看到咱們須要的信息。phptrace wiki➫tcp

GDB

gdb是一個由GNU開源組織發佈運行在UNIX/LINUX操做系統下功能強大的程序調試工具。使用gdb能夠在程序運行時觀察程序的內部結構和內存的使用狀況,當程序dump時還能夠經過gdb觀察到程序dump前發生了什麼。主要來講gdb具備如下2個功能:

  1. 跟蹤和變動執行計算機程序
  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的數字。

經常使用命令:

  • p:print,打印C變量的值
  • c:continue,繼續運行被停止的程序
  • b:breakpoint,設置斷點,能夠按照函數名設置,如b zif_php_function,也能夠按照源代碼的行數指定斷點,如b src/networker/Server.c:1000
  • t:thread,切換線程,若是進程擁有多個線程,可使用t指令,切換到不一樣的線程
  • ctrl + c:中斷當前正在運行的程序,和c指令配合使用
  • n:next,執行下一行,單步調試
  • info threads:查看運行的全部線程
  • l:list,查看源碼,可使用l 函數名 或者 l 行號
  • bt:backtrace,查看運行時的函數調用棧。當程序出錯後用於查看調用棧信息
  • finish:完成當前函數
  • f:frame,與bt配合使用,能夠切換到函數調用棧的某一層
  • r:run,運行程序

使用.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

php源碼中提供的一個.gdbinit示例➫

其餘gdb經常使用命令能夠參考:

gdb 調試php:

gdb有3種使用方式:

  1. 跟蹤正在運行的PHP程序,使用 「gdb -p 進程ID」 進行附加到進程上
  2. 運行並調試PHP程序,使用 「gdb php -> run server.php」 進行調試
  3. 當PHP程序發生coredump後使用gdb加載core內存鏡像進行調試 gdb php core

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來排查好比這些問題:

  1. 某個php進程佔用cpu 100%問題
  2. 出現core dump問題,好比「Segmentation fault」
  3. php擴展出現錯誤
  4. 死循環問題

一些使用gdb排查問題例子:

tcpdump

即dump the traffic on a network,是一個功能強大的,命令行的包分析器。它能夠將網絡中傳送的數據包徹底截獲下來提供分析。它支持針對網絡層、協議、主機、網絡或端口的過濾,並提供and、or、not等邏輯語句來幫助去掉無用的信息。這樣咱們就能詳細看到網絡通訊的過程,能幫助咱們解決不少網絡問題。好比能夠經過tcpdump知道何時發起的3次握手何時發送FIN包,何時發送RST包。

官方man手冊➫

命令格式爲:
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 ] [ 表達式 ]

參考連接

相關文章
相關標籤/搜索