調不盡的內存泄漏,用不完的Valgrind

調不盡的內存泄漏,用不完的Valgrind

Valgrind 安裝

1. 到www.valgrind.org下載最新版valgrind-X.X.X.tar.bz2

2. 解壓安裝包:tar –jxvf valgrind-3.2.3.tar.bz2

3. 解壓後生成目錄valgrind-3.2.3

4. cd valgrind-3.2.3

5. 運行./autogen.sh設置環境(需要標準的autoconf工具)(可選)

6. ./configure;配置Valgrind,生成MakeFile文件,具體參數信息詳見INSTALL文件。一般只需要設置--prefix=/where/you/want/it/installed

7. Make;編譯Valgrind

8. make install;安裝Valgrind

Valgrind包含的工具

Valgrind支持很多工具:memcheck,addrcheck,cachegrind,Massif,helgrind和Callgrind等。在運行Valgrind時,你必須指明想用的工具,如果省略工具名,默認運行memcheck。

1、memcheck

memcheck探測程序中內存管理存在的問題。它檢查所有對內存的讀/寫操作,並截取所有的malloc/new/free/delete調用。因此memcheck工具能夠探測到以下問題:

1)使用未初始化的內存

2)讀/寫已經被釋放的內存

3)讀/寫內存越界

4)讀/寫不恰當的內存棧空間

5)內存泄漏

6)使用malloc/new/new[]和free/delete/delete[]不匹配。

7)src和dst的重疊

2、cachegrind

cachegrind是一個cache剖析器。它模擬執行CPU中的L1, D1和L2 cache,因此它能很精確的指出代碼中的cache未命中。如果你需要,它可以打印出cache未命中的次數,內存引用和發生cache未命中的每一行代碼,每一個函數,每一個模塊和整個程序的摘要。如果你要求更細緻的信息,它可以打印出每一行機器碼的未命中次數。在x86和amd64上, cachegrind通過CPUID自動探測機器的cache配置,所以在多數情況下它不再需要更多的配置信息了。

3、helgrind

helgrind查找多線程程序中的競爭數據。helgrind查找內存地址,那些被多於一條線程訪問的內存地址,但是沒有使用一致的鎖就會被查出。這表示這些地址在多線程間訪問的時候沒有進行同步,很可能會引起很難查找的時序問題。

它主要用來檢查多線程程序中出現的競爭問題。Helgrind 尋找內存中被多個線程訪問,而又沒有一貫加鎖的區域,這些區域往往是線程之間失去同步的地方,而且會導致難以發掘的錯誤。Helgrind實現了名爲”Eraser” 的競爭檢測算法,並做了進一步改進,減少了報告錯誤的次數。

4、Callgrind

Callgrind收集程序運行時的一些數據,函數調用關係等信息,還可以有選擇地進行cache 模擬。在運行結束時,它會把分析數據寫入一個文件。callgrind_annotate可以把這個文件的內容轉化成可讀的形式。

一般用法:

$valgrind --tool=callgrind ./sec_infod

會在當前目錄下生成callgrind.out.[pid], 如果我們想結束程序, 可以

$killall callgrind

然後我們可以用

$callgrind_annotate --auto=yes callgrind.out.[pid] > log

$vi log

5、Massif

堆棧分析器,它能測量程序在堆棧中使用了多少內存,告訴我們堆塊,堆管理塊和棧的大小。Massif能幫助我們減少內存的使用,在帶有虛擬內存的現代系統中,它還能夠加速我們程序的運行,減少程序停留在交換區中的機率。

6、lackey

lackey是一個示例程序,以其爲模版可以創建你自己的工具。在程序結束後,它打印出一些基本的關於程序執行統計數據。

Valgrind的參數

用法: valgrind [options] prog-and-args [options]: 常用選項,適用於所有Valgrind工具

--tool=<name>

最常用的選項。運行 valgrind中名爲toolname的工具。默認memcheck。

-h --help

顯示所有選項的幫助,包括內核和選定的工具兩者。

--version

顯示valgrind內核的版本,每個工具都有各自的版本。

-q --quiet

安靜地運行,只打印錯誤信息。

--verbose

更詳細的信息。

--trace-children=<yes|no>

跟蹤子線程? [default: no]

--track-fds=<yes|no>

跟蹤打開的文件描述?[default: no]

--time-stamp=<yes|no>

增加時間戳到LOG信息? [default: no]

--log-fd=<number>

輸出LOG到描述符文件 [2=stderr]

--log-file=<file>

將輸出的信息寫入到filename.PID的文件裏,PID是運行程序的進行ID

--log-file-exactly=<file>

輸出LOG信息到 file

LOG信息輸出

--xml=yes

將信息以xml格式輸出,只有memcheck可用

--num-callers=<number>

show <number> callers in stack traces [12]

--error-exitcode=<number>

如果發現錯誤則返回錯誤代碼 [0=disable]

--db-attach=<yes|no>

當出現錯誤,valgrind會自動啓動調試器gdb。[default: no]

--db-command=<command>

啓動調試器的命令行選項[gdb -nw %f %p]

適用於Memcheck工具的相關選項:

--leak-check=<no|summary|full>

要求對leak給出詳細信息? Leak是指,存在一塊沒有被引用的內存空間,或沒有被釋放的內存空間,如summary,只反饋一些總結信息,告訴你有多少個malloc,多少個free 等;如果是full將輸出所有的leaks,也就是定位到某一個malloc/free。 [default: summary]

--show-reachable=<yes|no>

如果爲no,只輸出沒有引用的內存leaks,或指向malloc返回的內存塊中部某處的leaks [default: no]

更詳細的參數指令見附錄A。

Valgrind的使用

首先,在編譯程序的時候打開調試模式(gcc編譯器的-g選項)。如果沒有調試信息,即使最好的valgrind工具也將中能夠猜測特定的代碼是屬於哪一個函數。打開調試選項進行編譯後再用valgrind檢查,valgrind將會給你的個詳細的報告,比如哪一行代碼出現了內存泄漏。

當檢查的是C++程序的時候,還應該考慮另一個選項 -fno-inline。它使得函數調用鏈很清晰,這樣可以減少你在瀏覽大型C++程序時的混亂。比如在使用這個選項的時候,用memcheck檢查openoffice就很容易。當然,你可能不會做這項工作,但是使用這一選項使得valgrind生成更精確的錯誤報告和減少混亂。

一些編譯優化選項(比如-O2或者更高的優化選項),可能會使得memcheck提交錯誤的未初始化報告,因此,爲了使得valgrind的報告更精確,在編譯的時候最好不要使用優化選項。

如果程序是通過腳本啓動的,可以修改腳本里啓動程序的代碼,或者使用--trace-children=yes選項來運行腳本。

下面是用memcheck檢查sample.c的例子

這裏用到的示例程序文件名爲:sample.c(如下所示),選用的編譯器爲gcc。

生成可執行程序

gcc –g sample.c –o sample

圖1

運行Valgrind

valgrind --tool=memcheck ./sample

以下是運行上述命令後的輸出

圖2

左邊顯示類似行號的數字(10297)表示的是 Process ID。

最上面的紅色方框表示的是 valgrind 的版本信息。

中間的紅色方框表示 valgrind 通過運行被測試程序,發現的內存問題。通過閱讀這些信息,可以發現:

