天天看點

【原創】Valgrind 基礎

valgrind 是一種用于建構動态分析工具集的架構; 

valgrind 工具能夠自動探測許多種類的記憶體管理 bug 和線程 bug ,能夠幫助你在細微處進行程式調優; 

你可以基于 valgrind 建構新工具集; 

valgrind 目前釋出版包含了六種産品品質相關工具: 

一種記憶體錯誤探測器

兩種線程錯誤探測器

一種 cache 和分支預測分析工具

一種 call-graph generating cache 和分支預測分析工具

一種堆分析工具

valgrind 還包含了三種實驗性工具: 

heap/stack/global 數組越界探測器

a second heap profiler that examines how heap blocks are used

a simpoint basic block vector generator

valgrind 與 cpu 、作業系統,以及編譯器和基礎 c 庫都是強相關的;移植 valgrind 是比較困難的。 

盡管如此,valgrind 已經支援如下各種平台: 

- x86/linux

- amd64/linux

- ppc32/linux

- ppc64/linux

- arm/linux

- x86/macosx

- amd64/macosx

- s390x/linux

- mips32/linux

- mips64/linux

note that amd64 is just another name for x86_64 , and valgrind runs fine on intel processors.   

also note that the core of macosx is called " darwin " and this name is used sometimes. 

valgrind is licensed under the gnu general public license, version 2. 

however: if you contribute code, you need to make it available as gpl version 2 or later, and not 2-only. 

基于 tar.bz2 源碼包的安裝: 

run ./configure, with some options if you wish.  the only interesting one is the usual --prefix=/where/you/want/it/installed.

run "make".

run "make install", possibly as root if the destination permissions require that.

see if it works.  try "valgrind ls -l".  either this works, or it bombs out with some complaint. 

important !  do not move the valgrind installation into a place different from that specified by --prefix at build time.  

this will cause things to break in subtle ways, mostly when valgrind handles fork/exec calls. 

the valgrind distribution includes the following debugging and profiling tools: 

memcheck 

memcheck 用于探測程式中存在的記憶體管理問題,主要針對 c 和 c++ 程式; 

當程式運作在 memcheck 監控之下時,所有針對記憶體的讀寫操作都将被檢查,所有 malloc/new/free/delete 調用都會被攔截; 

是以,memcheck 能夠探測到你的程式是否存在以下的問題: 

accesses memory it shouldn't (areas not yet allocated, areas that have been freed, areas past the end of heap blocks, inaccessible areas of the stack).

通路了不該通路的記憶體(尚未配置設定過的區域;已經被釋放掉的區域;超過堆塊邊界的區域;棧中不可通路的區域)

uses uninitialised values in dangerous ways.

以危險方式使用未初始化的值

leaks memory.

存在記憶體洩露

does bad frees of heap blocks (double frees, mismatched frees).

針對堆塊執行了錯誤的 free 行為(double free 或 mismatched free)

passes overlapping source and destination memory blocks to memcpy() and related functions.

基于 memcpy() 或其他相關函數操作了存在重疊的源和目的位址所指向的記憶體塊;

memcheck 會在遇到上述錯誤的時候立即進行報告,同時給出錯誤發生對應的源碼行号,以及觸發該錯誤時對應的函數調用棧; 

memcheck 是基于位元組進行尋址跟蹤,基于比特進行 value 的初始化跟蹤;是以,其可以探測出單獨一比特未初始化資料的使用,而不會作為比特運作錯誤進行報告; 

基于 memcheck 運作程式時将會比正常運作慢 10-30 倍; 

cachegrind 

cachegrind 是一種 cache 分析工具; 

cachegrind 能夠針對 cpu 進行 i1, d1 和 l2 cache 的詳細模拟,并精确的定位出代碼中導緻 cache miss 的地方; 

cachegrind 能夠确定出 cache miss 的次數、記憶體引用情況,以及會觸發 cache miss 的每一行代碼,每一個函數,每一個子產品,并給出整個程式的情況摘要; 

cachegrind 對于任何語言編寫的程式都非常有用; 

基于 cachegrind 運作程式時将會比正常運作慢 20-100 倍; 

callgrind 

callgrind 是針對 cachegrind 的擴充; 

callgrind 提供了 cachegrind 所能提供的全部資訊,還額外提供了關于 callgraphs 的資訊; 

callgrind 在 valgrind 的 3.2.0 主釋出版中被加入; 

另外還有一個名為 kcachegrind 的可視化工具,可以對 callgrind 收集到的資訊盡心更好的展示; 

massif 

massif 是一種堆分析工具; 

massif 通過對程式堆擷取正常快照,進而展開詳細堆分析; 

massif 能夠生成随時間變化的堆使用圖,其中會給出程式的哪些部分進行了絕大部分的記憶體配置設定操作; 

作為圖的補充,通過 text 或 html 檔案能夠确定配置設定記憶體最多地方的更多資訊; 

基于 massif 運作程式時将會比正常運作慢 20 倍; 

helgrind 

helgrind 是一種線程調試器,用于查找多線程程式中的資料競争; 

helgrind 會查出特定類型記憶體的位置:被多于一個(posix p-)thread 所通路,但是卻沒有發現保證通路一緻性的鎖; 

這類位置已經隐式表明了多線程同步的缺失,将會導緻非常難于發現的時序問題; 

helgrind 對于任何使用 pthreads 的程式都非常有用; 

drd 

drd 是一種檢測多線程 c 和 c++ 程式錯誤的工具; 

drd 對于任何使用 posix 線程原語的程式有效;或對于任何建構與 posix 線程原語之上的、使用線程概念的程式有效; 

盡管 helgrind 能夠檢測出鎖定順序違反等問題,但對于大部分程式來說,drd 在進行分析時将花費更少的記憶體; 

