生產環境多多少少會遇到CPU佔用很高或者卡住的PHP程式,這時怎樣才能知道這個程式在幹啥呢?
一個方法是strace
跟蹤系統呼叫和引數,這樣能大概知道PHP程式在幹啥。要看到具體的PHP函式就需要用PHP擴充套件(xdebug、xhprof)或者用GDB除錯,高階點還可以用DTrace。
上週發現了ruby-stacktrace,它直接讀取ruby程式的記憶體來獲取堆疊資訊,不用GDB和擴充套件,所以效能很好,於是我也照著寫了一個php-stacktrace,算是勉強能用的玩具。
使用
使用比較簡單,下載解壓即可:
$ ./php-stacktrace --help
php-stacktrace 0.1
Sampling profiler for PHP programs
USAGE:
php-stacktrace <COMMAND> <DEBUGINFO> <PID>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
ARGS:
<COMMAND> trace or top or oneshot
<DEBUGINFO> Path to php debuginfo
<PID> PID of the PHP process you want to profile
三個引數都是必填的。
COMMAND可以是trace
、top
、oneshot
。oneshot
只檢視一次就退出,trace
和top
會一直跟蹤,trace
的輸出可以用來生成火焰圖,top
統計函式耗時。
DEBUGINFO是除錯資訊檔案的路徑,Linux通常要獨立安裝debuginfo包,因為不會從elf裡解析路徑,所以要通過這個引數指定,通常的路徑是/usr/lib/debug/.dwz/php....
(在隱藏目錄裡,是個小坑)。
PID就是要跟蹤的PHP程式ID。
順帶一提,只支援非執行緒安全的PHP 7.1。
原理
眾所周知,Zend VM是用C寫的,而各種PHP函式呼叫的資訊都會用C語言的struct/union來表示,所以只要兩步就能拿到堆疊資訊:
- 讀取PHP程式的記憶體
- 在記憶體裡找到函式呼叫堆疊資訊
第一步可以通過ptrace
或process_vm_readv
實現。ptrace
就是偵錯程式所用的方法,它可以暫停PHP程式然後讀取記憶體。process_vm_readv
可以不暫停程式,效能可能更好,但是不可靠,因為PHP還在執行,堆疊資訊不斷變化,很容易讀到錯誤的記憶體。
第二步就需要DWARF除錯資訊了,除錯資訊裡記錄了結構體大小、欄位偏移資訊,通過這些資訊我們就可以準確地去讀記憶體然後做解析。
原理還是很簡單的。
下一步
複製vm_stack
,儘量在一次process_vm_readv
拿到主要的堆疊資訊增加作用域資訊,現在只有函式名完善錯誤處理,現在的程式碼是一團糟- 多學點Rust
2017-10-12更新
新版已經移除了trace
和top
,增加了配置檔案,使用示例可看Readme
本作品採用《CC 協議》,轉載必須註明作者和本文連結