l 這是一個對內存的非法寫操作,非法寫操作的內存是4 bytes。

l 發生錯誤時的函數堆棧,以及具體的源代碼行號。

l 非法寫操作的具體地址空間。

最下面的紅色方框是對發現的內存問題和內存泄漏問題的總結。內存泄漏的大小(40 bytes)也能夠被檢測出來。

Valgrind的示例

例1.使用未初始化的內存

代碼如下

 

#include <stdio.h>                                                              
int main()
{
    int x;
    if(x == 0)
    {
        printf("X is zero");
    }
    return 0;
}


Valgrind提示如下
==14222== Conditional jump or move depends onuninitialised value(s)
==14222== at 0x400484: main (sample2.c:6)
X is zero==14222==
==14222== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 5 from 1)
==14222== malloc/free: in use at exit: 0 bytes in 0 blocks.
==14222== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==14222== For counts of detected errors, rerunwith: -v
==14222== All heap blocks were freed -- no leaksare possible.

 

  

例2.內存讀寫越界

代碼如下

 

#include<stdlib.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
    int len=5;
    int i;
    int *pt=(int*)malloc(len*sizeof(int));
    int *p=pt;
    for(i=0;i<len;i++)
    {p++;}
    *p=5;
    printf(“%d”,*p);
    return;
}
Valgrind提示如下
==23045== Invalid write of size 4
==23045== at 0x40050A: main (sample2.c:11)
==23045== Address 0x4C2E044 is 0 bytes after a block of size 20 alloc'd
==23045== at 0x4A05809: malloc (vg_replace_malloc.c:149)
==23045== by 0x4004DF: main (sample2.c:7)
==23045==
==23045== Invalid read of size 4
==23045== at 0x400514: main (sample2.c:12)
==23045== Address 0x4C2E044 is 0 bytes after a block of size 20 alloc'd
==23045== at 0x4A05809: malloc (vg_replace_malloc.c:149)
==23045== by 0x4004DF: main (sample2.c:7)
5==23045==
==23045== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 5 from 1)
==23045== malloc/free: in use at exit: 20 bytes in 1 blocks.
==23045== malloc/free: 1 allocs, 0 frees, 20 bytes allocated.
==23045== For counts of detected errors, rerunwith: -v
==23045== searching for pointers to 1 not-freed blocks.
==23045== checked 66,584 bytes.
==23045==
==23045== LEAK SUMMARY:
==23045== definitely lost: 20 bytes in 1 blocks.
==23045== possibly lost: 0 bytes in 0 blocks.
==23045== still reachable: 0 bytes in 0 blocks.
==23045== suppressed: 0 bytes in 0 blocks.
==23045== Use --leak-check=full to see details ofleaked memory.

 

  

例3.src和dst內存覆蓋

代碼如下

#include<stdlib.h>

#include<stdio.h>

#include<string.h>

int main(intargc,char *argv[])

{ char x[50];

int i;

for(i=0;i<50;i++)

{x[i]=i;}

strncpy(x+20,x,20);//Good

strncpy(x+20,x,21);//Overlap

x[39]=’/0’;

strcpy(x,x+20);//Good

x[39]=40;

x[40]=’/0’;

strcpy(x,x+20);//Overlap

return 0;

}

Valgrind提示如下

==24139== Sourceand destination overlap in strncpy(0x7FEFFFC09, 0x7FEFFFBF5, 21)

==24139== at0x4A0724F: strncpy (mc_replace_strmem.c:116)

==24139== by0x400527: main (sample3.c:10)

==24139==

==24139== Sourceand destination overlap in strcpy(0x7FEFFFBE0, 0x7FEFFFBF4)

==24139== at0x4A06E47: strcpy (mc_replace_strmem.c:106)

==24139== by0x400555: main (sample3.c:15)

==24139==

==24139== ERRORSUMMARY: 2 errors from 2 contexts (suppressed: 5 from 1)

==24139==malloc/free: in use at exit: 0 bytes in 0 blocks.

==24139==malloc/free: 0 allocs, 0 frees, 0 bytes allocated.

==24139== Forcounts of detected errors, rerun with: -v

==24139== All heapblocks were freed -- no leaks are possible.

例4.動態內存管理錯誤

常見的內存分配方式分三種:靜態存儲,棧上分配,堆上分配。全局變量屬於靜態存儲,它們是在編譯時就被分配了存儲空間,函數內的局部變量屬於棧上分配,而最靈活的內存使用方式當屬堆上分配,也叫做內存動態分配了。常用的內存動態分配函數包括:malloc, alloc, realloc, new等,動態釋放函數包括free, delete。

一旦成功申請了動態內存,我們就需要自己對其進行內存管理,而這又是最容易犯錯誤的。常見的內存動態管理錯誤包括:

l 申請和釋放不一致

由於 C++ 兼容 C,而 C 與 C++ 的內存申請和釋放函數是不同的,因此在 C++ 程序中,就有兩套動態內存管理函數。一條不變的規則就是採用 C 方式申請的內存就用 C 方式釋放;用 C++ 方式申請的內存,用 C++ 方式釋放。也就是用 malloc/alloc/realloc 方式申請的內存,用 free 釋放;用 new 方式申請的內存用 delete 釋放。在上述程序中,用 malloc 方式申請了內存卻用 delete 來釋放,雖然這在很多情況下不會有問題,但這絕對是潛在的問題。

l 申請和釋放不匹配

申請了多少內存,在使用完成後就要釋放多少。如果沒有釋放,或者少釋放了就是內存泄露;多釋放了也會產生問題。上述程序中,指針p和pt指向的是同一塊內存,卻被先後釋放兩次。

l 釋放後仍然讀寫

本質上說,系統會在堆上維護一個動態內存鏈表,如果被釋放,就意味着該塊內存可以繼續被分配給其他部分,如果內存被釋放後再訪問,就可能覆蓋其他部分的信息,這是一種嚴重的錯誤,上述程序第16行中就在釋放後仍然寫這塊內存。

下面的一段程序,就包括了內存動態管理中常見的錯誤。

#include<stdlib.h>

#include<stdio.h>

int main(intargc,char *argv[])

{ char*p=(char*)malloc(10);

char *pt=p;

int i;

for(i=0;i<10;i++)

{p[i]=’z’;}

delete p;

p[1]=’a’;

free(pt);

return 0;

}

Valgrind提示如下

==25811==Mismatched free() / delete / delete []

==25811== at0x4A05130: operator delete(void*) (vg_replace_malloc.c:244)

==25811== by0x400654: main (sample4.c:9)

==25811== Address0x4C2F030 is 0 bytes inside a block of size 10 alloc'd

==25811== at0x4A05809: malloc (vg_replace_malloc.c:149)

==25811== by0x400620: main (sample4.c:4)

==25811==

==25811== Invalidwrite of size 1

==25811== at0x40065D: main (sample4.c:10)

==25811== Address0x4C2F031 is 1 bytes inside a block of size 10 free'd

==25811== at 0x4A05130:operator delete(void*) (vg_replace_malloc.c:244)

==25811== by0x400654: main (sample4.c:9)

==25811==

==25811== Invalidfree() / delete / delete[]

==25811== at0x4A0541E: free (vg_replace_malloc.c:233)

