DHAT:动态堆分析工具
目錄
10.1。概觀10.2。了解DHAT的輸出要使用此工具,必須--tool=exp-dhat在Valgrind命令行上指定?。
10.1。概觀
DHAT是一個用于檢查程序如何使用其堆分配的工具。
它跟蹤分配的塊,并檢查每個內存訪問以查找哪個塊(如果有的話)。每個分配點(分配堆棧)收集和顯示以下數據:
-
總分配(字節(jié)數和塊數)
-
最大實際體積(字節(jié)數和塊數)
-
平均塊壽命(分配和釋放之間的指令數)
-
塊中每個字節(jié)的平均讀寫次數(“訪問率”)
-
對于始終分配只有一個大小的塊的分配點,大小為4096字節(jié)或更少:計數表示訪問塊內每個字節(jié)偏移的頻率。
使用這些統(tǒng)計信息可以識別具有以下特征的分配點:
-
潛在的過程生命周期泄漏:由點分配的塊剛剛累積,僅在運行結束時釋放。
-
過多的營業(yè)額:即使沒有持續(xù)很長時間,咀嚼了很多堆的點數
-
過度瞬態(tài):分配非常短暫的塊的點
-
無用或未充分使用的分配:分配但未完全填充或填寫但未隨后讀取的塊。
-
具有低效布局的區(qū)域 - 從未訪問的區(qū)域,或者散布在整個塊中的熱場。
與Massif堆分析器一樣,DHAT通過計數指令來測量程序進度,因此將所有年齡/時間相關數字作為指令計數。這首先聽起來有點奇怪,但是如果使用CPU時間,則使得運行可重復的方式是不可能的。
10.2。了解DHAT的輸出
DHAT提供了大量有關動態(tài)堆使用的有用信息。大多數使用它的藝術是解釋結果的數字。這是通過一組示例來說明的。
10.2.1。解釋max-live,tot-alloc和deaths字段
10.2.1.1。一個簡單的例子
========摘要統(tǒng)計========guest_insns:1,045,339,534[...]最大活動:63,490,984個塊tot-alloc:29,520塊中的1,904,700(平均尺寸64.52)死亡人數:29,520人,平均22,227,424人比例:6.37 rd,1.14 wr(12,141,526 b-read,2,174,460 b-written)在0x4C275B8:malloc(vg_replace_malloc.c:236)通過0x40350E:tcc_malloc(tinycc.c:6712)通過0x404580:tok_alloc_new(tinycc.c:7151)by 0x40870A:next_nomacro1(tinycc.c:9305)在整個程序中,這個堆棧(分配點)共分配了29,520個塊,總共包含1,904,700個字節(jié)。通過查看max-live數據,我們看到沒有多少塊同時生效,但是在高峰期,984個塊中有63,490個分配的字節(jié)。這告訴我們,該程序正在穩(wěn)定地釋放這樣的塊,而不是掛在所有的塊上,直到結束并釋放它們。
死亡條目告訴我們,這個堆棧分配的29,520個塊在程序運行期間死了(被釋放)。由于29,520也是總共分配的塊的數量,這告訴我們所有分配的塊在程序結束時被釋放。
它也告訴我們,平均死亡年齡為22,227,424個指示。從總結統(tǒng)計,我們看到,該計劃運行了1,045,339,534個指令,因此平均死亡年齡約為程序總運行時間的2%。
10.2.1.2。潛在的過程壽命泄漏示例
下一個示例(來自與上述不同的程序)顯示潛在的進程生命周期泄漏。當程序繼續(xù)分配數據時,會發(fā)生進程生命周期泄漏,但只能在退出之前釋放數據。因此,程序堆的大小不斷增加,但是Memcheck報告沒有泄漏,因為程序在退出時已經釋放了一切。這對于長時間運行的程序尤其危險。
========摘要統(tǒng)計========guest_insns:418,901,537[...]max-live:25412塊中的32,512tot-alloc:25412塊中的32,512(平均尺寸128.00)死亡人數:254人,平均年齡300,467,389人比例:0.26 rd,0.20 wr(8,756 b-read,6,604 b-written)在0x4C275B8:malloc(vg_replace_malloc.c:236)通過0x4C27632:realloc(vg_replace_malloc.c:525)by 0x56FF41D:QtFontStyle :: pixelSize(unsigned short,bool)(qfontdatabase.cpp:269)by 0x5700D69:loadFontConfig()(qfontdatabase_x11.cpp:1146)有兩個跡象表明這可能是一個生命周期的泄漏。首先,max-live和tot-alloc數字是相同的。可能發(fā)生的唯一方法是如果這些塊都被分配,然后全部被釋放。
其次,平均死亡人數(3億人)是總計劃生命周期的71%(4.1億美元),因此這不是一個暫時的無分配飆升 - 而是分散在大部分整個運行。一個解釋是,大概地,所有254個塊都在上半場被分配到下一半,然后在退出之前釋放。
10.2.2。解釋比例字段
10.2.2.1。相當無害的分配點記錄
最大活動:59,398個808個塊tot-alloc:24,240塊中的1,481,940(平均61.13)死亡人數:24,240人,平均年齡34,611,026人比例:2.13 rd,0.91 wr(3,166,650 b-read,1,358,820 b-written)在0x4C275B8:malloc(vg_replace_malloc.c:236)通過0x40350E:tcc_malloc(tinycc.c:6712)通過0x404580:tok_alloc_new(tinycc.c:7151)通過0x4046C4:tok_alloc(tinycc.c:7190)比例字段告訴我們,在塊被釋放之前,這里分配的塊中的每個字節(jié)讀取平均值為2.13倍。鑒于這些區(qū)塊的平均死亡年齡為34,611,026,每塊大約每1500個指令讀取一次。所以從這個角度來看,這些區(qū)別并不“工作”。
更有趣的是寫入比率:每個字節(jié)平均寫入0.91次。這告訴我們,分配的塊的一些部分永遠不會被寫入,平均至少有9%。要完全初始化塊將需要寫入每個字節(jié)至少一次,這將使寫入比為1.0。一些塊區(qū)明顯未被使用的事實可能指向數據對齊孔或其他布局低效。
那么,至少所有的區(qū)塊都被釋放(24,240個分配,24,240個死亡)。
如果所有的塊都是相同的大小,那么DHAT也會通過塊偏移來顯示訪問計數,所以我們可以看到這些未使用的區(qū)域在哪里。但是,情況并非如此:塊的大小不一樣,所以DHAT無法進行這樣的分析。我們可以看到它們必須具有不同的大小,因為平均塊大小(61.13)不是整數。
10.2.2.2。一個更可疑的例子
max-live:180,224在22個街區(qū)tot-alloc:22,2塊中的180,224(平均尺寸8192.00)死亡:無(這些區(qū)塊都沒有被釋放)比例:0.00 rd,0.00 wr(0 b-read,0 b-written)在0x4C275B8:malloc(vg_replace_malloc.c:236)通過0x40350E:tcc_malloc(tinycc.c:6712)0x40369C:__sym_malloc(tinycc.c:6787)通過0x403711:sym_malloc(tinycc.c:6805)這里,讀取和寫入訪問率都為零。因此,這一點是分配從未使用的塊,既不讀也不寫。事實上,他們也沒有被釋放(“死亡:無”),只是泄漏了。所以,這里是180k的完全無用的分配,可以刪除。
與Memcheck重新運行確實報告了同樣的泄漏。什么DHAT可以告訴我們,Memcheck不能,不僅是塊泄漏,它們也從未被使用過。
10.2.2.3。另一個可疑的例子
這里是一個分配塊,寫入但從不讀取的地方。我們從零讀取訪問比率立即看到這一點。他們確實得到釋放:
max-live:54個3個塊tot-alloc:1,020塊(平均尺寸18.00)死亡人數:90人,平均年齡34,558,236人比例:0.00 rd,1.11 wr(0 b-read,1,800 b-written)在0x4C275B8:malloc(vg_replace_malloc.c:236)通過0x40350E:tcc_malloc(tinycc.c:6712)通過0x4035BD:tcc_strdup(tinycc.c:6750)通過0x41FEBB:tcc_add_sysinclude_path(tinycc.c:20931)在前面的兩個示例中,很容易看到從不寫入或從不讀取的塊或兩者的某種組合。不幸的是,在C ++代碼中,情況不太清楚。這是因為一個對象的構造函數將寫入底層的塊,并且它的析構函數將從它讀取。因此,即使該對象一旦構造,該塊的讀取和寫入比率將不為零,也不會被使用,但只能最終被破壞。
真的,我們想要的只是在對象的構造結束和其破壞開始之間測量內存訪問。不幸的是,我不知道確定何時進行這些轉換的可靠方法。
10.2.3。解釋“通過偏移量進行聚合訪問計數”數據
對于總是分配相同大小的塊,分配4096字節(jié)或更小的塊的分配點,DHAT計數每個偏移量的訪問,例如:
max-live:317,408,5,668個塊tot-alloc:317,408,5,668塊(平均尺寸56.00)死亡人數:5,668人,平均622,890,597人比例:1.03 rd,1.28 wr(327,642 b-read,408,172 b-written)在0x4C275B8:malloc(vg_replace_malloc.c:236)通過0x5440C16:QDesignerPropertySheetPrivate :: ensureInfo(qhash.h:515)通過0x544350B:QDesignerPropertySheet :: setVisible(qdesigner_propertysh ...)通過0x5446232:QDesignerPropertySheet :: QDesignerPropertySheet(qdesigne ...)按偏移量進行聚合訪問計數:[0] 28782 28782 28782 28782 28782 28782 28782 28782[8] 20638 20638 20638 20638 0 0 0 0 [16] 22738 22738 22738 22738 22738 22738 22738 22738[24] 6013 6013 6013 6013 6013 6013 6013 6013 [32] 18883 18883 18883 37422 0 0 0 0[36] 5668 11915 5668 5668 11336 11336 11336 11336 [48] 6166 6166 6166 6166 0 0 0 0對于在64位平臺上運行的C ++代碼,這是相當典型的。在這里,我們對5668個塊的訪問統(tǒng)計信息進行了匯總,大小為56字節(jié)。每個字節(jié)至少被訪問5668次,除了偏移量12--15,36-39和52-55之外。這些可能是對準孔。
數字的仔細詮釋揭示了有用的信息。以N對齊偏移開始的N個連續(xù)相同數字的組,對于N為2,4或8,可能在該點處的結構中指示N字節(jié)對象。例如,該對象的前32個字節(jié)可能具有布局
[0] 64位類型[8] 32位類型[12] 32位定位孔[16] 64位類型[24] 64位類型作為反例,還清楚的是,無論在偏移量32處,它不是32位值。那是因為最后一個組(37422)與前三個(18883 18883 18883)不一樣。
這個例子引導一個人查詢(通過讀取源代碼)12-15和52-55的零是否是對齊孔,以及48--51是否確實是一個32位類型。如果是這樣,可能會將位置在48-51之間的位置替換為12--15,這樣可以將對象大小從56個字節(jié)縮小到48個字節(jié)。
請記住,以上推論只是“可能”。這是因為它們基于動態(tài)數據,而不是對對象布局的靜態(tài)分析。例如,零可能不是對齊孔,而只是結構的一部分,這些部分根本不用于此特定的運行。經驗表明,不太可能是這種情況,但可能會發(fā)生。
10.3。DHAT命令行選項
DHAT特定的命令行選項有:
--show-top-n=<number> [default: 10]在運行結束時,DHAT根據某個度量對累積的分配點進行排序,并顯示最高的得分條目。?--show-top-n?控制顯示的條目數。默認值為10是相當小的。對于現實的應用,您可能需要將其設置得更高,至少有幾百個。
在運行結束時,DHAT根據某個度量對累積的分配點進行排序,并顯示最高的得分條目。?--sort-by?選擇用于排序的度量:
max-bytes-live?最大活動字節(jié)[默認]
tot-bytes-allocd?字節(jié)總共分配(營業(yè)額)
max-blocks-live?最大活動塊
tot-blocks-allocd?分配總數(營業(yè)額)
這樣可以控制顯示分配點的順序。您可以選擇查看具有最多實時字節(jié)數或最高總字節(jié)周轉量,或最高數量的實時數據塊或最高總數據塊周轉量的分配點。這些給程序行為有用的不同圖片。例如,通過最大活動塊進行排序往往會顯示創(chuàng)建大量小對象的分配點。
需要注意的一個重要的一點是每個分配堆棧都是單獨的分配點。因為默認情況下堆棧有12個幀,所以這往往會在多個分配點上傳播數據。你可能想使用標記--num-callers = 4或一些這么小的數字來減少擴展。
總結
以上是生活随笔為你收集整理的DHAT:动态堆分析工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++11 并发指南三(Lock 详解)
- 下一篇: BZOJ 1412 [ZJOI2009]