Linux C++程式進行效能分析工具gprof使用入門

工程師WWW發表於2016-04-29

效能分析工具

軟體的效能是軟體質量的重要考察點,不論是線上服務程式還是離執行緒序,甚至是終端應用,效能都是使用者體驗的關鍵。這裡說的效能重大的範疇來講包括了效能和穩定性兩個方面,我們在做軟體測試的時候也是要重點測試版本的效能表現和穩定性的。對於軟體測試過程中發現的效能問題,如何定位有很多的方法。基本的方法可能是開發者對程式碼進行review,或者是使用一些工具對程式碼進行效能分析。常見的效能分析tuning工具有哪些呢?下面兩篇文章做了詳細的總結:

在我工作中主要是關於Linux C++程式程式碼的效能分析,gprof是可用於Linux C++程式碼效能profiling的工具之一,本文主要講講我對gprof的學習和使用過程。


Gprof的基本原理

gprof能夠讓你知道你的程式碼哪些地方是比較耗時的,哪些函式是被呼叫次數很多的,並且能夠讓你一目瞭然的看到函式與函式之間的呼叫關係。gprof是gcc/g++編譯器支援的一種效能診斷工具。只要在編譯時加上-pg選項,編譯器就會在編譯程式時在每個函式的開頭加一個mcount函式呼叫,在每一個函式呼叫之前都會先呼叫這個mcount函式,在mcount中會儲存函式的呼叫關係圖和函式的呼叫時間和被調次數等資訊。最終在程式退出時儲存在gmon.out檔案中,需要注意的是程式必須是正常退出或者通過exit呼叫退出,因為只要在exit()被呼叫時才會觸發程式寫gmon.out檔案。

那麼,gprof的使用方法主要以下三步:

  • 會用-pg引數編譯程式
  • 執行程式,並正常退出
  • 檢視gmon.out檔案

Gprof使用例項

  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. int add(int a, int b)  
  5. {  
  6.         return a+b;  
  7. }  
  8.   
  9. int sub(int a, int b)  
  10. {  
  11.         return a-b;  
  12. }  
  13.   
  14. int call ()  
  15. {  
  16.         std::cout << add(1,2) << std::endl;  
  17.         std::cout << sub(2,4) << std::endl;  
  18. }  
  19.   
  20. int main()  
  21. {  
  22.         int a=1, b=2;  
  23.         cout << add(a,b) << endl;  
  24.         for (int i=0; i<10000; i++)  
  25.                 call();  
  26.         return 0;  
  27. }  

使用g++編譯並加上-pg引數:
[plain] view plain copy
 print?
  1. g++ -o hello hello_grof.cpp -pg -g  

得到可執行檔案,我們可以使用readelf檢視一下它的符號表裡和沒有-pg時編譯的有啥不同:readelf -r ./hello和readelf -r ./hello_normal得出的結果對比。


左邊為有-pg引數編譯的結果。可以看出多了三個函式符號_mcount, __monstartup, _mcleanup都是和gprof相關的呼叫。

使用gdb除錯hello程式,在mcount函式中打斷點也可以看到其呼叫關係,在add函式執行前先呼叫mcount函式:



接下來執行程式./hello,會在當前目錄下生成gmon.out檔案。使用gprof檢視檔案資訊:
[plain] view plain copy
 print?
  1. gprof -b ./hello gmon.out  