==25811== by0x400668: main (sample4.c:11)

==25811== Address0x4C2F030 is 0 bytes inside a block of size 10 free'd

==25811== at0x4A05130: operator delete(void*) (vg_replace_malloc.c:244)

==25811== by0x400654: main (sample4.c:9)

==25811==

==25811== ERRORSUMMARY: 3 errors from 3 contexts (suppressed: 5 from 1)

==25811==malloc/free: in use at exit: 0 bytes in 0 blocks.

==25811==malloc/free: 1 allocs, 2 frees, 10 bytes allocated.

==25811== Forcounts of detected errors, rerun with: -v

==25811== All heapblocks were freed -- no leaks are possible.

例5.內存泄漏

代碼如下

#include<stdlib.h>

int main()

{

char *x =(char*)malloc(20);

char *y =(char*)malloc(20);

x=y;

free(x);

free(y);

return 0;

}

Valgrind提示如下

==19013== Invalidfree() / delete / delete[]

==19013== at0x4A0541E: free (vg_replace_malloc.c:233)

==19013== by0x4004F5: main (sample5.c:8)

==19013== Address0x4C2E078 is 0 bytes inside a block of size 20 free'd

==19013== at0x4A0541E: free (vg_replace_malloc.c:233)

==19013== by0x4004EC: main (sample5.c:7)

==19013==

==19013== ERRORSUMMARY: 1 errors from 1 contexts (suppressed: 5 from 1)

==19013==malloc/free: in use at exit: 20 bytes in 1 blocks.

==19013==malloc/free: 2 allocs, 2 frees, 40 bytes allocated.

==19013== Forcounts of detected errors, rerun with: -v

==19013==searching for pointers to 1 not-freed blocks.

==19013== checked66,584 bytes.

==19013==

==19013== LEAKSUMMARY:

==19013==definitely lost: 20 bytes in 1 blocks.

==19013== possiblylost: 0 bytes in 0 blocks.

==19013== stillreachable: 0 bytes in 0 blocks.

==19013==suppressed: 0 bytes in 0 blocks.

==19013== Use--leak-check=full to see details of leaked memory.

例6.非法寫/讀

代碼如下

int main()

{

int i, *x;

x = (int*)malloc(10*sizeof(int));

for (i=0; i<11;i++)

x[i] = i;

free(x);

}

Valgrind提示如下

==21483== Invalidwrite of size 4

==21483== at0x4004EA: main (sample6.c:6)

==21483== Address0x4C2E058 is 0 bytes after a block of size 40 alloc'd

==21483== at0x4A05809: malloc (vg_replace_malloc.c:149)

==21483== by0x4004C9: main (sample6.c:4)

==21483==

==21483== ERRORSUMMARY: 1 errors from 1 contexts (suppressed: 5 from 1)

==21483==malloc/free: in use at exit: 0 bytes in 0 blocks.

==21483==malloc/free: 1 allocs, 1 frees, 40 bytes allocated.

==21483== Forcounts of detected errors, rerun with: -v

==21483== All heapblocks were freed -- no leaks are possible.

例7.無效指針

代碼如下

#include<stdlib.h>

int main()

{

char *x =malloc(10);

x[10] = 'a';

free(x);

return 0;

}

Valgrind提示如下

==15262== Invalidwrite of size 1

==15262== at0x4004D6: main (sample7.c:5)

==15262== Address0x4C2E03A is 0 bytes after a block of size 10 alloc'd

==15262== at0x4A05809: malloc (vg_replace_malloc.c:149)

==15262== by0x4004C9: main (sample7.c:4)

==15262==

==15262== ERRORSUMMARY: 1 errors from 1 contexts (suppressed: 5 from 1)

==15262==malloc/free: in use at exit: 0 bytes in 0 blocks.

==15262==malloc/free: 1 allocs, 1 frees, 10 bytes allocated.

==15262== Forcounts of detected errors, rerun with: -v

==15262== All heapblocks were freed -- no leaks are possible.

例8.重複釋放

代碼如下

#include<stdlib.h>

int main()

{

char *x =malloc(10);

free(x);

free(x);

return 0;

}

Valgrind提示如下

==15005== Invalidfree() / delete / delete[]

==15005== at0x4A0541E: free (vg_replace_malloc.c:233)

==15005== by0x4004DF: main (sample8.c:6)

==15005== Address0x4C2E030 is 0 bytes inside a block of size 10 free'd

==15005== at0x4A0541E: free (vg_replace_malloc.c:233)

==15005== by0x4004D6: main (sample8.c:5)

==15005==

==15005== ERRORSUMMARY: 1 errors from 1 contexts (suppressed: 5 from 1)

==15005==malloc/free: in use at exit: 0 bytes in 0 blocks.

==15005==malloc/free: 1 allocs, 2 frees, 10 bytes allocated.

==15005== Forcounts of detected errors, rerun with: -v

==15005== All heapblocks were freed -- no leaks are possible.

Valgrind的侷限

l Valgrind不對靜態數組(分配在棧上)進行邊界檢查。如果在程序中聲明瞭一個數組:

int main()

{

char x[10];

x[11] = 'a';

}

Valgrind則不會警告你,你可以把數組改爲動態在堆上分配的數組,這樣就可能進行邊界檢查了。這個方法好像有點得不償失的感覺。

l Valgrind佔用了更多的內存--可達兩倍於你程序的正常使用量。如果你用Valgrind來檢測使用大量內存的程序就會遇到問題,它可能會用很長的時間來運行測試。大多數情況下,這都不是問題,即使速度慢也僅是檢測時速度慢,如果你用Valgrind來檢測一個正常運行時速度就很慢的程序,這下問題就大了。 Valgrind不可能檢測出你在程序中犯下的所有錯誤--如果你不檢查緩衝區溢出,Valgrind也不會告訴你代碼寫了它不應該寫的內存。

附錄A:參數指令

基本選項:

這些選項對所有工具都有效。

-h --help

顯示所有選項的幫助,包括內核和選定的工具兩者。

--help-debug

和--help相同,並且還能顯示通常只有Valgrind的開發人員使用的調試選項。

--version

顯示Valgrind內核的版本號。工具可以有他們自已的版本號。這是一種保證工具只在它們可以運行的內核上工作的一種設置。這樣可以減少在工具和內核之間版本兼容性導致奇怪問題的概率。

-q --quiet

安靜的運行,只打印錯誤信息。在進行迴歸測試或者有其它的自動化測試機制時會非常有用。

-v --verbose

顯示詳細信息。在各個方面顯示你的程序的額外信息,例如:共享對象加載,使用的重置,執行引擎和工具的進程,異常行爲的警告信息。重複這個標記可以增加詳細的級別。

-d

調試Valgrind自身發出的信息。通常只有Valgrind開發人員對此感興趣。重複這個標記可以產生更詳細的輸出。如果你希望發送一個bug報告,通過-v -v -d -d生成的輸出會使你的報告更加有效。

--tool=<toolname>[default: memcheck]

運行toolname指定的Valgrind,例如,Memcheck, Addrcheck, Cachegrind,等等。

--trace-children=<yes|no>[default: no]

當這個選項打開時,Valgrind會跟蹤到子進程中。這經常會導致困惑,而且通常不是你所期望的,所以默認這個選項是關閉的。