lackey, nulgrind 

lackey and nulgrind are also included in the valgrind distribution. they don't do very much, and are there for testing and demonstrative purposes. 

other tools 

several other valgrind tools have been created: you can find a list of them on the variants / patches page. 

1.1. how do you pronounce "valgrind"? 

the "val" as in the word "value". the "grind" is pronounced with a short 'i' -- ie. "grinned" (rhymes with "tinned") rather than "grined" (rhymes with "find"). 

4.4. my program crashes normally, but doesn't under valgrind, or vice versa. what's happening? 

基于 valgrind 運作目标程式和直接運作目标程式兩種方式存在些許不同;例如,記憶體布局會不同,線程排程方式會不同。 

      在大多數情況下,上述差異不會導緻什麼問題,但當你的程式本身存在 bug 的時候,可能會遇到一些問題。例如,如果你的程式原本會崩潰于錯誤的通路了非法記憶體位址的情況,而在基于 valgrind 運作時可能,該位址可能就是合法的記憶體位址了。另一種情況是,如果你的程式存在資料競争,但在 valgrind 下可能不會顯現出來。 

      對于上面的情況,你是無法做出任何應對的,因為這就是 valgrind 的工作方式,即 valgrind 無法精确的複制出可執行程式的本地執行環境。當出現你的程式崩潰于記憶體錯誤,但在 valgrind 下無法出現時,大多數情況下,memcheck 工具應該都能檢測出錯誤的記憶體操作。 

4.5. memcheck doesn't report any errors and i know my program has errors. 

産生上述問題可能有兩種原因。 

      首先,預設情況下,valgrind 隻會跟蹤 top-level 程序,是以,如果你的程式會建立子程序,則預設情況下,子程序 不會被 valgrind 所跟蹤。同樣的,如果你的程式時通過 shell 腳本、perl 腳本,或者其他類似的腳本啟動的,那麼 valgrind 将會跟蹤 shell 或 perl 解析器,或其他類似的東西。 

為了跟蹤子程序,需要使用  --trace-children=yes 選項。 

      如果你打算跟蹤的程序是由很多程序構成,最好的方式是将輸出結構通過網絡發送到某處。可以通過指定  --log-socket=127.0.0.1:12345 選項(将輸出結果發送到本地 12345 端口)達成此目的。 你可以使用  valgrind-listener 程式建立該端口的監聽。 

當然,你必須事先啟動監聽程序才能正常工作。 

      其次,大多數 valgrind 工具的工作模式為,通過替換程式中的某些函數,例如 malloc ,為自定義版本,來達成探測和分析的目的 ,如果你的程式是靜态連結的 ,則不會程序這種替換,進而無法程序有效的檢測。例如,可能從 memcheck 輸出中看到輸出 

而實際上,你明确知道自己調用了 malloc 。解決辦法就是使用  --soname-synonyms=somalloc=none 設定,或者在目标程式中避免使用靜态連結。 

      還存在另外一種無法進行替換的情況,即使用了另外一種 malloc 庫,例如 tcmalloc 或 jemalloc 等。在這種情況下,需要通過指定  --soname-synonyms=somalloc=zzzz 選項(其中 zzzz 為替換用的  malloc 庫的 soname),以允許 valgrind 進行相應替換。 

4.6. why doesn't memcheck find the array overruns in this program? 

非常不幸的是,memcheck 無法針對全局數組或棧數組進行邊界檢查。我們很想做到,但是目前尚未找到一種合理的實作方式,令這種檢查與 memcheck 的工作方式相符,是以,非常抱歉。 

然而,實驗工具  sgcheck 能夠探測出這種錯誤。 你可以通過指定  --tool=exp-sgcheck 選項運作 valgrind 程序相關檢查,唯一需要明确的是,該工具可能沒有 memcheck 那麼健壯。 

5.2. with memcheck's memory leak detector, what's the difference between "definitely lost", "indirectly lost", "possibly lost", "still reachable", and "suppressed"? 

詳細資訊可以參見使用者手冊中的 memcheck 章節。 

簡要說明如下: 

"definitely lost" 意味着你的程式一定存在記憶體洩露;

"indirectly lost" 意味着你的程式一定存在記憶體洩露,并且洩露情況和指針結構相關(例如,如果二叉樹的根節點被判定為"definitely lost",則其所有子節點将被判定為"indirectly lost");如果你正确修複了類型為 "definitely lost" 的洩露,那麼類型為 "indirectly lost" 的洩露應該會随着消失;

"possibly lost" 意味着你的程式一定存在記憶體洩露,除非你是故意進行着不符合正常的操作,例如将指針指向某個已配置設定記憶體塊的中間位置。參見使用者手冊查詢一些可能的情況。可以指定 --show-possibly-lost=no 選項屏蔽掉這類報告資訊。

"still reachable" 意味着你的程式可能是沒問題的,但确實沒有釋放掉一些本可以釋放的記憶體。這種情況是很常見的,并且通常基于合理的理由。 可以通過 --show-reachable=yes 選項控制是否輸出相應的報告資訊。

"suppressed" 意味着有些洩露資訊被壓制了。在預設的 suppression 檔案中可以看到一些 suppression 相關設定。理論上講,你可以忽略這類錯誤。

5.4. is it possible to attach valgrind to a program that is already running? 

不可以,因為 valgrind 為程式運作使用了完全不同的環境設定,例如采用了不同的記憶體布局方案等。是以 valgrind 需要在程式啟動伊始就進行完全控制。

it is possible to achieve something like this by running your program without any instrumentation (which involves a slow-down of about 5x, less than that of most tools), and then adding instrumentation once you get to a point of interest. support for this must be provided by the tool, however, and callgrind is the only tool that currently has such support. see the instructions on the callgrind_control program for details.