Memcheck:一个内存错误检测器
目錄
4.1。概觀4.2。來自Memcheck的錯(cuò)誤消息說明要使用此工具,可以--tool=memcheck?在Valgrind命令行中指定。不過,由于Memcheck是默認(rèn)工具,所以您不需要。
4.1。概觀
Memcheck是一個(gè)內(nèi)存錯(cuò)誤檢測(cè)器。它可以檢測(cè)C和C ++程序中常見的以下問題。
-
訪問內(nèi)存您不應(yīng)該,例如超越和低估堆塊,超出堆棧的頂部,以及在釋放后訪問內(nèi)存。
-
使用未定義的值,即尚未初始化的值,或已從其他未定義值導(dǎo)出的值。
-
不正確的釋放堆內(nèi)存,比如雙重釋放堆塊,或使用?malloc/?new/?new[]?對(duì)?free/?delete/delete[]
-
重疊src和?dst指針?memcpy以及相關(guān)功能。
-
將一個(gè)腥味(大概為負(fù))值傳遞給?size一個(gè)內(nèi)存分配功能的參數(shù)。
-
內(nèi)存泄漏。
像這樣的問題可能難以通過其他方式找到,經(jīng)常長(zhǎng)時(shí)間不被發(fā)現(xiàn),然后導(dǎo)致偶爾的,難以診斷的崩潰。
4.2。來自Memcheck的錯(cuò)誤消息說明
Memcheck發(fā)出一系列錯(cuò)誤消息。本節(jié)將簡(jiǎn)要介紹哪些錯(cuò)誤消息的意思。錯(cuò)誤檢查機(jī)械的精確行為在Memcheck檢查機(jī)械的詳細(xì)信息中有所描述。
4.2.1。非法讀取/非法寫入錯(cuò)誤
例如:
大小4讀取無效在0x40F6BBCC :(在/usr/lib/libpng.so.2.1.0.9中)由0x40F6B804 :(在/usr/lib/libpng.so.2.1.0.9中)by 0x40B07FF4:read_png_image(QImageIO *)(kernel / qpngio.cpp:326)by 0x40AC751B:QImageIO :: read()(kernel / qimage.cpp:3621)地址0xBFFFF0E0不是stack'd,malloc'd或free'd當(dāng)您的程序在Memcheck認(rèn)為不應(yīng)該讀取或?qū)懭雰?nèi)存時(shí),會(huì)發(fā)生這種情況。在這個(gè)例子中,程序做了4個(gè)字節(jié)的地址處讀0xBFFFF0E0,內(nèi)系統(tǒng)提供的庫libpng.so.2.1.0.9,將其從其他地方以相同的庫調(diào)用某處,從326行調(diào)用qpngio.cpp,等等上。
Memcheck嘗試確定非法地址可能涉及什么,因?yàn)檫@通常是有用的。所以,如果它指向一個(gè)已經(jīng)被釋放的內(nèi)存塊,那么你會(huì)被通知這個(gè),而且塊被釋放。同樣,如果它應(yīng)該是剛剛結(jié)束堆塊,這是數(shù)組下標(biāo)中一個(gè)一個(gè)錯(cuò)誤的常見結(jié)果,你會(huì)被告知這個(gè)事實(shí),以及塊分配的地方。如果您使用--read-var-info選項(xiàng)Memcheck將運(yùn)行更慢,但可能會(huì)更詳細(xì)地描述任何非法地址。
在這個(gè)例子中,Memcheck不能識(shí)別地址。實(shí)際上地址在堆棧上,但是由于某種原因,這不是有效的堆棧地址 - 它不在堆棧指針下面,這是不允許的。在這種特殊情況下,這可能是由GCC產(chǎn)生的無效代碼造成的,這是古代GCC的一個(gè)已知bug。
請(qǐng)注意,Memcheck僅告訴您,您的程序即將訪問非法地址的內(nèi)存。它不能阻止訪問發(fā)生。所以,如果您的程序進(jìn)行通常會(huì)導(dǎo)致分段錯(cuò)誤的訪問,您的程序仍然會(huì)遇到相同的命運(yùn) - 但??是在此之前您將收到來自Memcheck的消息。在這個(gè)特定的例子中,在堆棧上讀取垃圾是非致命的,并且程序保持活著。
4.2.2。使用未初始化的值
例如:
有條件的跳躍或移動(dòng)取決于未初始化的值在0x402DFA94:_IO_vfprintf(_itoa.h:49)by 0x402E8476:_IO_printf(printf.c:36)通過0x8048472:main(tests / manuel1.c:8)當(dāng)程序使用尚未初始化的值(換句話說,未定義)時(shí),將報(bào)告未初始化值使用錯(cuò)誤。這里,未定義的值用于printfC庫機(jī)械的?某處。運(yùn)行以下小程序時(shí)報(bào)告此錯(cuò)誤:
int main() {int x;printf(“x =%d \ n”,x); }重要的是要了解,您的程序可以盡可能多地復(fù)制垃圾(未初始化)數(shù)據(jù)。Memcheck觀察這個(gè)并且跟蹤數(shù)據(jù),但不會(huì)抱怨。只有當(dāng)您的程序嘗試以可能影響程序外部可見行為的方式使用未初始化的數(shù)據(jù)時(shí),才會(huì)發(fā)出投訴。在此示例中,x未初始化。Memcheck觀察到傳遞給的值_IO_printf,然后發(fā)送給_IO_vfprintf,但不發(fā)表任何評(píng)論。但是,?_IO_vfprintf必須檢查它的值,?x所以它可以把它變成相應(yīng)的ASCII字符串,而在這一點(diǎn)Memcheck抱怨。
未初始化數(shù)據(jù)的來源往往是:
-
程序中的局部變量尚未初始化,如上例所示。
-
在你(或構(gòu)造函數(shù))之前的堆塊(分配給?malloc,new或類似的函數(shù))的內(nèi)容寫在那里。
要查看程序中未初始化數(shù)據(jù)源的信息,請(qǐng)使用該--track-origins=yes選項(xiàng)。這使得Memcheck運(yùn)行速度更慢,但可以更容易地追蹤未初始化值錯(cuò)誤的根本原因。
4.2.3。在系統(tǒng)調(diào)用中使用未初始化或不可尋址的值
Memcheck檢查系統(tǒng)調(diào)用的所有參數(shù):
-
它會(huì)自動(dòng)檢查所有直接參數(shù),無論它們是初始化的。
-
此外,如果系統(tǒng)調(diào)用需要從程序提供的緩沖區(qū)中讀取,Memcheck會(huì)檢查整個(gè)緩沖區(qū)是否可尋址,并將其內(nèi)容初始化。
-
此外,如果系統(tǒng)調(diào)用需要寫入用戶提供的緩沖區(qū),Memcheck會(huì)檢查緩沖區(qū)是否可尋址。
系統(tǒng)調(diào)用后,Memcheck更新其跟蹤信息,以精確反映系統(tǒng)調(diào)用引起的內(nèi)存狀態(tài)變化。
以下是兩個(gè)無效參數(shù)的系統(tǒng)調(diào)用示例:
#include <stdlib.h>#include <unistd.h>int main(void){char * arr = malloc(10);int * arr2 = malloc(sizeof(int));寫(1 / * stdout * /,arr,10);出口(ARR2 [0]);}你得到這些投訴...
Syscall param write(buf)指向未初始化的字節(jié)在0x25A48723:__write_nocancel(在/lib/tls/libc-2.3.3.so)by 0x259AFAD3:__libc_start_main(in /lib/tls/libc-2.3.3.so)由0x8048348 :(在/auto/homes/njn25/grind/head4/a.out中)地址0x25AB8028是大小為10的塊內(nèi)的0個(gè)字節(jié)在0x259852B0:malloc(vg_replace_malloc.c:130)由0x80483F1:主(ac:5)Syscall param exit(error_code)包含未初始化的字節(jié)在0x25A21B44:__GI__exit(在/lib/tls/libc-2.3.3.so)通過0x8048426:main(ac:8)因?yàn)槌绦蚓哂?#xff08;a)從堆塊將未初始化的垃圾寫入標(biāo)準(zhǔn)輸出,并且(b)將未初始化的值傳遞給exit。請(qǐng)注意,第一個(gè)錯(cuò)誤是指由buf(不是?buf本身)指向的內(nèi)存?,但是第二個(gè)錯(cuò)誤直接指向了exit參數(shù)?arr2[0]。
4.2.4。非法釋放
例如:
無效()在0x4004FFDF:free(vg_clientmalloc.c:577)通過0x80484C7:main(tests / doublefree.c:10)地址0x3807F7B4是一個(gè)大小為177的空閑塊內(nèi)的0個(gè)字節(jié)在0x4004FFDF:free(vg_clientmalloc.c:577)通過0x80484C7:main(tests / doublefree.c:10)Memcheck使用malloc/?跟蹤程序分配的塊new,所以它可以準(zhǔn)確地知道free/ /?的參數(shù)?delete是否合法。這里,這個(gè)測(cè)試程序已經(jīng)釋放了同一個(gè)程序段兩次。與非法讀/寫錯(cuò)誤一樣,Memcheck嘗試了解地址的釋放。如果在這里,地址是先前被釋放的地址,那么你會(huì)被告知 - 同一塊的重復(fù)釋放容易發(fā)現(xiàn)。如果您嘗試釋放不指向堆塊開始的指針,您還將收到此消息。
4.2.5。當(dāng)一個(gè)堆塊被釋放不正當(dāng)?shù)慕獬峙涔δ?/h3>
在以下示例中,分配的塊?new[]錯(cuò)誤地被釋放?free:
free()/ delete / delete []在0x40043249:free(vg_clientfuncs.c:171)by 0x4102BB4E:QGArray ::?QGArray(void)(tools / qgarray.cpp:149)由0x4C261C41:PptDoc ::?PptDoc(void)(include / qmemarray.h:60)通過0x4C261F0E:PptXml ::?PptXml(void)(pptxml.cc:44)地址0x4BB292A8是一個(gè)大小為64個(gè)alloc'd的塊內(nèi)的0個(gè)字節(jié)在0x4004318C:operator new [](unsigned int)(vg_clientfuncs.c:152)by 0x4C21BC15:KLaola :: readSBStream(int)const(klaola.cc:314)by 0x4C21C155:KLaola :: stream(KLaola :: OLENode const *)(klaola.cc:416)通過0x4C21788F:OLEFilter :: convert(QCString const&)(olefilter.cc:272)在C++它與它是如何分配兼容的方式來釋放內(nèi)存是很重要的。交易是:
-
如果與分配?malloc,?calloc,?realloc,?valloc或者?memalign,您必須取消分配free。
-
如果分配new,你必須解除配置delete。
-
如果分配new[],你必須解除配置delete[]。
最糟糕的是,在Linux上顯然無論如何混合使用,但同樣的程序可能會(huì)在不同的平臺(tái)(例如Solaris)上崩潰。所以最好是妥善解決它。根據(jù)KDE的人“令人驚奇的是有多少C ++程序員不知道這個(gè)”。
要求背后的原因如下。在某些C ++實(shí)現(xiàn)中,delete[]必須使用分配的對(duì)象,new[]因?yàn)樵谥羔槍?shí)際返回之前,編譯器會(huì)將數(shù)組的大小和指針成員存儲(chǔ)到數(shù)組的內(nèi)容的析構(gòu)函數(shù)中。?delete不解決這個(gè)問題,會(huì)混淆,可能會(huì)破壞堆。
4.2.6。重疊的源和目標(biāo)塊
下面的C庫函數(shù)從一個(gè)存儲(chǔ)器塊復(fù)制一些數(shù)據(jù)到另一個(gè)(或類似的東西): ,?memcpy,?strcpy,?strncpy,?。strcat?strncat由他們src和?dst指針指向的塊不允許重疊。POSIX標(biāo)準(zhǔn)具有“如果在重疊的對(duì)象之間進(jìn)行復(fù)制,行為未定義”。因此,Memcheck檢查這一點(diǎn)。
例如:
== 27492 == memcpy中的源和目的地重疊(0xbffff294,0xbffff280,21) == 27492 == 0x40026CDC:memcpy(mc_replace_strmem.c:71) == 27492 == by 0x804865A:main(overlap.c:40)您不希望兩個(gè)塊重疊,因?yàn)槠渲幸粋€(gè)塊可能被復(fù)制部分覆蓋。
你可能會(huì)認(rèn)為,Memcheck在dst小于等于?的情況下被過度迂回地報(bào)告src。例如,明顯的實(shí)現(xiàn)方式memcpy是從第一個(gè)字節(jié)到最后一個(gè)字節(jié)的復(fù)制。但是,某些架構(gòu)的優(yōu)化指南建議從最后一個(gè)字節(jié)復(fù)制到第一個(gè)。而且,復(fù)制前的一些實(shí)現(xiàn)memcpy為零?dst,因?yàn)闅w零目的地的高速緩存行可以提高性能。
故事的道德是:如果你想寫真正的便攜式代碼,不要對(duì)語言實(shí)現(xiàn)做任何假設(shè)。
4.2.7。有趣的參數(shù)值
所有內(nèi)存分配函數(shù)都會(huì)使用一個(gè)參數(shù)來指定應(yīng)該分配的內(nèi)存塊的大小。顯然,請(qǐng)求的大小應(yīng)該是非負(fù)值,通常不會(huì)過大。例如,在64位機(jī)器上,分配請(qǐng)求的大小超過2 ** 63個(gè)字節(jié)是非常不起眼的。更可能的是,這樣的值是錯(cuò)誤大小計(jì)算的結(jié)果,并且實(shí)際上是負(fù)值(恰好恰好出現(xiàn)過大,因?yàn)槲荒J奖唤忉尀闊o符號(hào)整數(shù))。這樣的值被稱為“腥值”。所述size的分配如下功能參數(shù)被檢查為腥:?malloc,?calloc,?realloc,?memalign,?new,?new []。?__builtin_new,?__builtin_vec_new,calloc
例如:
== 32233 ==函數(shù)malloc的參數(shù)'size'有一個(gè)腥(可能為負(fù))的值:-3 == 32233 ==在0x4C2CFA7:malloc(vg_replace_malloc.c:298) == 32233 == 0x400555:foo(fishy.c:15) == 32233 == 0x400583:main(fishy.c:23)在早期的Valgrind版本中,這些值被稱為“愚蠢的參數(shù)”,并且不包括后跟蹤。
4.2.8。內(nèi)存泄漏檢測(cè)
Memcheck記錄所有響應(yīng)于malloc/?etc的調(diào)用發(fā)出的堆塊?new。所以當(dāng)程序退出時(shí),它知道哪些塊沒有被釋放。
如果--leak-check適當(dāng)設(shè)置,對(duì)于每個(gè)剩余的塊,Memcheck確定塊是否可以從根集中的指針到達(dá)。根組包括(a)所有線程的通用寄存器,以及(b)可訪問的客戶端存儲(chǔ)器中的初始化,對(duì)齊,指針大小的數(shù)據(jù)字,包括堆棧。
有兩種方式可以達(dá)到一個(gè)塊。第一個(gè)是使用“開始指針”,即指向塊開頭的指針。第二個(gè)是“內(nèi)部指針”,即指向塊中間的指針。有幾種方式可以發(fā)現(xiàn)內(nèi)部指針可以發(fā)生:
-
指針本來可能是一個(gè)開始指針,并被程序故意地(或不是故意地)移動(dòng)。特別是,如果您的程序使用帶標(biāo)記的指針,即如果它使用指針的底部,兩位或三位,由于對(duì)齊而通常總是為零,以便存儲(chǔ)額外的信息,則可能會(huì)發(fā)生這種情況。
-
這可能是內(nèi)存中的隨機(jī)垃圾值,完全不相關(guān),只是巧合。
-
它可能是指向C ++的內(nèi)部char數(shù)組的指針?std::string。例如,一些編譯器在std :: string的開頭添加3個(gè)字,以便在包含字符數(shù)組的內(nèi)存之前存儲(chǔ)長(zhǎng)度,容量和引用計(jì)數(shù)。他們?cè)谶@3個(gè)字之后返回一個(gè)指針,指向char數(shù)組。
-
一些代碼可能會(huì)分配一個(gè)內(nèi)存塊,并使用前8個(gè)字節(jié)來存儲(chǔ)(塊大小-8)作為64位數(shù)。?sqlite3MemMalloc做這個(gè)。
-
它可能是一個(gè)指向分配給C ++對(duì)象(具有析構(gòu)函數(shù))的數(shù)組的指針new[]。在這種情況下,一些編譯器在分配的塊的開始處存儲(chǔ)包含數(shù)組長(zhǎng)度的“魔術(shù)cookie”,并返回一個(gè)指向剛剛過去的魔術(shù)cookie(即內(nèi)部指針)的指針。有關(guān)詳細(xì)信息,請(qǐng)參閱此頁面。
-
它可能是指向使用多重繼承的C ++對(duì)象的內(nèi)部部分的指針。
您可以選擇激活啟發(fā)式檢漏過程中使用,以檢測(cè)對(duì)應(yīng)的內(nèi)部指針stdstring,?length64,?newarray?和multipleinheritance案件。如果啟發(fā)式檢測(cè)到內(nèi)部指針對(duì)應(yīng)于這種情況,則該塊將被認(rèn)為是內(nèi)部指針可達(dá)的。換句話說,內(nèi)部指針將被視為起始指針。
考慮到這一點(diǎn),考慮下面描述的九種可能的情況。
指針鏈AAA泄漏案BBB泄漏案------------- ------------- ------------- (1)RRR ------------> BBB DR (2)RRR ---> AAA ---> BBB DR IR (3)RRR BBB DL (4)RRR AAA ---> BBB DL IL (5)RRR ------?-----> BBB(y)DR,(n)DL (6)RRR ---> AAA - ? - > BBB DR(y)IR,(n)DL (7)RRR - → - > AAA ---> BBB(y)DR,(n)DL(y)IR,(n)IL (8)RRR-α - > AAA - → - > BBB(y)DR,(n)DL(y,y)IR,(n,y)IL,(_,n) (9)RRR AAA-β-> BBB DL(y)IL,(n)DL指針鏈傳說: - RRR:根集節(jié)點(diǎn)或DR塊 - AAA,BBB:堆塊 - --->:一個(gè)開始指針 - - ? - >:內(nèi)部指針泄漏案例傳奇: - DR:直接可達(dá) - IR:間接可達(dá) - DL:直接丟失 - IL:間接丟失 - (y)XY:如果內(nèi)部指針是真正的指針,則為XY - (n)XY:如果內(nèi)部指針不是真正的指針,則為XY - (_)XY:在任一種情況下都是XY每個(gè)可能的情況都可以減少到上述九個(gè)之一。Memcheck在其輸出中合并了一些這種情況,導(dǎo)致以下四種泄漏。
-
“仍然可達(dá)”。這涵蓋上面的1和2(對(duì)于BBB塊)的情況。找到一個(gè)起始指針或鏈的起始指針。由于塊仍然指向,程序員至少原則上可以在程序退出之前釋放它。“仍然可達(dá)”的塊是非常普遍的,可以說是不成問題的。因此,默認(rèn)情況下,Memcheck不會(huì)單獨(dú)報(bào)告此類塊。
-
“絕對(duì)迷失”。這涵蓋上面的案例3(對(duì)于BBB塊)。這意味著沒有找到指向塊的指針。該塊被分類為“丟失”,因?yàn)槌绦騿T不可能在程序退出時(shí)釋放它,因?yàn)闆]有指向它的指針。這可能是程序中某個(gè)較早點(diǎn)丟失指針的癥狀。這種情況應(yīng)由程序員修正。
-
“間接失蹤”。這涵蓋上面的案例4和9(對(duì)于BBB塊)。這意味著塊丟失,而不是因?yàn)闆]有指針,而是因?yàn)樗兄赶蛩膲K都是丟失的。例如,如果您有一個(gè)二叉樹并且根節(jié)點(diǎn)丟失,則其所有子節(jié)點(diǎn)將被間接丟失。因?yàn)槿绻麑?dǎo)致間接泄漏的絕對(duì)丟失塊是固定的,那么問題會(huì)消失,因此默認(rèn)情況下Memcheck不會(huì)單獨(dú)報(bào)告這些塊。
-
“可能丟失”。這涵蓋上面的5-8(對(duì)于BBB塊)。這意味著已經(jīng)找到了一個(gè)或多個(gè)指向塊的指針的鏈,但是指針中的至少一個(gè)是內(nèi)部指針。這可能只是內(nèi)存中的一個(gè)隨機(jī)值,恰好指向一個(gè)塊,所以你不應(yīng)該考慮這個(gè)確定,除非你知道你有內(nèi)部指針。
(注:將九種可能的情況映射到四種泄漏方式不一定是報(bào)告泄漏的最佳方法;特別是內(nèi)部指針被不一致地處理,將來可能會(huì)改進(jìn)分類。)
此外,如果塊的存在,則無論其屬于上述四種,都將被報(bào)告為“被抑制”。
以下是泄漏匯總示例。
泄漏摘要:絕對(duì)丟失:3個(gè)塊中的48個(gè)字節(jié)。間接丟失:32個(gè)字節(jié)在2個(gè)塊。可能丟失:6個(gè)塊中的96個(gè)字節(jié)。仍然可達(dá):64個(gè)字節(jié)在4個(gè)塊。被禁止:0個(gè)字節(jié),0個(gè)塊。如果使用啟發(fā)式方法來將某些塊視為可達(dá)到的,則漏洞摘要將詳細(xì)介紹每個(gè)啟發(fā)式啟發(fā)式啟發(fā)式可達(dá)的“仍可達(dá)到的”子集。在下面的例子中,95字節(jié)仍然可以達(dá)到,87字節(jié)(56 + 7 + 8 + 16)被認(rèn)為是啟發(fā)式可達(dá)的。
泄漏摘要:絕對(duì)丟失:4個(gè)字節(jié)在1個(gè)塊間接丟失:0個(gè)字節(jié),0個(gè)塊可能丟失:0個(gè)塊中的0個(gè)字節(jié)仍然可達(dá):6個(gè)塊中有95個(gè)字節(jié)其中通過啟發(fā)式可達(dá)到:stdstring:56個(gè)字節(jié),2個(gè)塊length64:16個(gè)字節(jié),1個(gè)塊newarray:1個(gè)塊中有7個(gè)字節(jié)多個(gè)繼承:1個(gè)塊中8個(gè)字節(jié)被禁止:0個(gè)字節(jié),0個(gè)塊如果--leak-check=full指定,Memcheck將給出每個(gè)絕對(duì)丟失或可能丟失的塊的詳細(xì)信息,包括分配的位置。(實(shí)際上,它將具有相同泄漏類型的所有塊的結(jié)果和足夠相似的堆棧跟蹤合并到一個(gè)“丟失記錄”中,?--leak-resolution允許您控制“足夠相似”的含義。)它不能告訴你什么時(shí)候或如何或?yàn)槭裁粗赶蛞粋€(gè)泄漏塊的指針丟失了;?你必須為自己工作。一般來說,您應(yīng)該嘗試確保您的程序在出口時(shí)沒有任何明顯的丟失或可能丟失的塊。
例如:
1塊中的8個(gè)字節(jié)在14的損失記錄中絕對(duì)丟失在0x ........:malloc(vg_replace_malloc.c:...)by 0x ........:mk(leak-tree.c:11)by 0x ........:main(leak-tree.c:39)1個(gè)塊中的88個(gè)(8個(gè)直接,80個(gè)間接)字節(jié)在14的損失記錄13中絕對(duì)丟失在0x ........:malloc(vg_replace_malloc.c:...)by 0x ........:mk(leak-tree.c:11)by 0x ........:main(leak-tree.c:25)第一條消息描述了一個(gè)完全丟失的單個(gè)8字節(jié)塊的簡(jiǎn)單情況。第二個(gè)例子提到另一個(gè)8字節(jié)塊已經(jīng)絕對(duì)丟失了;?不同之處在于其他塊中的另外80個(gè)字節(jié)由于這個(gè)丟失塊而間接丟失。損失記錄不以任何顯著的順序呈現(xiàn),因此損失記錄數(shù)字并不具體。損失記錄號(hào)可以在Valgrind gdbserver中使用,以列出泄露塊的地址和/或提供有關(guān)塊仍然可達(dá)的更多細(xì)節(jié)。
該選項(xiàng)--show-leak-kinds=<set>?控制一組泄漏種類以顯示何時(shí)--leak-check=full被指定。
該<set>泄漏種在下列方式之一指定的:
-
一個(gè)或多個(gè)逗號(hào)分隔的列表?definite indirect possible reachable。
-
all?指定完整集(所有泄漏種類)。
-
none?為空集。
泄漏種類的默認(rèn)值為?--show-leak-kinds=definite,possible。
還可以顯示可達(dá)和間接丟失的塊,除了絕對(duì)可能丟失的塊,您可以使用--show-leak-kinds=all。只顯示可達(dá)和間接丟失的塊,使用?--show-leak-kinds=indirect,reachable。然后可以顯示可達(dá)和間接丟失的塊,如以下兩個(gè)示例所示。
4塊中的64字節(jié)仍然可以在4的損失記錄中達(dá)到在0x ........:malloc(vg_replace_malloc.c:177)by 0x ........:mk(leak-cases.c:52)by 0x ........:main(leak-cases.c:74)2個(gè)字節(jié)中的32個(gè)字節(jié)在4的損失記錄1中間接丟失在0x ........:malloc(vg_replace_malloc.c:177)by 0x ........:mk(leak-cases.c:52)by 0x ........:main(leak-cases.c:80)因?yàn)橛胁煌N類的泄漏有不同的嚴(yán)重性,一個(gè)有趣的問題是:哪些泄漏應(yīng)該算作真正的“錯(cuò)誤”,哪些不應(yīng)該?
這個(gè)問題的答案會(huì)影響打印ERROR SUMMARY在行中的數(shù)字,也影響--error-exitcode選項(xiàng)的效果。首先,如果指定了漏洞,則僅將其視為真實(shí)的“錯(cuò)誤”?--leak-check=full。然后,該選項(xiàng)--errors-for-leak-kinds=<set>控制要作為錯(cuò)誤考慮的一組泄漏種類。默認(rèn)值為--errors-for-leak-kinds=definite,possible
4.3。Memcheck命令行選項(xiàng)
--leak-check=<no|summary|yes|full> [default: summary]啟用時(shí),在客戶端程序完成時(shí)搜索內(nèi)存泄漏。如果設(shè)置summary,它說發(fā)生了多少泄漏。如果設(shè)置為full或者?yes,每個(gè)單獨(dú)的泄漏將被詳細(xì)顯示和/或計(jì)數(shù)為錯(cuò)誤,如選項(xiàng)?--show-leak-kinds和?--errors-for-leak-kinds。
在進(jìn)行泄漏檢查時(shí),確定Memcheck如何將不同的回溯視為相同的目的,以將多個(gè)泄漏合并到單個(gè)泄漏報(bào)告中。設(shè)置時(shí)low,只有前兩個(gè)條目需要匹配。何時(shí)med,四個(gè)條目必須匹配。何時(shí)high,所有條目都需要匹配。
對(duì)于硬核泄漏調(diào)試,您可能希望?--leak-resolution=high與--num-callers=40這些大數(shù)量一起使用?。
請(qǐng)注意,該--leak-resolution設(shè)置不會(huì)影響Memcheck查找泄漏的能力。它只會(huì)改變結(jié)果的呈現(xiàn)方式。
指定泄漏檢查中顯示的泄漏種類,full?方法如下:
-
一個(gè)或多個(gè)逗號(hào)分隔的列表?definite indirect possible reachable。
-
all指定完整集(所有泄漏種類)。相當(dāng)于?--show-leak-kinds=definite,indirect,possible,reachable。
-
none?為空集。
指定泄漏種類作為full泄漏搜索中的錯(cuò)誤?。這?<set>是相同的?--show-leak-kinds
指定在泄漏搜索期間使用的泄漏檢查啟發(fā)式集合。啟發(fā)式控制指向一個(gè)塊的內(nèi)部指針使其被認(rèn)為是可達(dá)到的。啟發(fā)式集合以下列方式之一指定:
-
一個(gè)或多個(gè)逗號(hào)分隔的列表?stdstring length64 newarray multipleinheritance。
-
all激活完整的啟發(fā)式集。相當(dāng)于?--leak-check-heuristics=stdstring,length64,newarray,multipleinheritance。
-
none?為空集。
這些選項(xiàng)提供了一種替代方法來指定泄漏種類來顯示:
-
--show-reachable=no --show-possibly-lost=yes相當(dāng)于?--show-leak-kinds=definite,possible。
-
--show-reachable=no --show-possibly-lost=no相當(dāng)于?--show-leak-kinds=definite。
-
--show-reachable=yes相當(dāng)于?--show-leak-kinds=all。
控制Memcheck報(bào)告是否使用未定義的值錯(cuò)誤。no如果您不想看到未定義的值錯(cuò)誤,請(qǐng)將其設(shè)置為?。它也有加速M(fèi)emcheck的副作用。
控制Memcheck是否跟蹤未初始化值的來源。默認(rèn)情況下,它不會(huì),這意味著雖然可以告訴您未初始化的值正在以危險(xiǎn)的方式使用,但它不能告訴您未初始化值的來源。這通常使得難以追溯根本問題。
設(shè)置時(shí)yes,Memcheck記錄所有未初始化值的起始位置。然后,當(dāng)報(bào)告未初始化的值錯(cuò)誤時(shí),Memcheck將嘗試顯示值的原點(diǎn)。原點(diǎn)可以是以下四個(gè)位置之一:堆塊,堆棧分配,客戶端請(qǐng)求或其他其他來源(例如,調(diào)用brk)。
對(duì)于源自堆塊的未初始化值,Memcheck顯示塊分配的位置。對(duì)于來自堆棧分配的未初始化值,Memcheck可以告訴您分配了哪個(gè)值,但不超過該值 - 通常它會(huì)顯示函數(shù)的開始大括號(hào)的源位置。所以你應(yīng)該仔細(xì)檢查所有函數(shù)的局部變量是否正確初始化。
性能開銷:原始跟蹤價(jià)格昂貴。它使Memcheck的速度減半,并將內(nèi)存使用量提高至少100MB,甚至更多。然而,它可以大大減少確定未初始化價(jià)值錯(cuò)誤的根本原因所需的努力,因此,盡管運(yùn)行速度更慢,但通常也是程序員的生產(chǎn)力獲勝。
準(zhǔn)確度:Memcheck相當(dāng)準(zhǔn)確地跟蹤起始點(diǎn)。為了避免非常大的空間和時(shí)間開銷,進(jìn)行一些近似。盡管可能不大可能,Memcheck會(huì)報(bào)告錯(cuò)誤的來源,或者無法識(shí)別任何來源。
注意組合?--track-origins=yes?并且--undef-value-errors=no是無意義的。Memcheck啟動(dòng)時(shí)檢查并拒絕此組合。
控制Memcheck如何從一些字節(jié)可尋址的地址處理32位,64位,128位和256位自然對(duì)齊的負(fù)載,而其他字節(jié)不可尋址。何時(shí)yes,這樣的負(fù)載不會(huì)產(chǎn)生地址錯(cuò)誤。相反,來自非法地址的加載字節(jié)被標(biāo)記為未初始化,并且以正常方式處理與合法地址相對(duì)應(yīng)的字節(jié)。
當(dāng)no來自部分無效地址的加載處理與完全無效地址的加載相同時(shí):發(fā)出非法地址錯(cuò)誤,并將生成的字節(jié)標(biāo)記為初始化。
請(qǐng)注意,以這種方式行為的代碼違反ISO C / C ++標(biāo)準(zhǔn),應(yīng)被視為破壞。如果可能,這些代碼應(yīng)該是固定的。
控制在檢查值的定義時(shí),Memcheck是否應(yīng)該使用更精確但更昂貴(耗時(shí))的算法。默認(rèn)設(shè)置不是這樣做,它通常就足夠了。然而,對(duì)于高度優(yōu)化的代碼,valgrind有時(shí)可能會(huì)錯(cuò)誤地抱怨。調(diào)用valgrind?--expensive-definedness-checks=yes?有助于提高性能成本。已經(jīng)觀察到25%的運(yùn)行時(shí)間降級(jí),但額外的成本在很大程度上取決于手頭的應(yīng)用。
控制用于保持malloc'd和/或free'd塊的堆棧跟蹤。
使用alloc-then-free,在分配時(shí)間記錄堆棧跟蹤,并與塊相關(guān)聯(lián)。當(dāng)塊被釋放時(shí),記錄第二個(gè)堆棧跟蹤,這將替換分配堆棧跟蹤。因此,與此塊相關(guān)的任何“使用免費(fèi)”錯(cuò)誤只能顯示塊被釋放的位置的堆棧跟蹤。
對(duì)于alloc-and-free塊,存儲(chǔ)塊的分配和分配堆棧跟蹤。因此,“免費(fèi)使用”錯(cuò)誤將同時(shí)顯示,這可能會(huì)使錯(cuò)誤更容易診斷。與alloc-then-free此相比,此設(shè)置稍微增加了Valgrind的內(nèi)存使用,因?yàn)樵搲K包含兩個(gè)引用而不是一個(gè)引用。
有了alloc,只是分配棧跟蹤記錄(并報(bào)告)。隨著free,只有釋放堆棧跟蹤記錄(并報(bào)告)。這些值稍微減少了Valgrind的內(nèi)存和CPU使用率。它們可以是有用的,具體取決于您正在搜索的錯(cuò)誤類型以及需要分析的詳細(xì)程度。例如,如果您只對(duì)內(nèi)存泄漏錯(cuò)誤感興趣,則記錄分配堆棧跟蹤就足夠了。
隨著none,沒有堆棧跟蹤記錄的malloc和free操作。如果您的程序分配了許多塊和/或從許多不同的堆棧跟蹤分配/釋放,這可以顯著降低cpu和/或所需的內(nèi)存。當(dāng)然,與堆塊相關(guān)的錯(cuò)誤將不會(huì)報(bào)告很少的細(xì)節(jié)。
請(qǐng)注意,一旦記錄了堆棧跟蹤,Valgrind將堆棧跟蹤保存在內(nèi)存中,即使它沒有被任何塊引用。一些程序(例如,遞歸算法)可以產(chǎn)生大量堆棧跟蹤。如果Valgrind在這種情況下使用太多內(nèi)存,可以減少選項(xiàng)所需的內(nèi)存--keep-stacktraces?和/或使用較小的選項(xiàng)值--num-callers。
當(dāng)客戶端程序使用free(in?C)或?delete?(C++)釋放內(nèi)存時(shí)?,該內(nèi)存不會(huì)立即可用于重新分配。相反,它被標(biāo)記為不可訪問,并放置在自由塊的隊(duì)列中。目的是盡可能延長(zhǎng)釋放記憶回流的時(shí)間。這增加了Memcheck在被釋放后的某個(gè)相當(dāng)長(zhǎng)的一段時(shí)間內(nèi)能夠檢測(cè)到塊的無效訪問的機(jī)會(huì)。
此選項(xiàng)指定隊(duì)列中塊的最大總大小(以字節(jié)為單位)。默認(rèn)值為二千萬字節(jié)。增加這會(huì)增加Memcheck使用的內(nèi)存總量,但是可能會(huì)檢測(cè)到無效的釋放塊的使用,否則將無法檢測(cè)到。
當(dāng)從可用于重新分配的釋放塊的隊(duì)列中制作塊時(shí),Memcheck優(yōu)先重新循環(huán)大小或等于的大小--freelist-big-blocks。這確保了釋放大塊(特別是釋放塊大于?--freelist-vol)不會(huì)立即導(dǎo)致自由列表中所有(或許多)小塊的重新流通。換句話說,即使大塊被釋放,這個(gè)選項(xiàng)增加了發(fā)現(xiàn)“小”塊的懸掛指針的可能性。
設(shè)置值為0表示所有塊都按FIFO順序重新循環(huán)。
啟用時(shí),假設(shè)讀取和寫入堆棧指針之外的一些小距離是由于GCC 2.96中的錯(cuò)誤,并且不會(huì)報(bào)告它們。“小距離”默認(rèn)為256字節(jié)。請(qǐng)注意,GCC 2.96是一些古老的Linux發(fā)行版(RedHat 7.X)上的默認(rèn)編譯器,因此您可能需要使用此選項(xiàng)。不要使用它,如果你不必要,因?yàn)樗梢詫?dǎo)致真正的錯(cuò)誤被忽視。一個(gè)更好的選擇是使用更新的GCC修復(fù)這個(gè)錯(cuò)誤。
在32位PowerPC Linux上使用GCC 3.X或4.X時(shí),還可能需要使用此選項(xiàng)。這是因?yàn)镚CC生成的代碼偶爾訪問堆棧指針以下,特別是對(duì)于浮點(diǎn)數(shù)到整數(shù)轉(zhuǎn)換。這違反了32位PowerPC ELF規(guī)范,這并不意味著堆棧指針下方的位置可以訪問。
此選項(xiàng)自版本3.12已棄用,可能會(huì)從將來的版本中刪除。您應(yīng)該使用它?--ignore-range-below-sp來指定應(yīng)忽略的堆棧指針下方的偏移量的精確范圍。一個(gè)合適的等價(jià)物是--ignore-range-below-sp=1024-1。
這是替代不推薦使用的?--workaround-gcc296-bugs選項(xiàng)。指定時(shí),會(huì)導(dǎo)致Memcheck不會(huì)在堆棧指針下方的指定偏移量下報(bào)告訪問錯(cuò)誤。兩個(gè)偏移量必須是正十進(jìn)制數(shù),并且有些是反直覺的 - 第一個(gè)必須更大,以便將非環(huán)繞地址范圍意味著忽略。例如,要忽略堆棧指針下方8192字節(jié)的4字節(jié)訪問,請(qǐng)使用--ignore-range-below-sp=8192-8189。只能指定一個(gè)范圍。
啟用時(shí),Memcheck使用與分配功能匹配的功能來檢查堆塊是否被釋放。也就是說,它預(yù)計(jì)free將用于刪除根據(jù)所分配的塊malloc,delete供分配的塊new,并delete[]為塊的分配new[]。如果檢測(cè)到不匹配,則報(bào)告錯(cuò)誤。這通常很重要,因?yàn)樵谀承┉h(huán)境中,釋放不匹配的功能可能導(dǎo)致崩潰。
但是有一種情況是不能避免這種錯(cuò)配。那就是當(dāng)用戶提供?new/?new[]該呼叫malloc和delete/或delete[]該呼叫的實(shí)現(xiàn)free時(shí),這些功能是不對(duì)稱內(nèi)聯(lián)的。例如,假設(shè)delete[]是內(nèi)聯(lián)的,但new[]不是。結(jié)果是Memcheck將所有delete[]呼叫視為直接呼叫free,即使程序源不包含不匹配的呼叫。
這會(huì)導(dǎo)致很多混亂和不相關(guān)的錯(cuò)誤報(bào)告。?--show-mismatched-frees=no禁用這些檢查。盡管如此,通常不建議您禁用它們,因?yàn)槟赡軙?huì)錯(cuò)過實(shí)際錯(cuò)誤。
Memcheck的可尋址性檢查將忽略此選項(xiàng)中列出的任何范圍(可以指定多個(gè)范圍,以逗號(hào)分隔)。
填補(bǔ)了分配的塊malloc,?new等等,但不calloc與指定的字節(jié)。這在嘗試擺脫晦澀的內(nèi)存損壞問題時(shí)很有用。分配的區(qū)域仍然被Memcheck視為未定義 - 此選項(xiàng)僅影響其內(nèi)容。請(qǐng)注意,--malloc-fill當(dāng)用作客戶端請(qǐng)求VALGRIND_MEMPOOL_ALLOC或VALGRIND_MALLOCLIKE_BLOCK的參數(shù)時(shí),不會(huì)影響內(nèi)存塊。
填充由釋放的塊free,?delete等等,與指定的字節(jié)值。這在嘗試擺脫晦澀的內(nèi)存損壞問題時(shí)很有用。Memcheck仍然將被釋放的區(qū)域視為訪問無效 - 此選項(xiàng)僅影響其內(nèi)容。請(qǐng)注意,--free-fill當(dāng)用作客戶端請(qǐng)求VALGRIND_MEMPOOL_FREE或VALGRIND_FREELIKE_BLOCK的參數(shù)時(shí),不會(huì)影響內(nèi)存塊。
4.4。編寫抑制文件
抑制錯(cuò)誤中描述了基本抑制格式?。
抑制型(第二行)應(yīng)具有以下格式:
MEMCHECK:suppression_typeMemcheck抑制類型如下:
-
Value1,?Value2,?Value4,?Value8,?Value16,使用的1,2,4,8或16字節(jié)的值時(shí)意一個(gè)未初始化的值錯(cuò)誤。
-
Cond(或其舊名稱Value0),意思是使用未初始化的CPU條件代碼。
-
Addr1,?Addr2,?Addr4,?Addr8,?Addr16,分別是1,2,4,8或16字節(jié)的存儲(chǔ)器訪問期間意思的地址無效。
-
Jump,意味著跳轉(zhuǎn)到不可尋址的位置錯(cuò)誤。
-
Param,意味著無效的系統(tǒng)調(diào)用參數(shù)錯(cuò)誤。
-
Free,意思是無效或不匹配的。
-
Overlap,意思是?src/?dst重疊?memcpy或相似的功能。
-
Leak,意味著內(nèi)存泄漏。
Param?此時(shí),錯(cuò)誤具有強(qiáng)制性的額外信息行,這是違規(guī)系統(tǒng)調(diào)用參數(shù)的名稱。
Leak?錯(cuò)誤有一個(gè)可選的額外信息行,格式如下:
匹配泄漏種:<設(shè)定>其中<set>指定該抑制條目匹配的泄漏種類。?<set>以與選項(xiàng)相同的方式指定--show-leak-kinds,即以下之一:
- 一個(gè)或多個(gè)逗號(hào)分隔的列表?definite indirect possible reachable。
- all?指定完整集(所有泄漏種類)。
- none?為空集。
如果此可選附加行不存在,則抑制條目將匹配所有泄漏類型。
請(qǐng)注意,使用創(chuàng)建的泄漏抑制?--gen-suppressions將包含此可選額外的行,因此可能會(huì)比您預(yù)期的更少的泄漏。您可能需要在使用生成的壓縮之前刪除該行。
其他Memcheck錯(cuò)誤種類沒有額外的行。
如果您給出-v選項(xiàng),Valgrind將在執(zhí)行結(jié)束時(shí)打印所使用的抑制列表。對(duì)于泄漏抑制,該輸出給出與抑制相匹配的不同丟失記錄的數(shù)量,以及抑制抑制的字節(jié)數(shù)和塊數(shù)。如果運(yùn)行包含多個(gè)泄漏檢查,則在每次新的泄漏檢查之前,字節(jié)和塊的數(shù)量將重置為零。請(qǐng)注意,不同的丟失記錄的數(shù)量不會(huì)重置為零。
在下面的示例中,在最后一次泄漏搜索中,7個(gè)塊和96個(gè)字節(jié)已被抑制,名稱為?some_leak_suppression:
--21041-- used_suppression:10 some_other_leak_suppression s.supp:14被抑制:1個(gè)塊中的12,400個(gè)字節(jié) --21041-- used_suppression:39 some_leak_suppression s.supp:2被抑制:7個(gè)塊中的96個(gè)字節(jié)對(duì)于ValueN和AddrN?錯(cuò)誤,調(diào)用上下文的第一行是發(fā)生錯(cuò)誤的函數(shù)的名稱,或者失敗的是.so包含錯(cuò)誤位置的文件或可執(zhí)行文件的完整路徑。對(duì)于Free錯(cuò)誤,第一行是函數(shù)做的釋放(例如,名字?free,__builtin_vec_delete等等)。為Overlap錯(cuò)誤,第一行是與重疊參數(shù)的函數(shù)的名稱(例如?memcpy,strcpy等)。
任何抑制的最后一部分指定需要匹配的調(diào)用上下文的其余部分。
4.5。Memcheck檢查機(jī)械的詳細(xì)信息
如果您想要詳細(xì)了解Memcheck正在檢查的內(nèi)容,請(qǐng)閱讀本部分。
4.5.1。有效值(V)位
考慮到Memcheck實(shí)現(xiàn)與真實(shí)CPU相同的合成CPU是最簡(jiǎn)單的,除了一個(gè)關(guān)鍵的細(xì)節(jié)。在實(shí)際CPU中處理,存儲(chǔ)和處理的數(shù)據(jù)的每一位(字面上)在合成CPU中具有相關(guān)聯(lián)的“有效值”位,其說明伴隨位是否具有合法值。在下面的討論中,該位被稱為V(有效值)位。
因此,系統(tǒng)中的每個(gè)字節(jié)都有一個(gè)8位,它隨處可見。例如,當(dāng)CPU從存儲(chǔ)器加載一個(gè)字大小的項(xiàng)目(4字節(jié))時(shí),它也會(huì)從位圖中加載相應(yīng)的32 V位,該位圖存儲(chǔ)過程'整個(gè)地址空間的V位。如果CPU應(yīng)該將該值的全部或部分內(nèi)容寫入不同地址的存儲(chǔ)器,則相關(guān)的V位將被存儲(chǔ)在V位位圖中。
簡(jiǎn)而言之,系統(tǒng)中的每一位都有(概念上)一個(gè)關(guān)聯(lián)的V位,即使在CPU內(nèi)部也隨處可見。是的,所有CPU的寄存器(整數(shù),浮點(diǎn),向量和條件寄存器)都有自己的V位向量。為了使這個(gè)工作,Memcheck使用大量的壓縮來緊密地表示V位。
復(fù)制值不會(huì)導(dǎo)致Memcheck檢查或報(bào)告錯(cuò)誤。但是,當(dāng)以可能影響程序的外部可見行為的方式使用值時(shí),將立即檢查相關(guān)的V位。如果其中任何一個(gè)表示該值未定義(甚至部分),則會(huì)報(bào)告錯(cuò)誤。
這是一個(gè)(絕對(duì)荒謬的)例子:
int i,j; int [10],b [10]; for(i = 0; i <10; i ++){j = a [i];b [i] = j; }Memcheck不會(huì)對(duì)此發(fā)出任何抱怨,因?yàn)樗鼉H將未初始化的值復(fù)制a[]到?b[]并且不會(huì)以影響程序行為的方式使用它們。但是,如果循環(huán)更改為:
for(i = 0; i <10; i ++){j + = a [i]; } if(j == 77) printf(“hello there \ n”);那么Memcheck會(huì)抱怨說,?if條件取決于未初始化的值。請(qǐng)注意,它不會(huì)抱怨j += a[i];,因?yàn)樵谀莻€(gè)時(shí)候,未定義不是“可觀察的”。只有當(dāng)printfMemcheck投訴時(shí),必須做出是否做出您的程序的可觀察的操作的決定。
大多數(shù)低級(jí)操作(如增加)使Memcheck使用V位作為操作數(shù)來計(jì)算結(jié)果的V位。即使結(jié)果部分或全部未定義,也不會(huì)抱怨。
對(duì)定義的檢查僅發(fā)生在三個(gè)位置:當(dāng)使用值來生成存儲(chǔ)器地址時(shí),需要進(jìn)行控制流決定時(shí),以及檢測(cè)到系統(tǒng)調(diào)用時(shí),Memcheck根據(jù)需要檢查參數(shù)的定義。
如果檢查應(yīng)檢測(cè)未定義,則會(huì)發(fā)出錯(cuò)誤消息。結(jié)果的值隨后被認(rèn)為是明確的。否則會(huì)給出長(zhǎng)鏈的錯(cuò)誤消息。換句話說,一旦Memcheck報(bào)告了一個(gè)未定義的值錯(cuò)誤,它會(huì)嘗試避免報(bào)告從相同的未定義值導(dǎo)出的進(jìn)一步錯(cuò)誤。
這聽起來過于復(fù)雜。為什么不檢查內(nèi)存中的所有讀數(shù),并抱怨如果未定義的值被加載到CPU寄存器中?那么這樣做并不好,因?yàn)橥耆戏ǖ腃程序通常會(huì)在內(nèi)存中復(fù)制未初始化的值,我們不希望無限的抱怨。這是典型的例子。考慮一個(gè)這樣的結(jié)構(gòu):
struct S {int x; char c }; 結(jié)構(gòu)S s1,s2; s1.x = 42; s1.c ='z'; s2 = s1;要問的問題是:struct S以字節(jié)為單位多大?一個(gè)int是4個(gè)字節(jié)和?char一個(gè)字節(jié),所以也許struct S占用5個(gè)字節(jié)?錯(cuò)誤。我們所知道的所有非玩具編譯器都將struct S整合大量的單詞,在這種情況下為8個(gè)字節(jié)。不這樣做會(huì)強(qiáng)制編譯器生成真正令人震驚的代碼來訪問?struct S某些架構(gòu)上的數(shù)組。
所以s1占用8個(gè)字節(jié),但只有5個(gè)字節(jié)被初始化。對(duì)于作業(yè)s2 = s1,GCC生成代碼,將所有8個(gè)字節(jié)批量復(fù)制到s2?不考慮其含義。如果Memcheck從內(nèi)存中簡(jiǎn)單地檢查了值,那么每當(dāng)這樣的結(jié)構(gòu)賦值發(fā)生時(shí),它就會(huì)變得啰嗦。所以上述更復(fù)雜的行為是必要的。這讓GCC復(fù)制?s1到s2它喜歡的任何方式,如果未初始化值以后要使用的警告才會(huì)發(fā)出。
4.5.2。有效地址(A)位
請(qǐng)注意,上一小節(jié)描述了如何建立和維護(hù)值的有效性,而無需說明程序是否具有訪問任何特定內(nèi)存位置的權(quán)限。我們現(xiàn)在考慮后一個(gè)問題。
如上所述,存儲(chǔ)器或CPU中的每一位具有相關(guān)聯(lián)的有效值(V)位。此外,內(nèi)存中的所有字節(jié),但不在CPU中,都具有相關(guān)的有效地址(A)位。這表示程序是否可以合法讀取或?qū)懭朐撐恢谩K槐硎驹谠撐恢玫臄?shù)據(jù)的有效性 - 這是V位的工作 - 只有位置是否可被訪問。
每次程序讀或?qū)憙?nèi)存時(shí),Memcheck會(huì)檢查與地址相關(guān)的A位。如果其中任何一個(gè)表示無效的地址,則會(huì)發(fā)出錯(cuò)誤。請(qǐng)注意,讀寫本身不改變A位,只能查閱它們。
那么A位如何設(shè)置/清除?喜歡這個(gè):
-
當(dāng)程序啟動(dòng)時(shí),所有的全局?jǐn)?shù)據(jù)區(qū)被標(biāo)記為可訪問。
-
當(dāng)程序執(zhí)行?malloc/?new時(shí),用于準(zhǔn)確分配區(qū)域的A位,而不是更多的字節(jié)被標(biāo)記為可訪問。釋放該區(qū)域后,A位被更改以指示不可訪問。
-
堆棧指針寄存器(SP)向上或向下移動(dòng)時(shí),A位置1。規(guī)則是從SP棧到底的區(qū)域?被標(biāo)記為可訪問,下面SP是不可訪問的。(如果這聽起來不合邏輯,請(qǐng)記住,幾乎所有的Unix系統(tǒng)(包括GNU / Linux),堆棧都在增長(zhǎng),而不是上升。)SP像這樣的跟蹤?具有一個(gè)有用的副作用,即本地功能的一部分堆棧變量等在函數(shù)輸入時(shí)自動(dòng)標(biāo)記可訪問,退出時(shí)無法訪問。
-
進(jìn)行系統(tǒng)調(diào)用時(shí),A位被適當(dāng)?shù)馗淖儭@?#xff0c;mmap?神奇地使文件出現(xiàn)在進(jìn)程的地址空間中,因此如果mmap?成功,必須更新A位。
-
或者,您的程序可以使用上述客戶端請(qǐng)求機(jī)制明確地告訴Memcheck這些更改。
4.5.3。把它們放在一起
Memcheck的檢查機(jī)械可概括如下:
-
存儲(chǔ)器中的每個(gè)字節(jié)具有8個(gè)關(guān)聯(lián)的V(有效值)位,表示該字節(jié)是否具有定義的值,以及一個(gè)A(有效地址)位,表示該程序當(dāng)前是否具有讀/寫那個(gè)地址。如上所述,大量使用壓縮意味著開銷通常在25%左右。
-
當(dāng)讀取或?qū)懭氪鎯?chǔ)器時(shí),可以查詢相關(guān)的A位。如果它們指示無效的地址,Memcheck會(huì)發(fā)出無效的讀取或無效的寫入錯(cuò)誤。
-
當(dāng)內(nèi)存讀入CPU的寄存器時(shí),相關(guān)的V位將從存儲(chǔ)器中讀取并存儲(chǔ)在模擬CPU中。沒有咨詢。
-
當(dāng)寄存器寫入存儲(chǔ)器時(shí),該寄存器的V位也被寫回存儲(chǔ)器。
-
當(dāng)CPU寄存器中的值用于生成存儲(chǔ)器地址或確定條件轉(zhuǎn)移的結(jié)果時(shí),將檢查這些值的V位,如果其中任何一個(gè)未定義,則發(fā)出錯(cuò)誤。
-
當(dāng)CPU寄存器中的值用于任何其他目的時(shí),Memcheck計(jì)算結(jié)果的V位,但不檢查它們。
-
一旦檢查了CPU中的值的V位,就將它們?cè)O(shè)置為指示有效性。這樣可以避免長(zhǎng)鏈錯(cuò)誤。
-
當(dāng)從內(nèi)存中加載值時(shí),Memcheck會(huì)檢查該位置的A位,并在需要時(shí)發(fā)出非法地址警告。在這種情況下,加載的V位將被強(qiáng)制指示有效,盡管位置無效。
這顯然奇怪的選擇減少了向用戶呈現(xiàn)的令人困惑的信息量。它避免了從不可尋址并包含無效值的地方讀取存儲(chǔ)器的不愉快現(xiàn)象,因此,您不僅可以獲得無效地址(讀/寫)錯(cuò)誤,還可以獲得一個(gè)潛在的大量未初始化值錯(cuò)誤,每次使用該值時(shí)都會(huì)出現(xiàn)一個(gè)錯(cuò)誤。
來自地址部分有效且部分無效的多字節(jié)加載有一個(gè)模糊的邊界情況。有關(guān)詳細(xì)信息,請(qǐng)參閱選項(xiàng)--partial-loads-ok的詳細(xì)信息。
MEMCHECK攔截來電malloc,?calloc,realloc,?valloc,memalign,?free,new,?new[],?delete和?delete[]。你得到的行為是:
-
malloc/?new/?new[]:返回存儲(chǔ)器被標(biāo)記為可尋址的但不具有有效的值。這意味著你必須寫信才能閱讀它。
-
calloc:返回的內(nèi)存標(biāo)記為可尋址和有效,因?yàn)閏alloc將該區(qū)域清除為零。
-
realloc:如果新的大小大于舊的,新的部分可尋址但無效,如同?malloc。如果新尺寸較小,則下降部分被標(biāo)記為不可尋址。你可能只傳給?realloc以前由malloc/?calloc/?發(fā)給你的指針?realloc。
-
free/?delete/?delete[]:你只能傳遞給這些函數(shù)一個(gè)指針,以前由相應(yīng)的分配函數(shù)發(fā)給你。否則,Memcheck抱怨。如果指針確實(shí)有效,則Memcheck將其指向的整個(gè)區(qū)域標(biāo)記為不可尋址,并將該塊置于自由塊隊(duì)列中。目的是推遲這個(gè)塊的重新分配。在這種情況下,所有嘗試訪問它將會(huì)引發(fā)無效的地址錯(cuò)誤,就像您所希望的那樣。
4.6。Memcheck監(jiān)視器命令
Memcheck工具提供由Valgrind內(nèi)置的gdbserver?處理的監(jiān)視器命令(請(qǐng)參閱Valgrind gdbserver的Monitor命令處理)。
-
xb <addr> [<len>]?顯示從<addr>開始的<len>(默認(rèn)為1)字節(jié)的定義(V)位和值。對(duì)于每8個(gè)字節(jié),輸出兩行。
第一行顯示8個(gè)字節(jié)的有效位。使用兩個(gè)十六進(jìn)制數(shù)字給出范圍內(nèi)每個(gè)字節(jié)的定義。這些十六進(jìn)制數(shù)字對(duì)相應(yīng)字節(jié)的每個(gè)位的有效性進(jìn)行編碼,如果定義了該位,則使用0,如果該位未定義則為1。如果一個(gè)字節(jié)不可尋址,則其有效位被__(雙下劃線)替代。
第二行顯示低于相應(yīng)有效位的字節(jié)值。用于顯示字節(jié)數(shù)據(jù)的格式與GDB命令'x / <len> xb <addr>'類似。不可尋址字節(jié)的值顯示為?(兩個(gè)問號(hào))。
在下面的示例中,string10是一個(gè)10個(gè)字符的數(shù)組,其中偶數(shù)字節(jié)未定義。在下面的例子中,對(duì)應(yīng)的字節(jié)string10[5]是不可尋址的。
(gdb)p&string10 $ 4 =(char(*)[10])0x804a2f0 (gdb)mo xb 0x804a2f0 10ff 00 ff 00 ff __ ff 00 0x804A2F0:0x3f 0x6e 0x3f 0x65 0x3f 0x ?? 0x3f 0x65ff 00 0x804A2F8:0x3f 0x00 地址0x804A2F0 len 10有1個(gè)字節(jié)不可尋址 (GDB)命令xb不能與寄存器一起使用。要獲取寄存器的有效位,您必須使用該選項(xiàng)啟動(dòng)Valgrind?--vgdb-shadow-registers=yes。然后可以通過打印相應(yīng)的“shadow 1”寄存器來獲得寄存器的有效位。在下面的x86示例中,寄存器eax具有未定義的所有位,而寄存器ebx被完全定義。
(gdb)p / x $ eaxs1 $ 9 = 0xffffffff (gdb)p / x $ ebxs1 $ 10 = 0x0 (GDB) -
get_vbits <addr> [<len>]?使用與xb命令相同的約定,顯示從<addr>開始的<len>(默認(rèn)1)字節(jié)的定義(V)位?。get_vbits僅顯示V位(按4字節(jié)分組)。它不顯示值。如果要將V位與相應(yīng)的字節(jié)值相關(guān)聯(lián),則?xb命令將更容易使用,特別是在將未定義的整數(shù)部分與其V位值相關(guān)聯(lián)時(shí),在小端計(jì)算機(jī)上。
下面的示例示出的結(jié)果get_vibts?對(duì)string10在所使用的?xb?命令的解釋。
(gdb)monitor get_vbits 0x804a2f0 10 ff00ff00 ff__ff00 ff00 地址0x804A2F0 len 10有1個(gè)字節(jié)不可尋址 (GDB) -
make_memory [noaccess|undefined|defined|Definedifaddressable] <addr> [<len>]將<addr>中的<len>(默認(rèn)為1)字節(jié)的范圍標(biāo)記為具有給定狀態(tài)。參數(shù)?noaccess將范圍標(biāo)記為不可訪問,所以Memcheck會(huì)報(bào)告任何訪問錯(cuò)誤。?undefined或defined將該區(qū)域標(biāo)記為可訪問的,但是Memcheck分別將其中的字節(jié)視為具有未定義或定義的值。?Definedifaddressable標(biāo)記為已定義的范圍內(nèi)的字節(jié),這些字節(jié)已經(jīng)可以被尋址,但不能改變不可尋址范圍內(nèi)的字節(jié)的狀態(tài)。請(qǐng)注意,第一個(gè)字母Definedifaddressable?是大寫D以避免混淆defined。
在下面的例子中,第一個(gè)字節(jié)?string10被標(biāo)記為定義:
(gdb)monitor make_memory定義0x8049e28 1 (gdb)monitor get_vbits 0x8049e28 10 0000ff00 ff00ff00 ff00 (GDB) -
check_memory [addressable|defined] <addr> [<len>]檢查<addr>中<len>(默認(rèn)為1)字節(jié)的范圍是否具有指定的可訪問性。然后輸出<addr>的描述。在以下示例中,可以使用詳細(xì)的描述,因?yàn)樵撨x項(xiàng)--read-var-info=yes在Valgrind啟動(dòng)時(shí)給出:
(gdb)monitor check_memory定義0x8049e28 1 地址0x8049E28 len 1定義 == 14698 ==位置0x8049e28是string10 [0]中的0個(gè)字節(jié), == 14698 ==在prog.c中聲明:10,在線程1的幀#0中 (GDB) -
leak_check [full*|summary] [kinds <set>|reachable|possibleleak*|definiteleak] [heuristics heur1,heur2,...] [increased*|changed|any] [unlimited*|limited <max_loss_records_output>]?執(zhí)行泄漏檢查。參數(shù)*中的參數(shù)表示默認(rèn)值。
如果[full*|summary]是?summary,則僅提供泄漏搜索的摘要;?否則將生成完整的泄漏報(bào)告。完整的泄漏報(bào)告提供了每個(gè)泄漏的詳細(xì)信息:泄漏的塊被分配的堆棧跟蹤,泄露的塊的數(shù)量及其總大小。當(dāng)請(qǐng)求完整的報(bào)告時(shí),接下來的兩個(gè)參數(shù)進(jìn)一步指定了什么樣的泄漏報(bào)告。如果匹配第二個(gè)參數(shù)和第三個(gè)參數(shù),則會(huì)顯示一個(gè)泄漏的詳細(xì)信息。完整的泄漏報(bào)告可能會(huì)輸出許多泄漏的詳細(xì)信息。輸出信息的nr可以使用limited隨后的最大nr個(gè)泄漏記錄輸出的參數(shù)進(jìn)行控制。如果達(dá)到此最大值,則泄漏搜索將輸出最大字節(jié)數(shù)的記錄。
該kinds參數(shù)控制了full泄漏搜索顯示哪些塊。可以使用<set>與命令行選項(xiàng)類似的方式指定要顯示的一組泄漏種類--show-leak-kinds。或者,該值definiteleak?相當(dāng)于kinds definite,該值possibleleak相當(dāng)于?kinds definite,possible:它還將顯示可能的泄漏塊,只有一個(gè)內(nèi)部指針被找到。該值reachable將顯示所有塊類別(即等效于kinds all)。
該heuristics參數(shù)控制在泄漏搜索期間使用的啟發(fā)式。可以使用<set>類似于命令行選項(xiàng)來指定要使用的啟發(fā)式集合--leak-check-heuristics。heuristics參數(shù)?的默認(rèn)值為heuristics none。
該[increased*|changed|any]參數(shù)控制對(duì)full泄漏搜索顯示哪些更改。該值increased指定僅顯示自上次泄漏檢查以來泄漏字節(jié)或塊數(shù)增加的塊分配堆棧。該值changed指定應(yīng)顯示自上次泄漏檢查以來發(fā)生任何更改的分配堆棧。該值any指定應(yīng)顯示所有泄漏條目,而不管任何增加或減少。當(dāng)指定increased或changed指定時(shí),泄漏報(bào)告條目將顯示相對(duì)于上一個(gè)泄漏報(bào)告的增量。
以下示例顯示leak_check了在memcheck/tests/leak-cases.c回歸測(cè)試中使用?monitor命令。第一個(gè)命令輸出一個(gè)具有增加的泄漏字節(jié)的條目。第二個(gè)命令與第一個(gè)命令相同,但使用GDB和Valgrind gdbserver接受的縮寫形式。它只輸出摘要信息,因?yàn)橐郧暗男孤┧阉鳑]有增加。
(gdb)monitor leak_check full possibleleak增加 在1(+1)個(gè)塊中的== 19520 == 16(+16)個(gè)字節(jié)可能在12的損失記錄中丟失 == 19520 == at 0x40070B4:malloc(vg_replace_malloc.c:263) == 19520 == by 0x80484D5:mk(leak-cases.c:52) == 19520 == by 0x804855F:f(leak-cases.c:81) == 19520 == by 0x80488E0:main(leak-cases.c:107) == == 19520 == 19520 == LEAK SUMMARY: == 19520 ==絕對(duì)丟失:2(+0)個(gè)塊中的32(+0)個(gè)字節(jié) == 19520 ==間接丟失:1(+0)個(gè)塊中的16(+0)個(gè)字節(jié) == 19520 ==可能丟失:2(+1)塊中的32(+16)個(gè)字節(jié) == 19520 ==仍然可達(dá):6(+1)個(gè)塊中的96(+16)個(gè)字節(jié) == 19520 == suppress:0(+0)個(gè)字節(jié)在0(+0)塊 == 19520 ==可以訪問的塊(找到指針的塊)未顯示。 == 19520 ==要查看它們,請(qǐng)將“可達(dá)到的任何”參數(shù)添加到leak_check == == 19520 (gdb)mo l == 19520 == LEAK SUMMARY: == 19520 ==絕對(duì)丟失:2(+0)個(gè)塊中的32(+0)個(gè)字節(jié) == 19520 ==間接丟失:1(+0)個(gè)塊中的16(+0)個(gè)字節(jié) == 19520 ==可能丟失:2(+0)個(gè)塊中的32(+0)個(gè)字節(jié) == 19520 ==仍然可達(dá):6(+0)個(gè)塊中的96(+0)個(gè)字節(jié) == 19520 == suppress:0(+0)個(gè)字節(jié)在0(+0)塊 == 19520 ==可以訪問的塊(找到指針的塊)未顯示。 == 19520 ==要查看它們,請(qǐng)將“可達(dá)到的任何”參數(shù)添加到leak_check == == 19520 (GDB)請(qǐng)注意,當(dāng)使用Valgrind的gdbserver時(shí),無需重新運(yùn)行--leak-check=full?--show-reachable=yes查看可訪問的塊。您可以通過使用GDB命令monitor leak_check full reachable any(或使用縮寫:)獲得相同的信息,而無需重新運(yùn)行mo l f r a。
-
block_list <loss_record_nr>|<loss_record_nr_from>..<loss_record_nr_to> [unlimited*|limited <max_blocks>] [heuristics heur1,heur2,...]?顯示屬于<loss_record_nr>(或損失記錄范圍?<loss_record_nr_from>..<loss_record_nr_to>)的塊列表?。可以使用limited參數(shù)后跟最大nr個(gè)塊來輸出要打印?的塊的nr。如果給出一個(gè)或多個(gè)啟發(fā)式方法,則僅打印通過給定heur1,heur2,...?啟發(fā)式之一找到的損失記錄和塊。
泄漏搜索將丟失記錄中的已分配塊合并:丟失記錄重新分組具有相同狀態(tài)的所有塊(例如,絕對(duì)丟失)和相同的分配回溯。每個(gè)損失記錄在泄漏搜索結(jié)果中由損失記錄號(hào)識(shí)別。該block_list命令顯示損失記錄信息,后面跟隨損失記錄中合并的塊的地址和大小。如果使用啟發(fā)式方法找到一個(gè)塊,則塊大小后面是啟發(fā)式的。
如果直接丟失的塊導(dǎo)致一些其他塊間接丟失,則block_list命令還將顯示這些間接丟失的塊。根據(jù)直接丟失塊和間接丟失塊之間的間接級(jí)別,間接丟失的塊將被縮進(jìn)。每個(gè)間接丟失的塊后面都是其損失記錄的引用。
只要在這種泄漏搜索之后沒有塊被釋放,就可以使用block_list命令對(duì)泄漏搜索結(jié)果:一旦程序釋放一個(gè)塊,在再次使用block_list之前需要新的泄漏搜索。
在下面的示例中,程序通過丟失指向塊A(樹頂部)的指針來泄漏樹結(jié)構(gòu)。因此,塊A直接丟失,導(dǎo)致塊B到G的間接丟失。第一個(gè)block_list命令顯示A(一個(gè)絕對(duì)丟失的塊,地址為0x4028028,大小為16)的丟失記錄。塊A的間接丟失塊的地址和大小顯示在塊A的下方。第二個(gè)命令顯示第一個(gè)命令輸出的間接損失記錄之一的細(xì)節(jié)。
一個(gè)/ \公元前/ \ / \ DEFG (gdb)bt #0 main()在leak-tree.c:69 (gdb)monitor leak_check full any == 19552 == 112(16個(gè)直接,96個(gè)間接)1個(gè)塊中的字節(jié)在7的損失記錄中絕對(duì)丟失 == 19552 ==在0x40070B4:malloc(vg_replace_malloc.c:263) == 19552 == 0x80484D5:mk(leak-tree.c:28) == 19552 == by 0x80484FC:f(leak-tree.c:41) == 19552 == by 0x8048856:main(leak-tree.c:63) == == 19552 == 19552 == LEAK SUMMARY: == 19552 ==絕對(duì)丟失:1個(gè)塊中的16個(gè)字節(jié) == 19552 ==間接丟失:6個(gè)塊中的96個(gè)字節(jié) == 19552 ==可能丟失:0個(gè)字節(jié),0個(gè)塊 == 19552 ==仍然可達(dá):0個(gè)字節(jié),0個(gè)塊 == 19552 == suppress:0個(gè)字節(jié),0個(gè)塊 == == 19552 (gdb)monitor block_list 7 == 19552 == 112(16個(gè)直接,96個(gè)間接)1個(gè)塊中的字節(jié)在7的損失記錄中絕對(duì)丟失 == 19552 ==在0x40070B4:malloc(vg_replace_malloc.c:263) == 19552 == 0x80484D5:mk(leak-tree.c:28) == 19552 == by 0x80484FC:f(leak-tree.c:41) == 19552 == by 0x8048856:main(leak-tree.c:63) == 19552 == 0x4028028 [16] == 19552 == 0x4028068 [16]間接損失記錄1 == 19552 == 0x40280E8 [16]間接損失記錄3 == 19552 == 0x4028128 [16]間接損失記錄4 == 19552 == 0x40280A8 [16]間接損失記錄2 == 19552 == 0x4028168 [16]間接損失記錄5 == 19552 == 0x40281A8 [16]間接損失記錄6 (gdb)mo b 2 == 19552 == 1個(gè)塊中的16個(gè)字節(jié)在7的損失記錄中間接丟失 == 19552 ==在0x40070B4:malloc(vg_replace_malloc.c:263) == 19552 == 0x80484D5:mk(leak-tree.c:28) == 19552 == by 0x8048519:f(leak-tree.c:43) == 19552 == by 0x8048856:main(leak-tree.c:63) == 19552 == 0x40280A8 [16] == 19552 == 0x4028168 [16]間接損失記錄5 == 19552 == 0x40281A8 [16]間接損失記錄6 (GDB) -
who_points_at <addr> [<len>]?顯示找到指向addr的指針的所有位置。如果len等于1,則該命令僅顯示正好位于addr的位置(即“add指針”到addr)。如果len> 1,指示len第一個(gè)字節(jié)的“內(nèi)部指針”也將顯示。
搜索的位置與泄漏搜索中使用的位置相同。所以,who_points_at可以用來顯示為什么泄漏搜索仍然可以到達(dá)一個(gè)塊,或者可以搜索懸浮指針到一個(gè)被釋放的塊。將描述指向addr的每個(gè)位置(或指向內(nèi)部addr,如果正在搜索內(nèi)部指針)。
在下面的示例中,block_list在樹被泄露之前顯示了指向“樹塊A”(參見命令中的示例)的指針。說明詳見--read-var-info=yes?Valgrind啟動(dòng)時(shí)的選項(xiàng)。第二個(gè)調(diào)用顯示塊G的指針(起始和內(nèi)部指針)。塊G(0x40281A8)可通過塊C(0x40280a8)和寄存器ECX為tid 1(tid為Valgrind線程ID)到達(dá)。通過注冊(cè)EBX“內(nèi)部可達(dá)”。
(gdb)monitor who_points_at 0x4028028 == 20852 ==搜索指向0x4028028的指針 == 20852 == * 0x8049e20分在0x4028028 == 20852 ==位置0x8049e20是全局var“t”內(nèi)的0個(gè)字節(jié) == 20852 ==在leak-tree.c中聲明:35 (gdb)monitor who_points_at 0x40281A8 16 == 20852 ==搜索從0x40281a8指向16個(gè)字節(jié)的指針 == 20852 == * 0x40280ac指向0x40281a8 == 20852 ==地址0x40280ac是一個(gè)大小為16個(gè)alloc'd的塊內(nèi)的4個(gè)字節(jié) == 20852 == 0x40070B4:malloc(vg_replace_malloc.c:263) == 20852 == 0x80484D5:mk(leak-tree.c:28) == 20852 == 0x8048519:f(leak-tree.c:43) == 20852 == by 0x8048856:main(leak-tree.c:63) == 20852 == tid 1注冊(cè)ECX分?jǐn)?shù)為0x40281a8 == 20852 == tid 1注冊(cè)EBX內(nèi)部點(diǎn)在2個(gè)字節(jié)內(nèi)部0x40281a8 (GDB)當(dāng)who_points_at找到一個(gè)內(nèi)部指針時(shí),它會(huì)報(bào)告這個(gè)內(nèi)部指針被認(rèn)為是可達(dá)到的啟發(fā)式的。請(qǐng)注意,這是獨(dú)立于該選項(xiàng)的值完成的--leak-check-heuristics。在下面的示例中,丟失記錄6指示可能丟失的塊。who_points_at報(bào)告指出內(nèi)部指針指向該塊,并且該塊可以被認(rèn)為是可以使用啟發(fā)式的multipleinheritance。
(gdb)monitor block_list 6 == 3748 == 1個(gè)塊中的8個(gè)字節(jié)可能在7的損失記錄6中丟失 == 3748 == at 0x4007D77:operator new(unsigned int)(vg_replace_malloc.c:313) == 3748 == 0x8048954:main(leak_cpp_interior.cpp:43) == 3748 == 0x402A0E0 [8] (gdb)monitor who_points_at 0x402A0E0 8 == 3748 ==搜索指向0x402a0e0的8個(gè)字節(jié)的指針 == 3748 == * 0xbe8ee078內(nèi)部點(diǎn)4個(gè)字節(jié)內(nèi)部0x402a0e0 == 3748 ==地址0xbe8ee078在線程1的堆棧 == 3748 == block at 0x402a0e0認(rèn)為可以通過ptr 0x402a0e4使用多重繼承啟發(fā)式 (GDB)
4.7。客戶端請(qǐng)求
下面定義了以下客戶端請(qǐng)求?memcheck.h。看到memcheck.h他們的參數(shù)的確切細(xì)節(jié)。
-
VALGRIND_MAKE_MEM_NOACCESS,?VALGRIND_MAKE_MEM_UNDEFINED和?VALGRIND_MAKE_MEM_DEFINED。這些標(biāo)記地址范圍完全不可訪問,可訪問但包含未定義的數(shù)據(jù),并且可訪問和包含定義的數(shù)據(jù)。在Valgrind運(yùn)行時(shí)返回-1,否則返回0。
-
VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE。這就像VALGRIND_MAKE_MEM_DEFINED只是影響那些已經(jīng)可尋址的字節(jié)。
-
VALGRIND_CHECK_MEM_IS_ADDRESSABLE并且?VALGRIND_CHECK_MEM_IS_DEFINED:立即檢查給定的地址范圍是否具有相關(guān)屬性,如果沒有,請(qǐng)打印錯(cuò)誤消息。此外,為了方便客戶,如果相關(guān)屬性成立,則返回零;?否則,返回的值是屬性不為真的第一個(gè)字節(jié)的地址。在Valgrind不運(yùn)行時(shí)始終返回0。
-
VALGRIND_CHECK_VALUE_IS_DEFINED:一個(gè)快速簡(jiǎn)便的方法來查明Valgrind是否認(rèn)為一個(gè)特定的值(左值,準(zhǔn)確)是可尋址和定義的。打印錯(cuò)誤信息,如果沒有。它沒有返回值。
-
VALGRIND_DO_LEAK_CHECK:現(xiàn)在做一個(gè)完整的內(nèi)存泄漏檢查(像--leak-check=full)。這對(duì)于逐步檢查程序執(zhí)行中任意位置之間的泄漏是非常有用的。它沒有返回值。
-
VALGRIND_DO_ADDED_LEAK_CHECK:與?VALGRIND_DO_LEAK_CHECK以前的泄漏搜索相同?但僅顯示泄漏字節(jié)增加或塊數(shù)泄漏的條目。它沒有返回值。
-
VALGRIND_DO_CHANGED_LEAK_CHECK:與VALGRIND_DO_LEAK_CHECK以前的泄漏搜索相同?但僅顯示泄露字節(jié)增加或減少的條目或泄漏的塊數(shù)。它沒有返回值。
-
VALGRIND_DO_QUICK_LEAK_CHECK:喜歡?VALGRIND_DO_LEAK_CHECK,除了它只產(chǎn)生泄漏匯總(如--leak-check=summary)。它沒有返回值。
-
VALGRIND_COUNT_LEAKS:填寫四個(gè)參數(shù),其中先前泄漏檢查發(fā)現(xiàn)的內(nèi)存字節(jié)數(shù)(即直接泄漏和間接泄漏的總和),可疑,可達(dá)到并被抑制。這在調(diào)用VALGRIND_DO_LEAK_CHECK或?之后的測(cè)試線索代碼中很有用VALGRIND_DO_QUICK_LEAK_CHECK。
-
VALGRIND_COUNT_LEAK_BLOCKS:?VALGRIND_COUNT_LEAKS除了它返回塊數(shù)而不是每個(gè)類別中的字節(jié)數(shù)除外。
-
VALGRIND_GET_VBITS并且?VALGRIND_SET_VBITS:允許您獲取并設(shè)置地址范圍的V(有效性)位。你應(yīng)該只設(shè)置你所擁有的V位?VALGRIND_GET_VBITS。只有那些真正了解他們?cè)谧鍪裁吹娜瞬艜?huì)。
-
VALGRIND_CREATE_BLOCK和?VALGRIND_DISCARD。?VALGRIND_CREATE_BLOCK?取一個(gè)地址,一個(gè)字節(jié)和一個(gè)字符串。然后指定的地址范圍與該字符串相關(guān)聯(lián)。當(dāng)Memcheck報(bào)告對(duì)該范圍內(nèi)的地址的無效訪問時(shí),它將根據(jù)該塊而不是根據(jù)其知道的任何其他塊來描述它。請(qǐng)注意,這個(gè)宏的使用實(shí)際上并不會(huì)以任何方式改變內(nèi)存的狀態(tài) - 它只是給出范圍的名稱。
在某些時(shí)候,您可能希望Memcheck停止報(bào)告錯(cuò)誤VALGRIND_CREATE_BLOCK。為了實(shí)現(xiàn)這一點(diǎn),VALGRIND_CREATE_BLOCK返回一個(gè)“塊句柄”,它是一個(gè)C?int值。您可以將此塊句柄傳遞給VALGRIND_DISCARD。在這樣做之后,Valgrind將不再將指定范圍內(nèi)的尋址錯(cuò)誤與塊相關(guān)聯(lián)。傳遞無效手柄?VALGRIND_DISCARD是無害的。
4.8。內(nèi)存池:描述和使用自定義分配器
有些程序通常使用自定義內(nèi)存分配器,出于性能原因。留給自己,Memcheck無法理解自定義分配方案的行為,并且它了解標(biāo)準(zhǔn)分配器,因此可能會(huì)錯(cuò)過您的程序中的錯(cuò)誤和泄漏。本節(jié)描述的是一種給予Memcheck足夠的自定義分配器的描述的方法,它可以使至少有一些感覺發(fā)生了什么。
有許多不同種類的自定義分配器,所以Memcheck嘗試使用松散的抽象模型來推理它們。描述自定義分配系統(tǒng)時(shí),我們使用以下術(shù)語:
-
自定義分配涉及一組獨(dú)立的“內(nèi)存池”。
-
Memcheck對(duì)內(nèi)存池的概念由一個(gè)“錨地址”和一組與錨地址相關(guān)聯(lián)的非重疊“塊”組成。
-
通常,池的錨地址是保存“標(biāo)題”結(jié)構(gòu)的地址。
-
典型地,該池的塊被從通過該系統(tǒng)獲得的連續(xù)的“超級(jí)塊”拉伸?malloc或?mmap。
請(qǐng)記住,上面的最后兩點(diǎn)表示“通常”:Valgrind mempool客戶端請(qǐng)求API對(duì)于mempool的確切結(jié)構(gòu)有意識(shí)地模糊。沒有具體提及標(biāo)題或超級(jí)塊。然而,以下圖片可能有助于闡明API中術(shù)語的意圖:
“池子”(錨地址)|v+ -------- + --- +| 標(biāo)題| o |+ -------- + - | - +|v超級(jí)塊+ ------ + --- + -------------- + --- + ------------------ +| | RZB | 分配| rzB | |+ ------ + --- + -------------- + --- + ------------------ +^ ^| |“addr”“addr”+“size”注意,報(bào)頭和超級(jí)塊可以是連續(xù)的或不連續(xù)的,并且可能存在與單個(gè)報(bào)頭相關(guān)聯(lián)的多個(gè)超級(jí)塊;?這種變化對(duì)于Memcheck是不透明的。API只要求您的分配方案可以顯示“pool”,“addr”和“size”的合理值。
通常,在發(fā)出與mempools相關(guān)的客戶端請(qǐng)求之前,客戶端程序?qū)槠鋗empool分配這樣的頭和超級(jí)塊,并使用VALGRIND_MAKE_MEM_NOACCESS客戶端請(qǐng)求標(biāo)記超級(jí)塊NOACCESS?。
在處理mempools時(shí),目標(biāo)是保持一個(gè)特定的不變條件:Memcheck相信池的超級(jí)塊(包括redzones)的未分配部分是NOACCESS。為了維護(hù)這個(gè)不變量,客戶端程序必須確保超級(jí)塊在那個(gè)狀態(tài)下開始;?Memcheck不能這樣做,因?yàn)镸emcheck從來沒有明確地了解一個(gè)池的超級(jí)塊,只有池中已分配的塊。
一旦池的頭和超級(jí)塊被建立并被正確標(biāo)記,有許多客戶端請(qǐng)求程序可以用來通知Memcheck關(guān)于對(duì)mempool的狀態(tài)的改變:
-
VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed):該請(qǐng)求將地址pool注冊(cè)為內(nèi)存池的錨地址。它還提供了一個(gè)大小rzB,指定從池分配的塊周圍放置的redzones?的大小?。最后,它提供了一個(gè)?is_zeroed參數(shù),指定在分配時(shí)池的塊是否為零(更精確地:定義)。
完成此請(qǐng)求后,不會(huì)與池相關(guān)聯(lián)。該請(qǐng)求簡(jiǎn)單地告訴Memcheck池存在,以便后續(xù)調(diào)用可以將其稱為池。
-
VALGRIND_CREATE_MEMPOOL_EXT(pool, rzB, is_zeroed, flags):創(chuàng)建具有一些標(biāo)志的內(nèi)存池(可以一起OR-ed)指定擴(kuò)展行為。當(dāng)標(biāo)志為零時(shí),行為是相同的?VALGRIND_CREATE_MEMPOOL。
-
該標(biāo)志VALGRIND_MEMPOOL_METAPOOL?指定與使用的池相關(guān)聯(lián)的內(nèi)存塊VALGRIND_MEMPOOL_ALLOC將被應(yīng)用程序用作超級(jí)塊以使用MALLOC_LIKE塊VALGRIND_MALLOCLIKE_BLOCK。換句話說,元池是一個(gè)“2級(jí)”池:第一級(jí)是由...描述的塊VALGRIND_MEMPOOL_ALLOC。使用第二級(jí)塊進(jìn)行描述VALGRIND_MALLOCLIKE_BLOCK。請(qǐng)注意,池和第二級(jí)塊之間的關(guān)聯(lián)是隱式的:第二級(jí)塊將位于第一級(jí)塊內(nèi)。有必要VALGRIND_MEMPOOL_METAPOOL對(duì)這樣的2級(jí)池使用標(biāo)志,否則valgrind將檢測(cè)到重疊的內(nèi)存塊,并將中止執(zhí)行(例如
-
VALGRIND_MEMPOOL_AUTO_FREE。這樣的元池也可以使用標(biāo)志來標(biāo)記為“自動(dòng)免費(fèi)”池,該標(biāo)志VALGRIND_MEMPOOL_AUTO_FREE必須與...一起使用VALGRIND_MEMPOOL_METAPOOL。對(duì)于“自動(dòng)免費(fèi)”池,VALGRIND_MEMPOOL_FREE?將自動(dòng)釋放第一個(gè)級(jí)別塊中包含的第二級(jí)別塊VALGRIND_MEMPOOL_FREE。換句話說,調(diào)用VALGRIND_MEMPOOL_FREE將對(duì)VALGRIND_FREELIKE_BLOCK包含在第一級(jí)塊中的所有第二級(jí)塊產(chǎn)生隱式調(diào)用。注意:使用VALGRIND_MEMPOOL_AUTO_FREE沒有VALGRIND_MEMPOOL_METAPOOL標(biāo)志的標(biāo)志?是一個(gè)錯(cuò)誤。
-
-
VALGRIND_DESTROY_MEMPOOL(pool):此請(qǐng)求告訴Memcheck池已被拆除。Memcheck然后刪除與池相關(guān)聯(lián)的所有塊的記錄,以及其存儲(chǔ)池的記錄。在破壞其記憶的記錄的同時(shí),Memcheck將池中任何活塊的redzones重置為NOACCESS。
-
VALGRIND_MEMPOOL_ALLOC(pool, addr, size):此請(qǐng)求通知Memcheck?size已經(jīng)分配了一個(gè)字節(jié)塊addr,并將該組合與指定的塊相關(guān)聯(lián)?pool。如果池是用非零rzB紅色區(qū)域創(chuàng)建的?,Memcheck會(huì)將rzB塊之前和之后的字節(jié)標(biāo)記?為NOACCESS。如果使用is_zeroed參數(shù)集創(chuàng)建池,Memcheck會(huì)將該塊標(biāo)記為DEFINED,否則Memcheck會(huì)將該塊標(biāo)記為UNDEFINED。
-
VALGRIND_MEMPOOL_FREE(pool, addr):此請(qǐng)求通知Memcheck,該塊addr?不應(yīng)被視為已分配。Memcheck將標(biāo)記與NOACCESS相關(guān)聯(lián)的塊addr,并刪除其塊的存在記錄。
-
VALGRIND_MEMPOOL_TRIM(pool, addr, size):此請(qǐng)求修剪與之相關(guān)聯(lián)的塊pool。該請(qǐng)求僅對(duì)與之相關(guān)的塊進(jìn)行操作?pool。修剪正式定義為:
-
整個(gè)范圍內(nèi)的所有塊?addr..(addr+size-1)都被保留。
-
完全超出范圍的所有塊?addr..(addr+size-1)都被丟棄,就像?VALGRIND_MEMPOOL_FREE被稱為它們一樣。
-
所有其他塊必須與范圍相交?addr..(addr+size-1);?十字路口以外的區(qū)域被標(biāo)記為“非空”,就像它們已被獨(dú)立釋放一樣?VALGRIND_MEMPOOL_FREE。
這是一個(gè)很少見的請(qǐng)求,但可以在實(shí)現(xiàn)常規(guī)LIFO分配器中常見的無大規(guī)模操作的類型中有用。
-
-
VALGRIND_MOVE_MEMPOOL(poolA, poolB):此請(qǐng)求通知Memcheck以前錨定在地址的池poolA已移動(dòng)到錨地址?poolB。這是一個(gè)罕見的請(qǐng)求,通常只有當(dāng)您realloc的回憶標(biāo)題時(shí)才需要。
此請(qǐng)求不會(huì)更改內(nèi)存狀態(tài)位。
-
VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size):該請(qǐng)求通知MEMCHECK先前在地址分配的組塊addrA內(nèi)的?pool已被移動(dòng)和/或調(diào)整大小,應(yīng)加以改變,以覆蓋該區(qū)域addrB..(addrB+size-1)。這是一個(gè)罕見的請(qǐng)求,通常只有realloc在超級(jí)塊或希望擴(kuò)展塊而不改變其內(nèi)存狀態(tài)位時(shí)才需要?。
此請(qǐng)求不會(huì)更改內(nèi)存狀態(tài)位。
-
VALGRIND_MEMPOOL_EXISTS(pool):該請(qǐng)求通知呼叫者M(jìn)emcheck是否正在錨定地址處追蹤回憶pool。當(dāng)與該地址相關(guān)聯(lián)的mempool時(shí),它評(píng)估為1,否則為0。這是一個(gè)罕見的請(qǐng)求,僅在客戶端代碼可能丟失了一組活動(dòng)mempools的情況下才有用。
4.9。使用Valgrind調(diào)試MPI并行程序
Memcheck支持使用MPI消息傳遞標(biāo)準(zhǔn)的分布式內(nèi)存應(yīng)用程序的調(diào)試。該支持包括用于該P(yáng)MPI_*接口的包裝函數(shù)庫?。當(dāng)加入到應(yīng)用程序的地址空間中,無論是直接鏈接或通過?LD_PRELOAD,該包裝攔截來電PMPI_Send,?PMPI_Recv等他們?cè)偈褂每蛻舳说恼?qǐng)求,通知所造成的功能被包裝內(nèi)存狀態(tài)變化MEMCHECK。這減少了Memcheck通常為MPI應(yīng)用報(bào)告的誤報(bào)數(shù)量。
包裝器還利用機(jī)會(huì)仔細(xì)檢查作為參數(shù)傳遞給MPI函數(shù)的緩沖區(qū)的大小和定義,從而檢測(cè)錯(cuò)誤,例如將未定義的數(shù)據(jù)傳遞到?PMPI_Send或?qū)?shù)據(jù)接收到太小的緩沖區(qū)中。
與大多數(shù)其他Valgrind不同,包裝庫受BSD風(fēng)格許可,因此您可以將其鏈接到任何您喜歡的代碼庫。查看mpi/libmpiwrap.c?許可證詳細(xì)信息的頂部。
4.9.1。建造和安裝包裝紙
如果可能,將自動(dòng)構(gòu)建包裝庫。Valgrind的配置腳本將尋找適合?mpicc于構(gòu)建它。這必須與mpicc您用來構(gòu)建您要調(diào)試的MPI應(yīng)用程序相同。默認(rèn)情況下,Valgrind嘗試?mpicc,但您可以使用configure-time選項(xiàng)指定一個(gè)不同的?--with-mpicc。目前,封裝器只能使用mpicc基于GNU GCC或Intel C ++編譯器的s來構(gòu)建?。
檢查配置腳本是否打印如下所示的行:
檢查可用的MPI2兼容mpicc和mpi.h ...是的,mpicc如果說... no,您的?mpicc編譯和鏈接測(cè)試MPI2程序失敗。
如果配置測(cè)試成功,繼續(xù)與通常的方式?make和make install。最后的安裝樹應(yīng)該包含?libmpiwrap-<platform>.so。
匯編測(cè)試MPI程序(例如,MPI hello-world),并嘗試:
LD_PRELOAD = $ prefix / lib / valgrind / libmpiwrap- <platform> .so \mpirun [args] $ prefix / bin / valgrind ./hello你應(yīng)該看到類似于以下內(nèi)容
valgrind MPI包裝紙31901:適用于pid 31901 valgrind MPI包裝器31901:嘗試MPIWRAP_DEBUG =幫助可能的選項(xiàng)對(duì)于組中的每個(gè)過程重復(fù)。如果沒有看到這些,有一種構(gòu)建/安裝問題。
要包裝的MPI函數(shù)被假定為具有soname匹配的ELF共享對(duì)象?libmpi.so*。至少對(duì)于Open MPI和Quadrics MPI來說,這是正確的,如果需要,可以輕松更改。
4.9.2。入門
像往常一樣編譯MPI應(yīng)用程序,注意使用與mpicc您的Valgrind構(gòu)建配置相同的鏈接。
使用以下基本方案在Valgrind上運(yùn)行您的應(yīng)用程序:
MPIWRAP_DEBUG = [wrapper-args] \LD_PRELOAD = $ prefix / lib / valgrind / libmpiwrap- <platform> .so \mpirun [mpirun-args] \$ prefix / bin / valgrind [valgrind-args] \[應(yīng)用程序] [app-args]作為一種替代?LD_PRELOADING?libmpiwrap-<platform>.so,你可以簡(jiǎn)單地把它如果需要鏈接到您的應(yīng)用程序。這不應(yīng)該以任何方式打擾您的應(yīng)用程序的本機(jī)行為。
4.9.3。控制包裝庫
環(huán)境變量?MPIWRAP_DEBUG在啟動(dòng)時(shí)被查閱。默認(rèn)行為是打印起始橫幅
valgrind MPI包裝器16386:適用于pid 16386 valgrind MPI包裝器16386:嘗試MPIWRAP_DEBUG =幫助可能的選項(xiàng)然后比較安靜。
您可以在其中列出逗號(hào)分隔的選項(xiàng)?MPIWRAP_DEBUG。這些是
-
verbose:顯示所有包裝的條目/出口。還顯示額外的調(diào)試信息,如優(yōu)秀的狀態(tài)?MPI_Request期從半拉造成MPI_Irecv秒。
-
quiet:相反verbose,當(dāng)打包機(jī)想要報(bào)告檢測(cè)到的編程錯(cuò)誤,或者在包裝器發(fā)生災(zāi)難性故障的情況下,只打印任何東西。
-
warn:默認(rèn)情況下,缺少適當(dāng)?shù)陌b器的功能沒有評(píng)論,只是默默地忽略。這將導(dǎo)致每個(gè)使用的展開功能打印一個(gè)警告,每個(gè)功能最多可以有三個(gè)警告。
-
strict:如果使用缺少包裝器的功能,則打印錯(cuò)誤消息并中止程序。
如果要使用Valgrind的XML輸出工具(--xml=yes),您應(yīng)該傳入?quiet,?MPIWRAP_DEBUG以便從包裝器中除去任何無關(guān)的打印。
4.9.4。功能
所有MPI2功能除外?MPI_Wtick,?MPI_Wtime并?MPI_Pcontrol有包裝。前兩個(gè)沒有包裝,因?yàn)樗鼈兎祷匾粋€(gè)?double,Valgrind的函數(shù)包裝機(jī)制無法處理(但是可以很容易地?cái)U(kuò)展)。?MPI_Pcontrol不可包裹,因?yàn)樗哂锌勺兲匦?#xff1a;?int MPI_Pcontrol(const int level, ...)
大多數(shù)函數(shù)都使用一個(gè)默認(rèn)的包裝器來打包,除非是根據(jù)MPIWRAP_DEBUG上面列出的設(shè)置調(diào)用,否則它不會(huì)執(zhí)行任何命令,也不會(huì)中止它。以下功能具有“真實(shí)”,有用的包裝器:
PMPI_Send PMPI_Bsend PMPI_Ssend PMPI_RsendPMPI_Recv PMPI_Get_countPMPI_Isend PMPI_Ibsend PMPI_Issend PMPI_IrsendPMPI_Irecv PMPI_Wait PMPI_Waitall PMPI_Test PMPI_TestallPMPI_Iprobe PMPI_ProbePMPI_CancelPMPI_SendrecvPMPI_Type_commit PMPI_Type_freePMPI_Pack PMPI_UnpackPMPI_Bcast PMPI_Gather PMPI_Scatter PMPI_Alltoall PMPI_Reduce PMPI_Allreduce PMPI_Op_createPMPI_Comm_create PMPI_Comm_dup PMPI_Comm_free PMPI_Comm_rank PMPI_Comm_sizePMPI_Error_string PMPI_Init PMPI_Initialized PMPI_FinalizePMPI_Address列出的?幾個(gè)功能?HAS_NO_WRAPPER。他們根本沒有包裝,因?yàn)闆]有什么值得檢查,給出一個(gè)無操作的包裝器會(huì)降低性能沒有任何理由。
請(qǐng)注意,包裝庫本身本身可以產(chǎn)生大量對(duì)MPI實(shí)現(xiàn)的調(diào)用,特別是在步行復(fù)雜類型時(shí)。所謂的最常用的功能是?PMPI_Extent,?PMPI_Type_get_envelope,?PMPI_Type_get_contents,和?PMPI_Type_free。
4.9.5。類型
支持MPI-1.1結(jié)構(gòu)化類型,并正確運(yùn)行。目前支持的組合是?MPI_COMBINER_NAMED,?MPI_COMBINER_CONTIGUOUS,?MPI_COMBINER_VECTOR,?MPI_COMBINER_HVECTOR?MPI_COMBINER_INDEXED,?MPI_COMBINER_HINDEXED和?MPI_COMBINER_STRUCT。這應(yīng)該涵蓋所有MPI-1.1類型。該機(jī)制(功能?walk_type)應(yīng)易于擴(kuò)展以覆蓋MPI2組合器。
MPI定義了一些命名結(jié)構(gòu)化類型(MPI_FLOAT_INT,?MPI_DOUBLE_INT,?MPI_LONG_INT,?MPI_2INT,?MPI_SHORT_INT,?MPI_LONG_DOUBLE_INT),其是對(duì)一些基本類型和C的int。不幸的是,MPI規(guī)范使得不可能查看這些類型并查看字段在哪里。因此,這些包裝器假設(shè)類型被布置為struct { float val; int loc; }(for?MPI_FLOAT_INT)等,并且相應(yīng)地進(jìn)行操作。至少對(duì)于Open MPI 1.0.2和Quadrics MPI來說,這似乎是正確的。
如果strict是指定的選項(xiàng)MPIWRAP_DEBUG,如果遇到未處理類型,應(yīng)用程序?qū)⒅兄埂7駝t,應(yīng)用程序?qū)⒋蛴∫粭l警告消息并繼續(xù)。
做了一些努力來在單次通過中標(biāo)記/檢查對(duì)應(yīng)于數(shù)組數(shù)組的存儲(chǔ)器范圍。這對(duì)于性能很重要,因?yàn)閂algrind要求標(biāo)記/檢查任何范圍,無論多么小,承載相當(dāng)大的恒定成本。這種優(yōu)化被施加到原始類型的陣列(double,?float,?int,?long,long long,short,?char,和long double上平臺(tái)其中sizeof(long double) == 8)。對(duì)于所有其他類型的數(shù)組,包裝器單獨(dú)處理每個(gè)元素,因此可能會(huì)有非常大的性能成本。
4.9.6。寫新的包裝紙
在大多數(shù)情況下,包裝很簡(jiǎn)單。唯一重要的復(fù)雜性是出現(xiàn)在非阻塞接收。
問題是MPI_Irecv?聲明recv緩沖區(qū)并立即返回,為事務(wù)提供一個(gè)handle(MPI_Request)。稍后用戶將必須輪詢完成?MPI_Wait等等,并且當(dāng)事務(wù)成功完成時(shí),包裝器必須繪制recv緩沖區(qū)。但是recv緩沖區(qū)細(xì)節(jié)沒有呈現(xiàn)給?MPI_Wait- 只有句柄是。因此,庫保持一個(gè)影子表,它將未完成的MPI_Requests與相應(yīng)的緩沖區(qū)地址/計(jì)數(shù)/類型相關(guān)聯(lián)。操作完成后,將搜索表格中相關(guān)聯(lián)的地址/計(jì)數(shù)/類型信息,并相應(yīng)地標(biāo)記內(nèi)存。
訪問表受到(POSIX pthreads)鎖的保護(hù),以使庫線程安全。
該表被分配?malloc并且從不?freed,所以它將顯示在泄漏檢查中。
寫新的包裝器應(yīng)該是相當(dāng)容易的。源文件是?mpi/libmpiwrap.c。如果可能,找到一個(gè)與要包裝的行為類似行為的函數(shù)的現(xiàn)有包裝,并將其用作起點(diǎn)。包裝器按照與MPI 1.1規(guī)范相同的順序進(jìn)行組織,以輔助導(dǎo)航。添加包裝器時(shí),請(qǐng)記住在文件底部的長(zhǎng)列表中注釋掉默認(rèn)包裝器的定義(不要?jiǎng)h除它,只是將其注釋掉)。
4.9.7。使用包裝器時(shí)期待什么?
包裝器應(yīng)該減少M(fèi)emcheck在MPI應(yīng)用程序上的錯(cuò)誤錯(cuò)誤率。因?yàn)榉庋b是在MPI接口完成的,所以接口下面的MPI實(shí)現(xiàn)中仍然可能會(huì)有大量的錯(cuò)誤報(bào)告。你能做的最好的就是試圖壓制他們。
您還可以發(fā)現(xiàn)輸入端(緩沖區(qū)長(zhǎng)度/定義)檢查在您的MPI使用中發(fā)現(xiàn)錯(cuò)誤,例如傳遞太短的緩沖區(qū)?MPI_Recv。
未包裝的功能可能會(huì)增加錯(cuò)誤率。一種可能的方法是與運(yùn)行?MPI_DEBUG含有?warn。這將顯示您缺少正確的包裝器但仍然使用的功能。然后你可以為他們寫封裝。
PMPI_Reduce當(dāng)使用自定義(用戶定義的)縮減功能時(shí),一個(gè)已知的潛在的錯(cuò)誤錯(cuò)誤源?就是功能系列。在縮減操作中,每個(gè)節(jié)點(diǎn)都意圖將數(shù)據(jù)發(fā)送到使用指定的縮減功能的“中心點(diǎn)”,將數(shù)據(jù)項(xiàng)合并成單個(gè)項(xiàng)目。因此,通常,數(shù)據(jù)在節(jié)點(diǎn)之間傳遞并饋送到縮減功能,但是包裝庫不能將傳輸?shù)臄?shù)據(jù)標(biāo)記為初始化之前被遞送到縮減功能,因?yàn)樗羞@些都發(fā)生在“內(nèi)部”?PMPI_Reduce。因此,您的縮減功能可能會(huì)顯示誤報(bào)。
總結(jié)
以上是生活随笔為你收集整理的Memcheck:一个内存错误检测器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 表格 列合并
- 下一篇: oracle 清理跟踪文件.trc .t