--track-fds=<yes|no>[default: no]

當這個選項打開時,Valgrind會在退出時打印一個打開文件描述符的列表。每個文件描述符都會打印出一個文件是在哪裏打開的棧回溯,和任何與此文件描述符相關的詳細信息比如文件名或socket信息。

--time-stamp=<yes|no>[default: no]

當這個選項打開時,每條信息之前都有一個從程序開始消逝的時間,用天,小時,分鐘,秒和毫秒錶示。

--log-fd=<number>[default: 2, stderr]

指定Valgrind把它所有的消息都輸出到一個指定的文件描述符中去。默認值2, 是標準錯誤輸出(stderr)。注意這可能會干擾到客戶端自身對stderr的使用, Valgrind的輸出與客戶程序的輸出將穿插在一起輸出到stderr。

--log-file=<filename>

指定Valgrind把它所有的信息輸出到指定的文件中。實際上,被創建文件的文件名是由filename、'.'和進程號連接起來的(即<filename>.<pid>),從而每個進程創建不同的文件。

--log-file-exactly=<filename>

類似於--log-file,但是後綴".pid"不會被添加。如果設置了這個選項,使用Valgrind跟蹤多個進程,可能會得到一個亂七八糟的文件。

--log-file-qualifier=<VAR>

當和--log-file一起使用時,日誌文件名將通過環境變量$VAR來篩選。這對於MPI程序是有益的。更多的細節,查看手冊2.3節 "註解"。

--log-socket=<ip-address:port-number>

指定Valgrind輸出所有的消息到指定的IP,指定的端口。當使用1500端口時,端口有可能被忽略。如果不能建立一個到指定端口的連接,Valgrind將輸出寫到標準錯誤(stderr)。這個選項經常和一個Valgrind監聽程序一起使用。更多的細節,查看手冊2.3節 "註解"。

錯誤相關選項:

這些選項適用於所有產生錯誤的工具,比如Memcheck, 但是Cachegrind不行。

--xml=<yes|no>[default: no]

當這個選項打開時,輸出將是XML格式。這是爲了使用Valgrind的輸出做爲輸入的工具,例如GUI前端更加容易些。目前這個選項只在Memcheck時生效。

--xml-user-comment=<string>

在XML開頭 附加用戶註釋,僅在指定了--xml=yes時生效,否則忽略。

--demangle=<yes|no>[default: yes]

打開/關閉C++的名字自動解碼。默認打開。當打開時,Valgrind將嘗試着把編碼過的C++名字自動轉回初始狀態。這個解碼器可以處理g++版本爲2.X,3.X或4.X生成的符號。一個關於名字編碼解碼重要的事實是,禁止文件中的解碼函數名仍然使用他們未解碼的形式。Valgrind在搜尋可用的禁止條目時不對函數名解碼,因爲這將使禁止文件內容依賴於Valgrind的名字解碼機制狀態,會使速度變慢,且無意義。

--num-callers=<number>[default: 12]

默認情況下,Valgrind顯示12層函數調用的函數名有助於確定程序的位置。可以通過這個選項來改變這個數字。這樣有助在嵌套調用的層次很深時確定程序的位置。注意錯誤信息通常只回溯到最頂上的4個函數。(當前函數,和它的3個調用者的位置)。所以這並不影響報告的錯誤總數。這個值的最大值是50。注意高的設置會使Valgrind運行得慢,並且使用更多的內存,但是在嵌套調用層次比較高的程序中非常實用。

--error-limit=<yes|no>[default: yes]

當這個選項打開時,在總量達到10,000,000,或者1,000個不同的錯誤,Valgrind停止報告錯誤。這是爲了避免錯誤跟蹤機制在錯誤很多的程序下變成一個巨大的性能負擔。

--error-exitcode=<number>[default: 0]

指定如果Valgrind在運行過程中報告任何錯誤時的退出返回值,有兩種情況;當設置爲默認值(零)時,Valgrind返回的值將是它模擬運行的程序的返回值。當設置爲非零值時,如果Valgrind發現任何錯誤時則返回這個值。在Valgrind做爲一個測試工具套件的部分使用時這將非常有用,因爲使測試工具套件只檢查Valgrind返回值就可以知道哪些測試用例Valgrind報告了錯誤。

--show-below-main=<yes|no>[default: no]

默認地,錯誤時的棧回溯不顯示main()之下的任何函數(或者類似的函數像glibc的__libc_start_main(),如果main()沒有出現在棧回溯中);這些大部分都是令人厭倦的C庫函數。如果打開這個選項,在main()之下的函數也將會顯示。

--suppressions=<filename>[default: $PREFIX/lib/valgrind/default.supp]

指定一個額外的文件讀取不需要理會的錯誤;你可以根據需要使用任意多的額外文件。

--gen-suppressions=<yes|no|all>[default: no]

當設置爲yes時,Valgrind將會在每個錯誤顯示之後自動暫停並且打印下面這一行:---- Print suppression ? --- [Return/N/n/Y/y/C/c] ----這個提示的行爲和--db-attach選項(見下面)相同。如果選擇是,Valgrind會打印出一個錯誤的禁止條目,你可以把它剪切然後粘帖到一個文件,如果不希望在將來再看到這個錯誤信息。當設置爲all時,Valgrind會對每一個錯誤打印一條禁止條目,而不向用戶詢問。這個選項對C++程序非常有用,它打印出編譯器調整過的名字。注意打印出來的禁止條目是儘可能的特定的。如果需要把類似的條目歸納起來,比如在函數名中添加通配符。並且,有些時候兩個不同的錯誤也會產生同樣的禁止條目,這時Valgrind就會輸出禁止條目不止一次,但是在禁止條目的文件中只需要一份拷貝(但是如果多於一份也不會引起什麼問題)。並且,禁止條目的名字像<在這兒輸入一個禁止條目的名字>;名字並不是很重要,它只是和-v選項一起使用打印出所有使用的禁止條目記錄。

--db-attach=<yes|no>[default: no]

當這個選項打開時,Valgrind將會在每次打印錯誤時暫停並打出如下一行:---- Attach todebugger ? --- [Return/N/n/Y/y/C/c] ---- 按下回車,或者N、回車,n、回車,Valgrind不會對這個錯誤啓動調試器。按下Y、回車,或者y、回車,Valgrind會啓動調試器並設定在程序運行的這個點。當調試結束時,退出,程序會繼續運行。在調試器內部嘗試繼續運行程序,將不會生效。按下C、回車,或者c、回車,Valgrind不會啓動一個調試器,並且不會再次詢問。注意:--db-attach=yes與--trace-children=yes有衝突。你不能同時使用它們。Valgrind在這種情況下不能啓動。

2002.05: 這是一個歷史的遺留物,如果這個問題影響到你,請發送郵件並投訴這個問題。

2002.11:如果你發送輸出到日誌文件或者到網絡端口,我猜這不會讓你有任何感覺。不須理會。

--db-command=<command>[default: gdb -nw %f %p]

通過--db-attach指定如何使用調試器。默認的調試器是gdb.默認的選項是一個運行時擴展Valgrind的模板。 %f會用可執行文件的文件名替換,%p會被可執行文件的進程ID替換。