得到如下輸出:
[plain] view plain copy
 print?
  1. Flat profile:  
  2.   
  3. Each sample counts as 0.01 seconds.  
  4.  no time accumulated  
  5.   
  6.   %   cumulative   self              self     total             
  7.  time   seconds   seconds    calls  Ts/call  Ts/call  name      
  8.   0.00      0.00     0.00    10001     0.00     0.00  add(int, int)  
  9.   0.00      0.00     0.00    10000     0.00     0.00  sub(int, int)  
  10.   0.00      0.00     0.00    10000     0.00     0.00  call()  
  11.   0.00      0.00     0.00        1     0.00     0.00  global constructors keyed to _Z3addii  
  12.   0.00      0.00     0.00        1     0.00     0.00  __static_initialization_and_destruction_0(int, int)  
  13.   
  14.             Call graph  
  15.   
  16.   
  17. granularity: each sample hit covers 2 byte(s) no time propagated  
  18.   
  19. index % time    self  children    called     name  
  20.                 0.00    0.00       1/10001       main [7]  
  21.                 0.00    0.00   10000/10001       call() [10]  
  22. [8]      0.0    0.00    0.00   10001         add(int, int) [8]  
  23. -----------------------------------------------  
  24.                 0.00    0.00   10000/10000       call() [10]  
  25. [9]      0.0    0.00    0.00   10000         sub(int, int) [9]  
  26. -----------------------------------------------  
  27.                 0.00    0.00   10000/10000       main [7]  
  28. [10]     0.0    0.00    0.00   10000         call() [10]  
  29.                 0.00    0.00   10000/10001       add(int, int) [8]  
  30.                 0.00    0.00   10000/10000       sub(int, int) [9]  
  31. -----------------------------------------------  
  32.                 0.00    0.00       1/1           __do_global_ctors_aux [13]  
  33. [11]     0.0    0.00    0.00       1         global constructors keyed to _Z3addii [11]  
  34.                 0.00    0.00       1/1           __static_initialization_and_destruction_0(int, int) [12]  
  35. -----------------------------------------------  
  36.                 0.00    0.00       1/1           global constructors keyed to _Z3addii [11]  
  37. [12]     0.0    0.00    0.00       1         __static_initialization_and_destruction_0(int, int) [12]  
  38. -----------------------------------------------  
  39.   
  40. Index by function name  
  41.   
  42.   [11] global constructors keyed to _Z3addii (hello_grof.cpp) [9] sub(int, int) [10] call()  
  43.    [8] add(int, int)          [12] __static_initialization_and_destruction_0(int, int) (hello_grof.cpp)  

可以使用執行命令:
[plain] view plain copy
 print?
  1. gprof -b ./hello  gmon.out | gprof2doc.py > ~WWW/hello.dot  

生成dot格式的呼叫關係圖檔案,可以使用windows版的GVEdit for Graphviz軟體檢視呼叫關係圖:


附上一張比較複雜的程式呼叫關係圖:

對於呼叫的關係和呼叫熱點一目瞭然。

Gprof輸出解讀

這部分內容可將gprof -b ./hello中的-b引數去掉,可以顯示欄位的詳細含義描述:

[plain] view plain copy
 print?
  1. 14  %         the percentage of the total running time of the  
  2. 15 time       program used by this function.  
  3. 16  
  4. 17 cumulative a running sum of the number of seconds accounted  
  5. 18  seconds   for by this function and those listed above it.  
  6. 19  
  7. 20  self      the number of seconds accounted for by this  
  8. 21 seconds    function alone.  This is the major sort for this  
  9. 22            listing.  
  10. 23  
  11. 24 calls      the number of times this function was invoked, if  
  12. 25            this function is profiled, else blank.  
  13. 26  
  14. 27  self      the average number of milliseconds spent in this  
  15. 28 ms/call    function per call, if this function is profiled,  
  16. 29        else blank.  
  17. 30  
  18. 31  total     the average number of milliseconds spent in this  
  19. 32 ms/call    function and its descendents per call, if this  
  20. 33        function is profiled, else blank.  
  21. 34  
  22. 35 name       the name of the function.  This is the minor sort  
  23. 36            for this listing. The index shows the location of  
  24. 37        the function in the gprof listing. If the index is  
  25. 38        in parenthesis it shows where it would appear in  
  26. 39        the gprof listing if it were to be printed.  

總結

gprof是常見的效能分析工具,在此羅列一下它的一些不足,也是從網上看的:

  • 1、對多執行緒支援不好,不準確
  • 2、必須退出exit()才行
  • 3、它只能分析應用程式在執行過程中所消耗掉的使用者時間,無法得到程式核心空間的執行時間。對核心態的呼叫分析無能為力。如果程式系統呼叫比率比較大,就不適合。

相關文章