分区:堆分析器
目錄
9.1。概觀9.2。使用Massif和ms_print要使用此工具,必須--tool=massif在Valgrind命令行上指定?。
9.1。概觀
Massif是一個堆分析器。它測量程序使用多少堆內存。這包括有用空間和分配用于記錄和排列目的的額外字節。它也可以測量程序堆棧的大小,但默認情況下不會這樣做。
堆分析可以幫助您減少程序使用的內存量。在具有虛擬內存的現代機器上,它具有以下優點:
-
它可以加快您的程序 - 一個較小的程序將更好地與您的機器的緩存交互,并避免分頁。
-
如果您的程序使用大量的內存,它將減少耗盡機器交換空間的機會。
此外,傳統的檢漏器(如Memcheck)沒有檢測到某些空間泄漏。這是因為內存沒有實際損失 - 指針仍然存在,但它并沒有被使用。有這樣的漏洞的程序可能會不必要地增加隨時間推移使用的內存量。Massif可以幫助識別這些泄漏。
重要的是,Massif不僅告訴您程序使用多少堆內存,還提供了非常詳細的信息,指出程序的哪些部分負責分配堆內存。
9.2。使用Massif和ms_print
首先,對于其他Valgrind工具,您應該使用調試信息(-g選項)編譯。編譯程序的優化級別并不重要,因為這不太可能影響堆內存使用。
然后,您需要運行Massif來收集分析信息,然后運行ms_print以可讀的方式呈現。
9.2.1。示例程序
一個例子將使事情清晰。考慮在堆上分配多個不同塊的以下C程序(用行號注釋)。
1 #include <stdlib.h>23 void g(void)4 {5 malloc(4000);6}78 void f(void)9 { 10 malloc(2000); 11 g(); 12} 13 14 int main(void) 15 { 16 int i; 17 int * a [10]; 18 19(i = 0; i <10; i ++){ 20 a [i] = malloc(1000); 21} 22 23 f(); 24 25 g(); 26 27(i = 0; i <10; i ++){ 28(a [i]); 29} 三十 31返回0; 32}9.2.2。運行區塊
要收集有關該程序的堆分析信息?prog,請鍵入:
valgrind --tool = massif prog程序將執行(緩慢)。完成后,Valgrind的評論中沒有匯總統計資料;?所有Massif的分析數據都將寫入文件。默認情況下,此文件被調用?massif.out.<pid>,<pid>進程ID?在哪里?,盡管可以使用該--massif-out-file選項更改此文件名。
9.2.3。運行ms_print
要以易于閱讀的形式查看Massif收集的信息,請使用ms_print。如果輸出文件的名稱是?massif.out.12345,請鍵入:
ms_print massif.out.12345ms_print將產生(a)顯示程序執行中的內存消耗的圖,(b)程序中各個點的負責分配位置的詳細信息,包括峰值內存分配點。使用單獨的腳本來呈現結果是故意的:它將數據收集與其表示分離,意味著將來可以添加新的呈現數據的方法。
9.2.4。輸出序言
在Massif下運行此程序之后,ms_print的輸出的第一部分包含一個前導碼,它只是說明了程序,Massif和ms_print是如何被調用的:
-------------------------------------------------- ------------------------------ 命令:示例 Massif參數:(無) ms_print參數:massif.out.12797 -------------------------------------------------- ------------------------------9.2.5。輸出圖
下一部分是顯示程序執行時內存消耗如何發生的圖形:
KB 19.63 ^#| #| #| #| #| #| #| #| #| #| #| #| #| #| #| #| #| :#| :#| :#0 + ------------------------------------------------ -----------------------> ki 0 113.4快照數:25詳細的快照:[9,14(peak),24]為什么圖形的大部分都是空的,最后只有幾個酒吧?默認情況下,Massif使用“執行指令”作為時間單位。對于諸如示例的非常短期的程序,大多數執行的指令涉及程序的加載和動態鏈接。的執行main(并因此堆分配)只發生在最后。對于這樣的短期運行程序,我們可以使用該--time-unit=B選項來指定我們希望時間單位是堆和堆棧上分配/取消分配的字節數。
如果我們使用此選項重新運行Massif下的程序,然后重新運行ms_print,我們可以獲得更有用的圖形:
19.63 ^ ### | # | #:: | #:::: | :::::::::#::: :: | :#:::: :: | :#::::: ::: | :#:::::: :: | :::::::::::#::::::: ::: | ::#:::::::: :: | ::::::#::::::::: ::| @@@::#:::::::::@| :: @:::#:::::::::@| :::: @:::#:::::::::@| ::::@:::#:::::::::@| :::::@:::#:::::::::@| :::::::@:::#:::::::::@| :::::::@:::#:::::::::@| :::::::::@:::#::::::::@| :::::::::@:::#::::::::@0 + ------------------------------------------------ -----------------------> KB 0 29.48快照數:25詳細的快照:[9,14(peak),24]可以使用ms_print?--x和--yoptions?來更改圖形的大小?。每個垂直條表示一個快照,即在某個時間點的內存使用量的測量。如果下一張快照超過一列,則從快照的頂部繪制到下一個快照列之前的水平線字符。底部的文字顯示,這個程序采用了25個快照,這是每個堆分配/取消分配一個加上幾個附加組件。通過為每個堆分配/取消分配拍攝快照,Massif開始,但是隨著程序運行時間的延長,它不需要快照。當程序繼續時,它也會丟棄較舊的快照;?當達到最大快照數(默認為100)?--max-snapshots雖然可以隨選擇而變化)其中一半被刪除。這意味著始終保持合理數量的快照。
大多數快照是正常的,只記錄基本信息。正常快照由圖形中由“:”字符組成的條形圖表示。
一些快照詳細。記錄這些快照的發生地點的信息,我們將盡快看到。詳細的快照在圖中由“@”字符組成的條形表示。底部的文本顯示為此程序拍攝了3個詳細快照(快照9,14和24)。默認情況下,每10個快照都是詳細的,雖然可以通過該--detailed-freq選項進行更改?。
最后,最多有一個峰值快照。峰值快照是一個詳細的快照,并記錄內存消耗最大的點。峰值快照由圖形中由'#'字符組成的條形表示。底部的文字顯示,快照14是高峰。
高原發生時間的確定可能是錯誤的,原因有兩個。
-
峰值快照僅在釋放發生之后才被采用。這避免了許多不必要的峰值快照錄制(想象一下,如果您的程序連續分配了大量的堆塊,每次都會創建一個新的峰值)會發生什么。但這意味著如果您的程序不會釋放任何塊,則不會記錄高峰。這也意味著如果您的程序解除分配塊,但后來分配到較高的峰值,而不會隨后取消分配,則報告的峰值將太低。
-
即使有這種行為,準確記錄峰值也很慢。因此,默認情況下,Massif記錄一個峰值,其大小在真正峰值的大小的1%之內。峰值測量中的這種不準確度可以通過--peak-inaccuracy選項進行更改。
以下圖表來自KDE Web瀏覽器Konqueror的執行。它顯示了更大程序的圖形。
MB 3.952 ^# | @#:| :@@#:| @@ :::: @@#:| @ :::@@#::| @@@ :::@@#::| @@:@@@ :::@@#::| ::: @:@@@ :::@@#::| ::@:@@@ :::@@#::| :@::@:@@@ :::@@#::| @@:@::@:@@@ :::@@#:::| ::: :: @@:@::@:@@@ :::@@#:::| :@@:::::: :::: @@@ ::: @@:@::@:@@@ :::@@#:::| :::: @@:::: ::::::: @ ::: @@:@::@:@@@ :::@@#::::| @::: @@:::: ::::::: @ ::: @@:@::@:@@@ :::@@#:::| @::: @@:::: ::::::: @ ::: @@:@::@:@@@ :::@@#:::| @::: @@ :::::: ::::::: @ ::: @@:@::@:@@@ :: @@#:::| :: @@@ :: :: @@ :: ::: ::::::: @ ::: @@:@::@:@@@ :: @@#:::| ::::: @ @::: @@ :: ::: ::::::: @ ::: @@:@::@:@@@ :: @@#:::| @@ ::::: @ @::: @@ :: ::: ::::::: @ ::: @@:@::@:@@@ :: @@#:::0 + ------------------------------------------------ ----------------------->彌0 626.4快照數:63詳細的快照:[3,4,10,11,15,16,29,33,34,36,39,41,42,43,44,49,50,51,53,55,56,57(peak)]請注意,較大的單位是KB,MB,GB等。通常的內存測量是基于1024的乘數,而不是1000的標準SI乘數。嚴格來說,它們應該寫為KiB,MiB ,GiB等
9.2.6。快照詳情
回到我們的例子,圖表后面是每個快照的詳細信息。前九個快照是正常的,因此每個快照只記錄少量信息:
-------------------------------------------------- ------------------------------n時間(B)總計(B)有用堆(B)堆棧(B)堆棧(B) -------------------------------------------------- ------------------------------0 0 0 0 0 01 1,008 1,008 1,000 8 02 2,016 2,016 2,000 16 03 3,024 3,024 3,000 24 04 4,032 4,032 4,000 32 05 5,040 5,040 5,000 40 06 6,048 6,048 6,000 48 07 7,056 7,056 7,000 56 08 8,064 8,064 8,000 64 0每個正常的快照記錄幾件事情。
-
其數量。
-
被采取的時間?在這種情況下,由于使用的時間單位是字節?--time-unit=B。
-
那時的總內存消耗。
-
在該點分配的有用堆字節數。這反映了程序要求的字節數。
-
在該點分配的額外堆字節數。這反映了分配的字節數超出了程序所要求的數量。有兩個額外的堆字節來源。
首先,每個堆塊都有與之關聯的管理字節。管理字節的確切數量取決于分配器的詳細信息。默認情況下,Massif假定每個塊為8個字節,從示例中可以看出,但是可以通過該--heap-admin選項更改此數字?。
第二,分配器通常將要求更大數量的字節數通常為8或16。這是確保塊內的元素適當對齊所必需的。如果要求N個字節,則Massif將N替換為該--alignment選項指定的值的最接近的倍數?。
-
堆棧的大小。默認情況下,堆棧分析是關閉的,因為它會大大減慢Massif。因此,示例中堆棧列為零。可以使用--stacks=yes選項打開堆棧分析?。
下一個快照是詳細的。除了基本的計數之外,它給出了一個分配樹,它明確指出哪些代碼段負責分配堆內存:
9 9,072 9,072 9,000 72 0 99.21%(9,000B)(堆分配函數)malloc / new / new [],--alloc-fns等 - > 99.21%(9,000B)0x804841A:main(example.c:20)分配樹可以從上到下讀取。第一行表示所有堆分配函數,如malloc?C ++?new。所有堆分配通過這些功能,因此所有9,000個有用字節(占所有分配字節的99.21%)通過它們。但是怎么malloc和新的叫?在這一點上,到目前為止,每個分配都是由于內部main的第20行,因此是樹中的第二行。的?->指示主(第20行)調用?malloc。
接下來我們來看看隨后的輸出結果如何:
-------------------------------------------------- ------------------------------n時間(B)總計(B)有用堆(B)堆棧(B)堆棧(B) -------------------------------------------------- ------------------------------10 10,080 10,080 10,000 80 011 12,088 12,088 12,000 88 012 16,096 16,096 16,000 96 013 20,104 20,104 20,000 104 014 20,104 20,104 20,000 104 0 99.48%(20,000B)(堆分配函數)malloc / new / new [],--alloc-fns等 - > 49.74%(10,000B)0x804841A:main(example.c:20) | - > 39.79%(8,000B)0x80483C2:g(example.c:5) | - > 19.90%(4,000B)0x80483E2:f(example.c:11) | | - > 19.90%(4,000B)0x8048431:main(example.c:23) | | | - > 19.90%(4,000B)0x8048436:main(example.c:25) | - > 09.95%(2,000B)0x80483DA:f(example.c:10)- > 09.95%(2,000B)0x8048431:main(example.c:23)前四個快照類似于之前的快照。但是,達到了全球分配峰值,并提供了詳細的快照(數字14)。其分配樹顯示,已分配了20,000B的有用堆內存,線和箭頭表示這是來自三個不同的代碼位置:第20行,負責10,000B(49.74%);?第5行,負責8,000B(39.79%);?第10行,負責2000B(9.95%)。
然后,我們可以在分配樹中進一步向下鉆取。例如,在第5行要求的8,000B中,有一半來自第11行的呼叫,一半是由于第25行的呼叫。
簡而言之,Massif將程序中每個單個分配點的堆棧跟蹤整合到單個樹中,這樣可以在特定的時間點給出一個完整的圖片,說明如何以及為什么所有堆內存被分配。
請注意,樹條目不對應于功能,而是對應到各個代碼位置。例如,如果函數A?調用malloc和函數B調用?A兩次,一次在第10行,一次在第11行,則兩次調用將導致樹中的兩個不同的堆棧跟蹤。相比之下,如果來自第15行的B呼叫A(例如由于循環)重復,那么這些呼叫中的每一個將由樹中相同的堆棧跟蹤來表示。
還請注意,示例中的每個具有子節點的樹條目都滿足不變量:條目的大小等于其子項大小的總和。例如,第一個條目的大小為20,000B,其子級的大小為10,000B,8000B和2,000B。一般來說,這個不變量幾乎總是成立。然而,在極少的情況下,堆棧跟蹤可能會發生錯誤,在這種情況下,堆棧跟蹤可以是另一個堆棧跟蹤的子跟蹤。這意味著樹中的某些條目可能不滿足不變量 - 條目的大小將大于其子項大小的總和。這不是一個大問題,但可能會使結果混淆。有時可以檢測到這種情況;?如果是這樣,它會發出警告:
警告:檢測到錯誤的堆棧跟蹤。在馬西的產量,條目的子條目的大小可能不總結對于條目的大小,通常它們的大小。但是,Massif不會發現并警告每一個這樣的事件。幸運的是,畸形堆棧跟蹤在實踐中很少見。
現在回到ms_print的輸出,最后一部分是類似的:
-------------------------------------------------- ------------------------------n時間(B)總計(B)有用堆(B)堆棧(B)堆棧(B) -------------------------------------------------- ------------------------------15 21,112 19,096 19,000 96 016 22,120 18,088 18,000 88 017 23,128 17,080 17,000 80 018 24,136 16,072 16,000 72 019 25,144 15,064 15,000 64 020 26,152 14,056 14,000 56 021 27,160 13,048 13,000 48 022 28,168 12,040 12,000 40 023 29,176 11,032 11,000 32 024 30,184 10,024 10,000 24 0 99.76%(10,000B)(堆分配函數)malloc / new / new [],--alloc-fns等 - > 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)在1+地方,全部低于ms_print的門檻(01.00%)最終的詳細快照顯示堆如何看待終止。00.00%條目表示分配內存然后被釋放的代碼位置(在這種情況下為第20行,第28行的內存被釋放)。但是,此條目沒有提供代碼位置詳細信息;?默認情況下,Massif僅記錄負責超過1%有用內存字節的代碼位置的詳細信息,ms_print同樣僅打印負責超過1%的代碼位置的詳細信息。不符合該閾值的條目進行匯總。這避免了大量不重要條目填寫輸出。可以使用--thresholdMassif和ms_print支持的選項來更改?閾值。
9.2.7。分岔程序
如果您的程序分叉,該子將繼承所有為父級收集的分析數據。
如果輸出文件格式字符串(受控制?--massif-out-file)不包含%p,那么來自父和子的輸出將被混合在單個輸出文件中,這幾乎肯定會使其無法讀取ms_print。
9.2.8。測量過程中的所有內存
值得強調的是,默認情況下地塊措施只堆內存,即與分配的內存?malloc,?calloc,?realloc,?memalign,?new,?new[],和一些其他類似的功能。(它可任選地測量堆棧存儲器,當然。)這意味著它不直接測量與較低級別的系統調用,如所分配的存儲器?mmap,?mremap和?brk。
堆分配功能,如malloc建立在這些系統調用之上。例如,當需要時,分配器通常將調用mmap分配大塊存儲器,然后響應于對等等的調用將該存儲器塊的塊交給客戶端程序malloc。Massif直接測量這些較高級別的malloc呼叫,而不是較低級別的系統呼叫。
此外,客戶端程序可以直接使用這些較低級別的系統調用來分配內存。默認情況下,Massif不測量這些。它也不測量代碼,數據和BSS段的大小。因此,Massif報告的數字可能會顯著小于通過工具報告的數據,例如top衡量程序在記憶體中的總體大小。
但是,如果要測量程序使用的所有內存,可以使用--pages-as-heap=yes。啟用此選項時,Massif的正常堆塊分析將被較低級頁面分析所取代。通過分配的mmap類似系統調用的每個頁面?被視為一個不同的塊。這意味著代碼,數據和BSS段都是測量的,因為它們只是內存頁面。即使堆棧被測量,因為它最終被分配(并且在必要時被擴展)通過mmap;?因為這個原因--stacks=yes是不允許的?--pages-as-heap=yes。
使用之后--pages-as-heap=yes,ms_print的輸出大部分不變。一個區別是每個詳細快照的開始說:
(頁面分配系統調用)mmap / mremap / brk,--alloc-fns等而不是通常的
: (堆分配函數)malloc / new / new [],--alloc-fns等輸出中的堆棧跟蹤可能更難讀取,并且解釋它們可能需要對諸如內存分配器的程序的較低級別進行詳細的了解。但是對于一些具有關于內存使用的完整信息的程序可能非常有用。
9.2.9。代理信息資料
Massif的信息通常很容易采取行動。開始觀看的明顯地方是峰值快照。
查看圖形的整體形狀,查看內存使用情況是否按照預期上升和下降也是有用的。圖中的峰值可能值得研究。
詳細的快照可以變得相當大。值得在非常廣的窗口中查看它們。使用文本編輯器查看它們也是個好主意。這樣可以輕松地向上和向下滾動,同時將光標保留在特定的列中,這使得分配鏈之后更容易。
9.3。Massif命令行選項
特定于Massif的命令行選項有:
--heap=<yes|no> [default: yes]指定堆分析是否應該完成。
如果啟用堆分析,則給出每個塊要使用的管理字節數。這應該是平均值的估計,因為它可能會有所不同。例如,glibc在Linux上使用的分配器需要每個塊的4到15個字節之間,具體取決于各種因素。該分配器還需要用于釋放塊的管理空間,但是Massif無法解釋這一點。
指定是否應進行堆棧分析。此選項會使Massif大大減慢,默認情況下也會關閉。請注意,Massif假定主棧在啟動時的大小為零。這是不正確的,但是否則準確地說是困難的。此外,從零開始更好地表示用戶程序實際上具有控制權的主堆棧部分的大小。
告訴Massif在頁面級別而不是malloc'd塊級別配置內存。詳見上文。
記錄詳細快照的分配樹的最大深度。增加它將使Massif運行得更慢,使用更多的內存,并產生更大的輸出文件。
使用此選項指定的函數將被視為一個堆分配函數,如?malloc。這對于包裝器的功能是有用的,malloc或者new可以用不感興趣的信息填充分配樹。可以在命令行中多次指定此選項,以指定多個功能。
請注意,如果命名函數是堆棧跟蹤中的頂部條目,或者正好位于另一個以此方式處理的函數的下方,則該函數將僅以此方式處理。例如,如果你有一個malloc1包裝的功能?malloc,并且malloc2包裝?malloc1,只是指定?--alloc-fn=malloc2將不起作用。您也需要指定--alloc-fn=malloc1。這有點不方便,但是原因是檢查分配功能很慢,如果Massif可以在找到不匹配而不是必須的時候停止查找堆棧跟蹤條目,就可以節省大量的時間繼續通過所有條目。
請注意,C ++名稱被解除。還要注意,重載的C ++名稱必須寫滿。單引號可能是必要的,以防止shell破壞它們。例如:
--alloc-fn ='operator new(unsigned,std :: nothrow_t const&)'任何直接堆分配(即調用?malloc,new等等,或一個由名為函數的調用--alloc-fn?選項)發生在該選項指定的函數將被忽略。這對于測試目的來說非常有用。可以在命令行中多次指定此選項,以指定多個功能。
任何realloc被忽略的塊也將被忽略,即使realloc調用沒有發生在忽略的函數中。如果忽略塊被縮小,這樣可以避免負堆大小的可能性?realloc。
編寫C ++函數名的規則與--alloc-fn上述相同。
堆分配的重要性閾值,占總內存大小的百分比。少于此的分配樹條目將被聚合。請注意,這應該與ms_print的相同名稱的選項一起指定。
Massif不一定記錄實際的全局內存分配峰值;?默認情況下,只有當全局內存分配大小超過上一個峰值至少1.0%時,才會記錄一個峰值。這是因為在路上可能有很多本地分配峰值,為每個分配峰進行詳細的快照將是昂貴和浪費的,因為除了其中一個將被丟棄。通過此選項可以將此不準確度更改為(甚至0.0%),但是當數字接近零時,Massif將運行得非常慢。
用于分析的時間單位。有三種可能性:執行指令(i),這對大多數情況都有好處;?實際(wallclock)時間(ms,即毫秒),這有時是有用的;?并且在堆和/或堆棧(B)上分配/釋放的字節,這對于非常短的程序是有用的,并且用于測試目的,因為它是跨越不同機器最可重復的。
詳細快照的頻率。隨著?--detailed-freq=1每個快照都是詳細的。
記錄的最大快照數。如果設置為N,對于除非常短路的程序以外的所有程序,快照的最終數量將在N / 2和N之間。
將配置文件數據寫入file?而不是默認的輸出文件?massif.out.<pid>。的?%p和%q格式說明可以用來嵌入進程ID和/或名稱的環境變量的內容,由于是用于芯選項的情況下?--log-file。
9.4。Massif監視器命令
Massif工具提供由Valgrind gdbserver?處理的監視器命令(請參閱由Valgrind gdbserver?監視命令處理)。
-
snapshot [<filename>]?請求獲取快照并將其保存在給定的<filename>(默認的massif.vgdb.out)中。
-
detailed_snapshot [<filename>]?請求獲取詳細的快照并將其保存在給定的<filename>(默認的massif.vgdb.out)中。
-
all_snapshots [<filename>]?請求到目前為止捕獲所有捕獲的快照并將其保存在給定的<filename>(默認的massif.vgdb.out)中。
9.5。Massif客戶端請求
Massif沒有massif.h文件,但它確實實現了兩個核心的客戶端請求:?VALGRIND_MALLOCLIKE_BLOCK和?VALGRIND_FREELIKE_BLOCK;?它們在?客戶端請求機制中描述。
9.6。ms_print命令行選項
ms_print的選項是:
-h --help顯示幫助信息。
顯示版本號。
與Massif的--threshold選項相同,但在分析之后而不是在之前應用。
圖形的寬度,以列為單位。
圖形的高度,以行為單位。
9.7。Massif的輸出文件格式
Massif的文件格式是純文本(即不是二進制文件),故意易于閱讀人機和機器。盡管如此,這里并沒有描述確切的格式。這是因為格式當前是非常Massif特定的。在將來,我們希望使格式更加一般,從而適合與其他工具的可能使用。一旦完成,格式將在這里記錄。
總結
- 上一篇: 组内管理帐号
- 下一篇: PAT1097:Deduplicatio