這指定了Valgrind將怎樣調用調試器。默認選項不會因爲在構造時是否檢測到了GDB而改變,通常是/usr/bin/gdb.使用這個命令,你可以指定一些調用其它的調試器來替換。

給出的這個命令字串可以包括一個或多個%p %f擴展。每一個%p實例都被解釋成將調試的進程的PID,每一個%f實例都被解釋成要調試的進程的可執行文件路徑。

--input-fd=<number>[default: 0, stdin]

使用--db-attach=yes和--gen-suppressions=yes選項,在發現錯誤時,Valgrind會停下來去讀取鍵盤輸入。默認地,從標準輸入讀取,所以關閉了標準輸入的程序會有問題。這個選項允許你指定一個文件描述符來替代標準輸入讀取。

--max-stackframe=<number>[default: 2000000]

棧的最大值。如果棧指針的偏移超過這個數量,Valgrind則會認爲程序是切換到了另外一個棧執行。如果在程序中有大量的棧分配的數組,你可能需要使用這個選項。valgrind保持對程序棧指針的追蹤。如果棧指針的偏移超過了這個數量,Valgrind假定你的程序切換到了另外一個棧,並且Memcheck行爲與棧指

針的偏移沒有超出這個數量將會不同。通常這種機制運轉得很好。然而,如果你的程序在棧上申請了大的結構,這種機制將會表現得愚蠢,並且Memcheck將會報告大量的非法棧內存訪問。這個選項允許把這個閥值設置爲其它值。應該只在Valgrind的調試輸出中顯示需要這麼做時才使用這個選項。在這種情況下,它會告訴你應該指定的新的閥值。普遍地,在棧中分配大塊的內存是一個壞的主意。因爲這很容易用光你的棧空間,尤其是在內存受限的系統或者支持大量小堆棧的線程的系統上,因爲Memcheck執行的錯誤檢查,對於堆上的數據比對棧上的數據要高效很多。如果你使用這個選項,你可能希望考慮重寫代碼在堆上分配內存而不是在棧上分配。

MALLOC()相關的選項:

對於使用自有版本的malloc() (例如Memcheck和massif),下面的選項可以使用。

--alignment=<number>[default: 8]

默認Valgrind的malloc(),realloc(), 等等,是8字節對齊地址的。這是大部分處理器的標準。然而,一些程序可能假定malloc()等總是返回16字節或更多對齊的內存。提供的數值必須在8和4096區間之內,並且必須是2的冪數。

非通用選項:

這些選項可以用於所有的工具,它們影響Valgrind core的幾個特性。大部分人不會用到這些選項。

--run-libc-freeres=<yes|no>[default: yes]

GNU C庫(libc.so),所有程序共用的,可能會分配一部分內存自已用。通常在程序退出時釋放內存並不麻煩 -- 這裏沒什麼問題,因爲Linux內核在一個進程退出時會回收進程全部的資源,所以這只是會造成速度慢。glibc的作者認識到這樣會導致內存檢查器,像Valgrind,在退出時檢查內存錯誤的報告glibc的內存泄漏問題,爲了避免這個問題,他們提供了一個__libc_freeres()例程特別用來讓glibc釋放分配的所有內存。因此Memcheck在退出時嘗試着去運行__libc_freeres()。不幸的是,在glibc的一些版本中,__libc_freeres是有bug會導致段錯誤的。這在Red Hat 7.1上有特別聲明。所以,提供這個選項來決定是否運行__libc_freeres。如果你的程序看起來在Valgrind上運行得很好,但是在退出時發生段錯誤,你可能需要指定--run-libc-freeres=no來修正,這將可能錯誤的報告libc.so的內存泄漏。

--sim-hints=hint1,hint2,...

傳遞雜湊的提示給Valgrind,輕微的修改模擬行爲的非標準或危險方式,可能有助於模擬奇怪的特性。默認沒有提示打開。小心使用!目前已知的提示有:

l lax-ioctls: 對ioctl的處理非常不嚴格,唯一的假定是大小是正確的。不需要在寫時緩衝區完全的初始化。沒有這個,用大量的奇怪的ioctl命令來使用一些設備驅動將會非常煩人。

l enable-inner:打開某些特殊的效果,當運行的程序是Valgrind自身時。

--kernel-variant=variant1,variant2,...

處理系統調用和ioctls在這個平臺的默認核心上產生不同的變量。這有助於運行在改進過的內核或者支持非標準的ioctls上。小心使用。如果你不理解這個選項做的是什麼那你幾乎不需要它。已經知道的變量有:

l bproc: 支持X86平臺上的sys_broc系統調用。這是爲了運行在BProc,它是標準Linux的一個變種,有時用來構建集羣。

--show-emwarns=<yes|no>[default: no]

當這個選項打開時,Valgrind在一些特定的情況下將對CPU仿真產生警告。通常這些都是不引人注意的。

--smc-check=<none|stack|all>[default: stack]

這個選項控制Valgrind對自我修改的代碼的檢測。Valgrind可以不做檢測,可以檢測棧中自我修改的代碼,或者任意地方檢測自我修改的代碼。注意默認選項是捕捉絕大多數情況,到目前我們瞭解的情況爲止。使用all選項時會極大的降低速度。(但是用none選項運行極少影響速度,因爲對大多數程序,非常少的代碼被添加到棧中)

調試VALGRIND選項:

還有一些選項是用來調試Valgrind自身的。在運行一般的東西時不應該需要的。如果你希望看到選項列表,使用--help-debug選項。

內存檢查選項:

--leak-check=<no|summary|yes|full>[default: summary]

當這個選項打開時,當客戶程序結束時查找內存泄漏。內存泄漏意味着有用malloc分配內存塊,但是沒有用free釋放,而且沒有指針指向這塊內存。這樣的內存塊永遠不能被程序釋放,因爲沒有指針指向它們。如果設置爲summary,Valgrind會報告有多少內存泄漏發生了。如果設置爲full或yes,Valgrind給出每一個獨立的泄漏的詳細信息。

--show-reachable=<yes|no>[default: no]

當這個選項關閉時,內存泄漏檢測器只顯示沒有指針指向的內存塊,或者只能找到指向塊中間的指針。當這個選項打開時,內存泄漏檢測器還報告有指針指向的內存塊。這些塊是最有可能出現內存泄漏的地方。你的程序可能,至少在原則上,應該在退出前釋放這些內存塊。這些有指針指向的內存塊和沒有指針指向的內存塊,或者只有內部指針指向的塊,都可能產生內存泄漏,因爲實際上沒有一個指向塊起始的指針可以拿來釋放,即使你想去釋放它。

--leak-resolution=<low|med|high>[default: low]

在做內存泄漏檢查時,確定memcheck將怎麼樣考慮不同的棧是相同的情況。當設置爲low時,只需要前兩層棧匹配就認爲是相同的情況;當設置爲med,必須要四層棧匹配,當設置爲high時,所有層次的棧都必須匹配。對於hardcore內存泄漏檢查,你很可能需要使用--leak-resolution=high和--num-callers=40或者更大的數字。注意這將產生巨量的信息,這就是爲什麼默認選項是四個調用者匹配和低分辨率的匹配。注意--leak-resolution= 設置並不影響memcheck查找內存泄漏的能力。它只是改變了結果如何輸出。

