1 簡介
改進應用程序的性能是一項很是耗時耗力的工做,可是究竟程序中是哪些函數消耗掉了大部分執行時間,這一般都不是很是明顯的。GNU 編譯器工具包所提供了一種剖析工具 GNU profiler(gprof)。gprof 能夠爲 Linux平臺上的程序精確分析性能瓶頸。gprof精確地給出函數被調用的時間和次數,給出函數調用關係。
gprof 用戶手冊網站 http://sourceware.org/binutils/docs-2.17/gprof/index.htmlhtml
2 功能
Gprof 是GNU gnu binutils工具之一,默認狀況下linux系統當中都帶有這個工具。linux
1. 能夠顯示「flat profile」,包括每一個函數的調用次數,每一個函數消耗的處理器時間,git
2. 能夠顯示「Call graph」,包括函數的調用關係,每一個函數調用花費了多少時間。github
3. 能夠顯示「註釋的源代碼」--是程序源代碼的一個複本,標記有程序中每行代碼的執行次數。centos
3 原理
經過在編譯和連接程序的時候(使用 -pg 編譯和連接選項),gcc 在你應用程序的每一個函數中都加入了一個名爲mcount ( or 「_mcount」 , or 「__mcount」 , 依賴於編譯器或操做系統)的函數,也就是說你的應用程序裏的每個函數都會調用mcount, 而mcount 會在內存中保存一張函數調用圖,並經過函數調用堆棧的形式查找子函數和父函數的地址。這張調用圖也保存了全部與函數相關的調用時間,調用次數等等的全部信息。瀏覽器
4 使用流程
1. 在編譯和連接時 加上-pg選項。通常咱們能夠加在 makefile 中。微信
2. 執行編譯的二進制程序。執行參數和方式同之前。多線程
3. 在程序運行目錄下 生成 gmon.out 文件。若是原來有gmon.out 文件,將會被重寫。svg
4. 結束進程。這時 gmon.out 會再次被刷新。函數
5. 用 gprof 工具分析 gmon.out 文件。
5 參數說明
l -b 再也不輸出統計圖表中每一個字段的詳細描述。
l -p 只輸出函數的調用圖(Call graph的那部分信息)。
l -q 只輸出函數的時間消耗列表。
l -e Name 再也不輸出函數Name 及其子函數的調用圖(除非它們有未被限制的其它父函數)。能夠給定多個 -e 標誌。一個 -e 標誌只能指定一個函數。
l -E Name 再也不輸出函數Name 及其子函數的調用圖,此標誌相似於 -e 標誌,但它在總時間和百分比時間的計算中排除了由函數Name 及其子函數所用的時間。
l -f Name 輸出函數Name 及其子函數的調用圖。能夠指定多個 -f 標誌。一個 -f 標誌只能指定一個函數。
l -F Name 輸出函數Name 及其子函數的調用圖,它相似於 -f 標誌,但它在總時間和百分比時間計算中僅使用所打印的例程的時間。能夠指定多個 -F 標誌。一個 -F 標誌只能指定一個函數。-F 標誌覆蓋 -E 標誌。
l -z 顯示使用次數爲零的例程(按照調用計數和累積時間計算)。
通常用法: gprof –b 二進制程序 gmon.out >report.txt
6 報告說明
Gprof 產生的信息解釋:
Call Graph 的字段含義:
程序的累積執行時間只是包括gprof可以監控到的函數。工做在內核態的函數和沒有加-pg編譯的第三方庫函數是沒法被gprof可以監控到的,(如sleep()等)
Gprof 的具體參數能夠 經過 man gprof 查詢。
7 共享庫的支持
對於代碼剖析的支持是由編譯器增長的,所以若是但願從共享庫中得到剖析信息,就須要使用 -pg 來編譯這些庫。提供已經啓用代碼剖析支持而編譯的 C 庫版本(libc_p.a)。
若是須要分析系統函數(如libc庫),能夠用 –lc_p替換-lc。這樣程序會連接libc_p.so或libc_p.a。這很是重要,由於只有這樣才能監控到底層的c庫函數的執行時間,(例如memcpy(),memset(),sprintf()等)。
gcc example1.c –pg -lc_p -o example1
注意要用ldd ./example | grep libc來查看程序連接的是libc.so仍是libc_p.so
8 用戶時間與內核時間
gprof 的最大缺陷:它只能分析應用程序在運行過程當中所消耗掉的用戶時間,沒法獲得程序內核空間的運行時間。一般來講,應用程序在運行時既要花費一些時間來運行用戶代碼,也要花費一些時間來運行 「系統代碼」,例如內核系統調用sleep()。
有一個方法能夠查看應用程序的運行時間組成,在 time 命令下面執行程序。這個命令會顯示一個應用程序的實際運行時間、用戶空間運行時間、內核空間運行時間。
如 time ./program
輸出:
real 2m30.295s
user 0m0.000s
sys 0m0.004s
9 注意事項
1. g++在編譯和連接兩個過程,都要使用-pg選項。
2. 只能使用靜態鏈接libc庫,不然在初始化*.so以前就調用profile代碼會引發「segmentation fault」,解決辦法是編譯時加上-static-libgcc或-static。
3. 若是不用g++而使用ld直接連接程序,要加上連接文件/lib/gcrt0.o,如ld -o myprog /lib/gcrt0.o myprog.o utils.o -lc_p。也多是gcrt1.o
4. 要監控到第三方庫函數的執行時間,第三方庫也必須是添加 –pg 選項編譯的。
5. gprof只能分析應用程序所消耗掉的用戶時間.
6. 程序不能以demon方式運行。不然採集不到時間。(可採集到調用次數)
7. 首先使用 time 來運行程序從而判斷 gprof 是否能產生有用信息是個好方法。
8. 若是 gprof 不適合您的剖析須要,那麼還有其餘一些工具能夠克服 gprof 部分缺陷,包括 OProfile 和 Sysprof。
9. gprof對於代碼大部分是用戶空間的CPU密集型的程序用處明顯。對於大部分時間運行在內核空間或者因爲外部因素(例如操做系統的 I/O 子系統過載)而運行得很是慢的程序難以進行優化。
10. gprof 不支持多線程應用,多線程下只能採集主線程性能數據。緣由是gprof採用ITIMER_PROF信號,在多線程內只有主線程才能響應該信號。可是有一個簡單的方法能夠解決這一問題:http://sam.zoy.org/writings/programming/gprof.html
11. gprof只能在程序正常結束退出以後才能生成報告(gmon.out)。
a) 緣由: gprof經過在atexit()裏註冊了一個函數來產生結果信息,任何非正常退出都不會執行atexit()的動做,因此不會產生gmon.out文件。
b) 程序可從main函數中正常退出,或者經過系統調用exit()函數退出。
10 多線程應用
gprof 不支持多線程應用,多線程下只能採集主線程性能數據。緣由是gprof採用ITIMER_PROF信號,在多線程內只有主線程才能響應該信號。
採用什麼方法纔可以分析全部線程呢?關鍵是可以讓各個線程都響應ITIMER_PROF信號。能夠經過樁子函數來實現,重寫pthread_create函數。
11 數據圖形化
1) gprof ./main > profile.txt 把數據輸出到profile.txt文件中
2) gprof2dot.py profile.txt > profile.dot 生成dot文件
3) dot -Tsvg -o gprof.svg 生成svg文件 咱們就直接用瀏覽器就能夠打開svg看那個函數是熱點了。
gprof2dot.py腳本能夠用githun上fork下來,dot工具,linux能夠直接安裝。centos 命令 yum install graphviz。其餘發行版本的,把安裝命令換一下就好了。
參考連接:http://www.javashuo.com/article/p-pzuujogw-p.html
https://fooyou.github.io/document/2015/07/22/performance-tools-for-linux-cplusplus.html