pstack:
pstack命令可顯示每個進程的棧跟蹤。
pstack 命令必須由相應進程的屬主或 root 運行。
可以使用 pstack 來確定進程掛起的位置。
此命令允許使用的唯一選項是要檢查的進程的 PID。
pstack 看活動的進程內的堆棧
用法:
root# pstack PID
gstack:
gstack -打印正在運行的進程的堆棧跟蹤
使用方法:
gstack PID
描述
gstack連接到命令行中pid的活動進程
打印執行堆棧跟蹤。如果ELF符號存在於二進制(usu -)中
如果你沒有運行條帶(1),那么這個例子就會被打印出來
同樣如此。
如果進程是線程組的一部分,那么gstack將打印出一個堆棧
對組中的每個線程進行跟蹤。
https://blog.csdn.net/O4dC8OjO7ZL6/article/details/78954755
---------------------
思路分析
我們知道gdb的bt(backtrace)可以打印函數調用棧,但需要手動敲命令執行,不能批量多次運行,似乎不太方便。有沒有更好的工具和方法搞定這個需求呢?有的,gstack就是一款用於方便查看函數調用棧的工具。gstack的用途是“print a stack trace of a running process”,即打印一個正在運行的進程的函數調用棧。下面以一個正在運行的redis-server進程為例,執行gstack `pidof redis-server`即可看到該進程當前正在運行的3個線程各自的函數調用棧。這其實與gdb bt看到的差不多,而且我們的目標是只需要函數名,而不需要地址信息,那么gstack有沒有什么參數可以去掉每行的地址,以精簡打印呢?
如下所示,gstack竟然沒有幫助,這不像是一個正常的程序啊。用file `which gstack`查看,果然,它只是一個腳本,並不是一個正常的程序。
查看/usr/bin/gstack腳本源代碼,發現它其實只是包裝了gdb bt,並用sed對gdb bt的輸出結果做了過濾而已。如下給出gstack腳本源代碼的解讀,該腳本分為五部分:
第一部分:檢查是否提供一個入參,如果入參數量不是1,則打印用法提示,並退出腳本。
第二部分:檢查入參必須是一個當前正在運行的程序的PID,如果不是,則退出腳本。
第三部分:判斷內核是否支持gdb打印所有線程函數棧,如果不支持,則后續會將“bt”命令輸入gdb中;如果支持,則后續會將“thread apply all bt”命令輸入gdb中。
第四部分:執行gdb,通過“gdb [options] [executable-file] [process-id]”方式附着到指定PID的進程上,通過<<EOF方式為gdb傳入多個命令,並將執行輸出的結果通過管道“|”傳給后續的sed命令。
第五部分:用sed去掉gdb輸出的無效行,只提取含有線程信息、函數信息的行。
通過上述對gstack腳本源代碼的分析可知,gstack只是gdb bt的簡單封裝,與我們的目標還有一定差距。看來需要自己編寫一些擴展腳本或程序,才能進一步達成目標。
首先,需要編寫一個腳本,重復運行多次gstack,采集目標程序足夠多次函數調用棧;其次,需要進一步凈化數據,比如函數地址信息就需要過濾掉;還有,需要歸並出不同的函數調用棧,找到不同的函數調用鏈,因為gstack輸出的函數棧是用Thread行分隔的,可以編寫一個程序來解析Thread行,將每個Thread塊(多行)放到哈希桶中排重(即,排除重復項),從而得到唯一不同的函數調用鏈。
三.擴展編程
首先,編寫一個makefile腳本,用shell for循環不斷調用gstack,將輸出結果追加到臨時文本文件中。
仍以redis-server為例,執行 make gstack_log PID=`pidof redis-server` NN=5,即可對redis-server連續運行5次gstack,並將結果保存到一個臨時文件tmp_gstack_1353.txt中。在正式采集時,可以將NN設置為很大,比如NN=2000次,以采集到足夠多的不同的函數調用棧信息。
然后,查看一下輸出的臨時文件的內容,即多次gstack輸出結果的羅列。下一步需要將每個Thread行所分隔的塊(多行),如塊1、塊2、塊3、塊4、、、進行凈化和排重。
編寫一個Node.js小程序gstack_data_format.js,用於對gstack輸出結果凈化並排重。程序讀入gstack結果文件(如:tmp_gstack_1353.txt),一行一行地讀入並累加到一個字符串變量中,遇到Thread行則停止累加,並將該字符串作為KEY添加到一個HASH桶中,因為HASH KEY天然不會重復,利用這個特點進行排重;遇到Thread行后,清空該字符串變量,重新開始累加;依次往復,直到讀完整個文件。程序基本流程如下,具體源代碼請見本文附錄。
如下給出gstack_data_format.js的運行效果。該gstack結果文件為1186行,采集到237個函數棧,進行凈化、排重后,得到2個唯一不同的函數調用棧。