--freelist-vol=<number>[default: 5000000]

當客戶程序使用free(C中)或者delete(C++)釋放內存時,這些內存並不是馬上就可以用來再分配的。這些內存將被標記爲不可訪問的,並被放到一個已釋放內存的隊列中。這樣做的目的是,使釋放的內存再次被利用的點儘可能的晚。這有利於memcheck在內存塊釋放後這段重要的時間檢查對塊不合法的訪問。這個選項指定了隊列所能容納的內存總容量,以字節爲單位。默認的值是5000000字節。增大這個數目會增加memcheck使用的內存,但同時也增加了對已釋放內存的非法使用的檢測概率。

--workaround-gcc296-bugs=<yes|no>[default: no]

當這個選項打開時,假定讀寫棧指針以下的一小段距離是gcc 2.96的bug,並且不報告爲錯誤。距離默認爲256字節。注意gcc 2.96是一些比較老的Linux發行版(RedHat 7.X)的默認編譯器,所以你可能需要使用這個選項。如果不是必要請不要使用這個選項,它可能會使一些真正的錯誤溜掉。一個更好的解決辦法是使用較新的,修正了這個bug的gcc/g++版本。

--partial-loads-ok=<yes|no>[default: no]

控制memcheck如何處理從地址讀取時字長度,字對齊,因此哪些字節是可以尋址的,哪些是不可以尋址的。當設置爲yes是,這樣的讀取並不拋出一個尋址錯誤。而是從非法地址讀取的V字節顯示爲未定義,訪問合法地址仍然是像平常一樣映射到內存。設置爲no時,從部分錯誤的地址讀取與從完全錯誤的地址讀取同樣處理:拋出一個非法地址錯誤,結果的V字節顯示爲合法數據。注意這種代碼行爲是違背ISO C/C++標準,應該被認爲是有問題的。如果可能,這種代碼應該修正。這個選項應該只是做爲一個最後考慮的方法。

--undef-value-errors=<yes|no>[default: yes]

控制memcheck是否檢查未定義值的危險使用。當設爲yes時,Memcheck的行爲像Addrcheck, 一個輕量級的內存檢查工具,是Valgrind的一個部分,它並不檢查未定義值的錯誤。使用這個選項,如果你不希望看到未定義值錯誤。

CACHEGRIND選項:

手動指定I1/D1/L2緩衝配置,大小是用字節表示的。這三個必須用逗號隔開,中間沒有空格,例如: valgrind --tool=cachegrind --I1=65535,2,64你可以指定一個,兩個或三個I1/D1/L2緩衝。如果沒有手動指定,每個級別使用普通方式(通過CPUID指令得到緩衝配置,如果失敗,使用默認值)得到的配置。

--I1=<size>,<associativity>,<linesize>

指定第一級指令緩衝的大小,關聯度和行大小。

--D1=<size>,<associativity>,<linesize>

指定第一級數據緩衝的大小,關聯度和行大小。

--L2=<size>,<associativity>,<linesize>

指定第二級緩衝的大小,關聯度和行大小。

CALLGRIND選項:

--heap=<yes|no>[default: yes]

當這個選項打開時,詳細的追蹤堆的使用情況。關閉這個選項時,massif.pid.txt或massif.pid.html將會非常的簡短。

--heap-admin=<number>[default: 8]

每個塊使用的管理字節數。這隻能使用一個平均的估計值,因爲它可能變化。glibc使用的分配器每塊需要4~15字節,依賴於各方面的因素。管理已經釋放的塊也需要空間,儘管massif不計算這些。

--stacks=<yes|no>[default: yes]

當打開時,在剖析信息中包含棧信息。多線程的程序可能有多個棧。

--depth=<number>[default: 3]

詳細的堆信息中調用過程的深度。增加這個值可以給出更多的信息,但是massif會更使這個程序運行得慢,使用更多的內存,並且產生一個大的massif.pid.txt或者massif.pid.hp文件。

--alloc-fn=<name>

指定一個分配內存的函數。這對於使用malloc()的包裝函數是有用的,可以用它來填充原來無效的上下文信息。(這些函數會給出無用的上下文信息,並在圖中給出無意義的區域)。指定的函數在上下文中被忽略,例如,像對malloc()一樣處理。這個選項可以在命令行中重複多次,指定多個函數。

--format=<text|html>[default: text]

產生text或者HTML格式的詳細堆信息,文件的後綴名使用.txt或者.html。

HELGRIND選項:

--private-stacks=<yes|no>[default: no]

假定線程棧是私有的。

--show-last-access=<yes|some|no>[default: no]

顯示最後一次字訪問出錯的位置。

LACKEY選項:

--fnname=<name>[default: _dl_runtime_resolve()]

對<name>函數計數。

--detailed-counts=<no|yes>[default: no]

對讀取,存儲和alu操作計數。

 

4.3. Explanation of error messages from Memcheck

Despite considerable sophistication under the hood, Memcheck can only really detect two kinds of errors: use of illegal addresses, and use of undefined values. Nevertheless, this is enough to help you discover all sorts of memory-management problems in your code.

[Valgrind Memcheck僅能檢測兩種錯誤,一是非法地址使用,二是未定義值使用。但這足以幫助你檢查出代碼中各種類型的內存管理問題。]

This section presents a quick summary of what error messages mean. The precise behaviour of the error-checking machinery is described in Details of Memcheck's checking machinery.

4.3.1. Illegal read / Illegal write errors [非法讀寫錯誤]

For example:

Invalid read of size 4
   at 0x40F6BBCC: (within /usr/lib/libpng.so.2.1.0.9)
   by 0x40F6B804: (within /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)
 Address 0xBFFFF0E0 is not stack'd, malloc'd or free'd

This happens when your program reads or writes memory at a place which Memcheck reckons it shouldn't.

[上面的錯誤信息表示Memcheck認爲你的程序錯誤的進行了對內存某個位置的讀或寫操作。]

In this example, the program did a 4-byte read at address 0xBFFFF0E0, somewhere within the system-supplied library libpng.so.2.1.0.9, which was called from somewhere else in the same library, called from line 326 of qpngio.cpp, and so on.

[具體信息表明,程序在 0xBFFFF0E0 處進行了4個字節的讀操作,該操作發生在系統提供的 libpng.so.2.1.0.9 中,進而顯示該庫的調用由 qpngio.cpp 文件的 326 行處引起,等等類似描述。]

Memcheck tries to establish what the illegal address might relate to, since that's often useful. So, if it points into a block of memory which has already been freed, you'll be informed of this, and also where the block was free'd at. Likewise, if it should turn out to be just off the end of a malloc'd block, a common result of off-by-one-errors in array subscripting, you'll be informed of this fact, and also where the block was malloc'd.

[Memcheck儘可能地幫助用戶指出錯誤地址與什麼有關聯,以提供有用信息。例如,如果程序使用了已經被釋放的內存塊,那麼你除了能從Memcheck得知這一情況外,還能得到具體是哪一塊內存被釋放了。再比如,對於數組處理中通常會發生的越界現象,Memcheck 不僅會通知用戶這一情況的出現,還會指出出錯位置。]

In this example, Memcheck can't identify the address. Actually the address is on the stack, but, for some reason, this is not a valid stack address -- it is below the stack pointer and that isn't allowed. In this particular case it's probably caused by gcc generating invalid code, a known bug in some ancient versions of gcc.

