1. 程式人生 > >【linux】Valgrind工具集詳解(十一):Massif(堆分析器)

【linux】Valgrind工具集詳解(十一):Massif(堆分析器)

一、概述

Massif是一個堆分析器。它統計程式使用的堆記憶體大小(由malloc等函式分配的記憶體)。預設情況下不統計程式所使用的所有記憶體,如果想統計所有記憶體,需要使用選項–pages-as-heap=yes。

堆分析可以幫助減少程式使用的記憶體。如果分配的記憶體還沒有釋放並且指標也在,這種情況對於Memcheck(記憶體洩漏檢查器)來說不算錯誤。但是隨著時間記憶體增加,這也算記憶體洩漏,Massif可以幫助識別這些洩漏。

重要的是,Massif不僅會報告程式正在使用多少堆記憶體,還會提供非常詳細的資訊,來指明這些記憶體是由程式中哪部分分配的。

二、使用

0、原始碼
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *x = (int *)malloc(sizeof(int)*10);
    free(x);
    x = (int *)malloc(sizeof(int)*10);
    int *y = (int *)malloc(sizeof(int)*10);
    free(y);
    free(x);
    return 0;
}
1、 編譯

gcc編譯原始碼時,新增 -g 選項,Massif對編譯優化沒有要求。

2、分析

執行命令:valgrind --tool=massif --time-unit=B ./a.out,./a.out是可執行程式,執行完畢後,massif將分析資料儲存在在當前目錄下,檔案格式是massif.out.PID,PID是程序號

3、檢視

使用 ms_print massif.out.PID檢視分析結果,內容如下

--------------------------------------------------------------------------------
Command:            ./a.out
Massif arguments:   --time-unit=B
ms_print arguments: massif.out.5262
--------------------------------------------------------------------------------
     B
  112^                                                ############            
     |                                                #                       
     |                                                #                       
     |                                                #                       
     |                                                #                       
     |                                                #                       
     |                                                #                       
     |                                                #                       
     |                                                #                       
     |                                                #                       
     |            @@@@@@@@@@@@            ::::::::::::#           ::::::::::: 
     |            @                       :           #           :           
     |            @                       :           #           :           
     |            @                       :           #           :           
     |            @                       :           #           :           
     |            @                       :           #           :           
     |            @                       :           #           :           
     |            @                       :           #           :           
     |            @                       :           #           :           
     |            @                       :           #           :           
   0 +----------------------------------------------------------------------->B
     0                                                                     336

Number of snapshots: 9
 Detailed snapshots: [2, 6 (peak)]
--------------------------------------------------------------------------------
  n        time(B)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
  0              0                0                0             0            0
  1             56               56               40            16            0
  2             56               56               40            16            0
71.43% (40B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->71.43% (40B) 0x40058D: main (main.c:6)
--------------------------------------------------------------------------------
  n        time(B)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
  3            112                0                0             0            0
  4            168               56               40            16            0
  5            224              112               80            32            0
  6            224              112               80            32            0
71.43% (80B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->35.71% (40B) 0x4005A7: main (main.c:8)
| 
->35.71% (40B) 0x4005B5: main (main.c:9)
| 
->00.00% (0B) in 1+ places, all below ms_print's threshold (01.00%)
--------------------------------------------------------------------------------
  n        time(B)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
  7            280               56               40            16            0
  8            336                0                0             0            0
4、 說明

<1> 座標圖詳解
座標圖中“:”表示普通快照、“@”表示詳細快照、“#”表示峰值快照;座標圖左下角的“Number of snapshots: 9”是快照總數量、
“Detailed snapshots: [2, 6 (peak)]”是詳細快照列表,peak表示峰值快照。
<2>普通快照詳解

-<a>-------<b>------------ <c>----------<d>---------------<e>-------------<f>--
  n        time(B)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
  0          0                0                0             0            0
  1          56               56               40            16           0

<a>快照編號;
<b>快照採集時間,(B)括號中的B表示時間單位是位元組,在執行Massif分析時,添加了引數–time-unit=B;
<c>總記憶體消耗量;
<d>可用堆記憶體的位元組數,即程式申請記憶體時,指定的數量;
<e>額外堆記憶體的位元組數,包括管理記憶體增加的位元組(預設是8,可以使用–heap-admin選項來重新設定)和為了對齊多出的位元組(通常是8或16,可以使用–alignment選項來重新設定);
<f>棧的大小,預設情況下,棧分析是關閉的,因為它會大大降低Massif的速度。因此,示例中的表示棧大小的列為零(可以使用–stacks=yes選項開啟棧分析 )。

<3>詳細快照詳解
除了基本計數(和普通快照一樣)之外,它還提供了一個分配樹,準確地指出這些堆記憶體是由哪些程式碼分配的:

71.43% (40B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->71.43% (40B) 0x40058D: main (main.c:6)

第一行表示分配的函式;
第二行表示在原始碼中的位置
<4>峰值快照詳解
峰值快照和詳細快照一樣,不再贅述

5、警告

如果程式有分支(有父子程序),需要使用–massif-out-file選項來指定儲存檔名,並且在檔名中新增“%p”,來分別儲存父子程序的分析結果,否則會記錄到一個檔案中,導致ms_print無法讀取。

6、完整記憶體分析

預設情況下,Massif只分析堆記憶體,即函式malloc、 calloc、 realloc、memalign、new、new[]和一些其他類似的函式分配的記憶體。這意味著它不直接分析較低級別的系統呼叫,如 mmap、 mremap、brk。它也不分析程式碼段、資料段和BSS段(約等於全域性區域)的大小。因此Massif分析報告的數字可能遠遠小於top等程式工具所報告的數字。
如果想測量程式使用的所有記憶體,可以使用–pages-as-heap=yes選項,啟用此選項後,Massif通過把mmap和類似系統呼叫函式分配的每個“頁面”都被視為一個不同於常規堆塊的“塊”來分析。這意味著程式碼段、資料段、BSS段和棧記憶體都被測量。
注意:不允許–stacks=yes和–pages-as-heap=yes同時出現。
設定–pages-as-heap=yes後,ms_print的輸出大部分不變。一個區別是每個詳細快照的開頭由:

(heap allocation functions) malloc/new/new[], --alloc-fns, etc.

變成:

(heap allocation functions) malloc/new/new[], --alloc-fns, etc.

三、Massif命令列選項

–heap=<yes|no> [default: yes]
是否啟動對分析,預設是yes(啟動)。

–heap-admin= [default: 8]
設定每個塊的管理位元組數,屬於快照詳解中額外位元組數

–stacks=<yes|no> [default: no]
是否啟動棧記憶體分析,啟動後會減慢Massif,預設是關閉。

–pages-as-heap=<yes|no> [default: no]
是否檢查程式所使用的全部記憶體,即程式碼段、資料段、BSS段和棧記憶體,參見上面的分析。
注意:不允許–stacks=yes和–pages-as-heap=yes同時出現。

–depth= [default: 30]
設定詳細快照中分配樹的最大深度。增加它將使Massif執行得更慢,使用更多記憶體,併產生更大的輸出檔案。

–alloc-fn=
指定封裝了堆分配函式的函式名。
注意:
如果malloc1封裝了函式malloc,並且malloc2又封裝了malloc1,則只指定 --alloc-fn=malloc2將不起作用。還需要指定–alloc-fn=malloc1;
如果是C++,必須完整地寫入過載的函式名,並放到單引號內,如:

--alloc-fn ='operator new(unsigned,std :: nothrow_t const&)'

–ignore-fn=
指定堆分析時,忽略的函式,如malloc、new或–alloc-fn指定的函式等,編寫C ++函式名的規則與–alloc-fn相同。

–threshold=<m.n> [default: 1.0]
設定詳細快照中分配樹是否打印出來程式碼詳細位置的閾值,低於該閾值時,不列印,預設是1.0%,如:

99.76% (10,000B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->79.81% (8,000B) 0x80483C2: g (example.c:5)
| ->39.90% (4,000B) 0x80483E2: f (example.c:11)
| | ->39.90% (4,000B) 0x8048431: main (example.c:23)
| |   
| ->39.90% (4,000B) 0x8048436: main (example.c:25)
|   
->19.95% (2,000B) 0x80483DA: f (example.c:10)
| ->19.95% (2,000B) 0x8048431: main (example.c:23)
|   
->00.00% (0B) in 1+ places, all below ms_print's threshold (01.00%)

–peak-inaccuracy=<m.n> [default: 1.0]
針對峰值快照的選項,當記憶體增加到比前一個峰值至少1.0%(預設值)時,才將這時的記憶體進行詳細的快照,更新為新的峰值。否則記憶體增加一點點就記錄為峰值會影響Massif的效能。

–time-unit=<i|ms|B> [default: i]
設定Massif分析的時間單位。有三種:按照程式執行的指令數、按照時間(毫秒)、按照申請、釋放的位元組數變化。最後一種對於小程式和測試時非常有用,因為它的復現性比較好。

–detailed-freq= [default: 10]
設定詳細快照的頻率。當設定為1(–detailed-freq=1)時,每個快照都被記錄成詳細的。

–max-snapshots= [default: 100]
設定最大快照數。如果設定為N,對於除非常小的程式(程式小到它全部快照數不超過預設值)外,最終的快照數將介於N / 2和N之間。

–massif-out-file= [default: massif.out.%p]
將Massif分析資料寫入指定file 而不是預設輸出檔案 massif.out.。
檔名中可以新增 %p和%q用來加入程序ID和%q後指定環境變數的內容。
比如,有環境變數“HELLO”,可以使用下面的命令格式將程序ID和該環境變數加入到檔名中:

valgrind --tool=massif --massif-out-file=hello.%p.%q{HELLO} ./a.out

如果%q後沒有環境變數,將報錯:valgrind: --massif-out-file: expected ‘{’ after ‘%q’,錯誤的命令格式如下:

valgrind --tool=massif --massif-out-file=hello.%p.%q ./a.out

如果%q後面的環境變數不存在,將報錯:valgrind: --massif-out-file: environment variable HELLO is not set

四、ms_print命令列選項

-h --help
顯示幫助資訊。

–version
顯示版本號。

–threshold=<m.n> [default: 1.0]
與Massif的–threshold選項相同,但在分析後而不是在分析期間使用。

–x=<4…1000> [default: 72]
設定圖表的寬度。

–y=<4…1000> [default: 20]
設定圖表的高度。