[在上面的例子中, Memcheck不能確定具體地址。實際上,地址位於棧中,但是由於某些原因,它是一個無效的棧地址。]

Note that Memcheck only tells you that your program is about to access memory at an illegal address. It can't stop the access from happening. So, if your program makes an access which normally would result in a segmentation fault, you program will still suffer the same fate -- but you will get a message from Memcheck immediately prior to this. In this particular example, reading junk on the stack is non-fatal, and the program stays alive.

[注意:Memcheck 只能告訴你你的程序將訪問非法地址內存,但它不能阻止訪問。所以,如果你的程序訪問中導致段錯誤,那程序將一直帶有這個錯誤,但 Memcheck 會在此發生之前給你提示信息。在上面的例子中,對棧上的垃圾區域進行讀操作沒有風險,程序也不會崩潰。]

4.3.2. Use of uninitialised values [值在初始化前使用]

For example:

Conditional jump or move depends on uninitialised value(s)
   at 0x402DFA94: _IO_vfprintf (_itoa.h:49)
   by 0x402E8476: _IO_printf (printf.c:36)
   by 0x8048472: main (tests/manuel1.c:8)

An uninitialised-value use error is reported when your program uses a value which hasn't been initialised -- in other words, is undefined. Here, the undefined value is used somewhere inside the printf() machinery of the C library. This error was reported when running the following small program:

[未初始值的使用錯誤,即未定義先使用。如下例:]

int main()
{
  int x;
  printf ("x = %d/n", x);
}

It is important to understand that your program can copy around junk (uninitialised) data as much as it likes.

[你的程序能夠對垃圾(未初始)數據進行拷貝,理解這點很重要。]

Memcheck observes this and keeps track of the data, but does not complain. A complaint is issued only when your program attempts to make use of uninitialised data. In this example, x is uninitialised. Memcheck observes the value being passed to _IO_printf and thence to _IO_vfprintf, but makes no comment. However, _IO_vfprintf has to examine the value of x so it can turn it into the corresponding ASCII string, and it is at this point that Memcheck complains.

[Memcheck 觀察並跟蹤數據,但它暫時不會報錯。只有當你試圖使用未初始的數據時纔會報錯。在上面的例子中,x 未被初始化,Memcheck 能夠觀察到該值傳給 _IO_printf,然後傳給 _IO_vfprintf,但此時不報錯。而當 _IO_vfprintf 必須要檢測 x 的值時,它需要將 x 值轉換成相應的 ASCII 碼字符串,此時 Memcheck就要報錯了。]

Sources of uninitialised data tend to be:

[未初始值的來源包括:]

  • Local variables in procedures which have not been initialised, as in the example above.

  • [本地變量在執行過程中未初始化,如上例。]
  • The contents of malloc'd blocks, before you write something there. In C++, the new operator is a wrapper round malloc, so if you create an object with new, its fields will be uninitialised until you (or the constructor) fill them in.

  • [malloc分配的塊內容未能初始化,特別是在寫入數據之前。在C++中,new 操作符包含 malloc 操作,所以,如果你用 new 創建了一個對象,你必須爲其指向的內存填充內容,或使用構造函數初始後,纔算真正初始化。]

4.3.3. Illegal frees [非法釋放內存]

For example:

Invalid free()
   at 0x4004FFDF: free (vg_clientmalloc.c:577)
   by 0x80484C7: main (tests/doublefree.c:10)
 Address 0x3807F7B4 is 0 bytes inside a block of size 177 free'd
   at 0x4004FFDF: free (vg_clientmalloc.c:577)
   by 0x80484C7: main (tests/doublefree.c:10)

Memcheck keeps track of the blocks allocated by your program with malloc/new, so it can know exactly whether or not the argument to free/delete is legitimate or not. Here, this test program has freed the same block twice. As with the illegal read/write errors, Memcheck attempts to make sense of the address free'd. If, as here, the address is one which has previously been freed, you wil be told that -- making duplicate frees of the same block easy to spot.

[因爲Memcheck 能夠保持對 malloc/new 分配的塊的跟蹤,所以,它能夠確切地知道 free/delete 是否合法。這裏,可以看到上面測試程序對同一塊釋放了兩次。與處理非法讀寫錯誤一樣,Memcheck 試圖使 free'd 出錯地址看起來更有意義。見實例]

4.3.4. When a block is freed with an inappropriate deallocation function [使用不恰當的釋放函數釋放塊]

In the following example, a block allocated with new[] has wrongly been deallocated with free:

[下面的例子顯示了使用free() 錯誤地釋放了 new[] 分配的內存:]

Mismatched free() / delete / delete []
   at 0x40043249: free (vg_clientfuncs.c:171)
   by 0x4102BB4E: QGArray::~QGArray(void) (tools/qgarray.cpp:149)
   by 0x4C261C41: PptDoc::~PptDoc(void) (include/qmemarray.h:60)
   by 0x4C261F0E: PptXml::~PptXml(void) (pptxml.cc:44)
 Address 0x4BB292A8 is 0 bytes inside a block of size 64 alloc'd
   at 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)
   by 0x4C21788F: OLEFilter::convert(QCString const &) (olefilter.cc:272)

In C++ it's important to deallocate memory in a way compatible with how it was allocated. The deal is:

[C++中,以正確的方式釋放內存非常重要,具體如下:]

  • If allocated with malloccallocreallocvalloc or memalign, you must deallocate with free.

  • [如果內存使用 malloc/calloc/realloc/valloc/memalign 分配,你必須使用 free 釋放。]
  • If allocated with new[], you must deallocate with delete[].

  • [如果使用 new[] 分配,你必須使用 delete[] 釋放。]
  • If allocated with new, you must deallocate with delete.

  • [如果使用 new 分配,你必須使用 delete 釋放。]

The worst thing is that on Linux apparently it doesn't matter if you do mix these up, but the same program may then crash on a different platform, Solaris for example. So it's best to fix it properly. According to the KDE folks "it's amazing how many C++ programmers don't know this".

[最糟糕的是,如果搞混,有時在 Linux 平臺不會出錯,但在其他平臺,如 Solaris 卻會使程序崩潰。所以,最好還是修改程序,恰當使用每種方式。也不怪 KDE 的開發者們在驚歎“怎麼會有那麼多程序員不知道這一點。”]

The reason behind the requirement is as follows. In some C++ implementations, delete[] must be used for objects allocated by new[] because the compiler stores the size of the array and the pointer-to-member to the destructor of the array's content just before the pointer actually returned. This implies a variable-sized overhead in what's returned by new or new[].

4.3.5. Passing system call parameters with inadequate read/write permissions [傳遞系統調用參數時讀寫權限不足]

Memcheck checks all parameters to system calls:

  • It checks all the direct parameters themselves.

  • [檢查直接參數]
  • Also, if a system call needs to read from a buffer provided by your program, Memcheck checks that the entire buffer is addressable and has valid data, ie, it is readable.

  • [程序提供緩衝區參數]
  • Also, if the system call needs to write to a user-supplied buffer, Memcheck checks that the buffer is addressable.

  • [用戶提供緩衝區參數]

After the system call, Memcheck updates its tracked information to precisely reflect any changes in memory permissions caused by the system call.

Here's an example of two system calls with invalid parameters:

#include <stdlib.h>
  #include <unistd.h>
  int main( void )
  {
    char* arr  = malloc(10);
    int*  arr2 = malloc(sizeof(int));
    write( 1 /* stdout */, arr, 10 );
    exit(arr2[0]);
  }

You get these complaints ...

Syscall param write(buf) points to uninitialised byte(s)
     at 0x25A48723: __write_nocancel (in /lib/tls/libc-2.3.3.so)
     by 0x259AFAD3: __libc_start_main (in /lib/tls/libc-2.3.3.so)
     by 0x8048348: (within /auto/homes/njn25/grind/head4/a.out)
   Address 0x25AB8028 is 0 bytes inside a block of size 10 alloc'd
     at 0x259852B0: malloc (vg_replace_malloc.c:130)
     by 0x80483F1: main (a.c:5)

  Syscall param exit(error_code) contains uninitialised byte(s)
     at 0x25A21B44: __GI__exit (in /lib/tls/libc-2.3.3.so)
     by 0x8048426: main (a.c:8)

... because the program has (a) tried to write uninitialised junk from the malloc'd block to the standard output, and (b) passed an uninitialised value to exit. Note that the first error refers to the memory pointed to by buf (not buf itself), but the second error refers directly to exit's argument arr2[0].

4.3.6. Overlapping source and destination blocks [源頭和目標塊重疊]

The following C library functions copy some data from one memory block to another (or something similar): memcpy()strcpy()strncpy()strcat()strncat(). The blocks pointed to by their src and dst pointers aren't allowed to overlap. Memcheck checks for this.

[C庫中一些具有拷貝功能的函數,如memcpy()strcpy()strncpy()strcat()strncat(),他們將數據從一個內存塊中拷貝至另一箇中。源頭和目標內存塊不允許有重疊, Memcheck 可以檢查這種錯誤。]

For example:

==27492== Source and destination overlap in memcpy(0xbffff294, 0xbffff280, 21)
==27492==    at 0x40026CDC: memcpy (mc_replace_strmem.c:71)
==27492==    by 0x804865A: main (overlap.c:40)

You don't want the two blocks to overlap because one of them could get partially overwritten by the copying.

You might think that Memcheck is being overly pedantic reporting this in the case where dst is less than src. For example, the obvious way to implement memcpy() is by copying from the first byte to the last. However, the optimisation guides of some architectures recommend copying from the last byte down to the first. Also, some implementations of memcpy() zero dst before copying, because zeroing the destination's cache line(s) can improve performance.

In addition, for many of these functions, the POSIX standards have wording along the lines "If copying takes place between objects that overlap, the behavior is undefined." Hence overlapping copies violate the standard.

The moral of the story is: if you want to write truly portable code, don't make any assumptions about the language implementation.

4.3.7. Memory leak detection [內存漏洞檢測]

Memcheck keeps track of all memory blocks issued in response to calls to malloc/calloc/realloc/new. So when the program exits, it knows which blocks have not been freed.

[Memcheck 保持對所有 malloc/calloc/realloc/new 分配的內存塊的跟蹤,因此當程序退出時,它能檢測出哪一塊未被釋放。]

If --leak-check is set appropriately, for each remaining block, Memcheck scans the entire address space of the process, looking for pointers to the block. Each block fits into one of the three following categories.

[如果 --leak-check 被設置,則 Memcheck 將掃描整個空間地址,尋找指向塊的指針。處理後,在 Memcheck 中可以將塊分類三類:]

  • Still reachable: A pointer to the start of the block is found. This usually indicates programming sloppiness. Since the block is still pointed at, the programmer could, at least in principle, free it before program exit. Because these are very common and arguably not a problem, Memcheck won't report such blocks unless --show-reachable=yes is specified.

  • [仍可被訪問的塊:存在一個指針指向該塊的起始位置。這通常表明編程水平太潮。因爲程序退出後程序員至少要將內存釋放掉,而不應再有指針指向它。Memcheck 通常不會報告此類問題,除非你使用了 --show-reachable=yes 選項,因爲這些問題很常見且存有爭議。]
  • Possibly lost, or "dubious": A pointer to the interior of the block is found. The pointer might originally have pointed to the start and have been moved along, or it might be entirely unrelated. Memcheck deems such a block as "dubious", because it's unclear whether or not a pointer to it still exists.

  • [可能丟失或“有疑問的塊”:指向塊內部的指針被發現。該指針很可能開始時指向塊首,後來被移動,或者最終沒有任何關聯。 Memcheck 認爲這樣的塊爲“有疑問的塊”,因爲不清楚是否還有指針指向它。]
  • Definitely lost, or "leaked": The worst outcome is that no pointer to the block can be found. The block is classified as "leaked", because the programmer could not possibly have freed it at program exit, since no pointer to it exists. This is likely a symptom of having lost the pointer at some earlier point in the program.

  • [明顯丟失或“內存泄漏”:最壞的結果是某些塊已沒有指針指向,卻還能被發現。這樣的塊被歸入“泄漏”類,是因爲程序員在程序終止時沒有釋放,因爲沒有指針指向他們。這很象程序運行前期指針丟失的症狀。]

For each block mentioned, Memcheck will also tell you where the block was allocated. It cannot tell you how or why the pointer to a leaked block has been lost; you have to work that out for yourself. In general, you should attempt to ensure your programs do not have any leaked or dubious blocks at exit.

[對以上每種情況,Memcheck 都會告訴你該塊在何處被分配。但它不會告訴你如何和爲什麼指向該塊的指針丟失。你必須自己進一步確定。總起來說,你應該首先確保程序在退出時不會有任何泄漏或有泄漏傾向的塊存在。]

For example:

8 bytes in 1 blocks are definitely lost in loss record 1 of 14
   at 0x........: malloc (vg_replace_malloc.c:...)
   by 0x........: mk (leak-tree.c:11)
   by 0x........: main (leak-tree.c:39)

88 (8 direct, 80 indirect) bytes in 1 blocks are definitely lost 
                           in loss record 13 of 14
   at 0x........: malloc (vg_replace_malloc.c:...)
   by 0x........: mk (leak-tree.c:11)
   by 0x........: main (leak-tree.c:25)

The first message describes a simple case of a single 8 byte block that has been definitely lost. The second case mentions both "direct" and "indirect" leaks. The distinction is that a direct leak is a block which has no pointers to it. An indirect leak is a block which is only pointed to by other leaked blocks. Both kinds of leak are bad.

[第一部分描述了有8字節塊明確丟失,第二部分提到了“直接”和“間接”泄漏,其區別是直接泄漏爲存在一個內存塊,而沒有指針指向它。間接泄漏則爲存在一個塊,而存有其他泄漏塊指向該塊。兩種情況都不是好事。]

The precise area of memory in which Memcheck searches for pointers is: all naturally-aligned machine-word-sized words found in memory that Memcheck's records indicate is both accessible and initialised.

[以上提到的 Memcheck用於尋找指針的內存塊的確切區域指的是:內存中所有能過找到的自然關聯的、按機器字長度計算的 words, 以上所指內存是指 Memcheck 記錄中表明可被訪問和可被初始化的內存區域。]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章