Valgrind學習總結

Valgrind查找內存泄露利器

Valgrind是一個GPL的軟件,用於Linux(For x86, amd64 and ppc32)程序的內存調試和代碼剖析。你可以在它的環境中運行你的程序來監視內存的使用情況,比如C 語言中的malloc和free或者 C++中的new和 delete。使用Valgrind的工具包,你可以自動的檢測許多內存管理和線程的bug,避免花費太多的時間在bug尋找上,使得你的程序更加穩固。

Valgrind的主要功能

Valgrind工具包包含多個工具,如Memcheck,Cachegrind,Helgrind, Callgrind,Massif。下面分別介紹個工具的作用

Memcheck 工具主要檢查下面的程序錯誤:

使用未初始化的內存 (Use of uninitialised memory)

使用已經釋放了的內存 (Reading/writing memory after it has been free’d)

使用超過 malloc分配的內存空間(Reading/writing off the end of malloc’d blocks)

對堆棧的非法訪問 (Reading/writing inappropriate areas on the stack)

申請的空間是否有釋放 (Memory leaks – where pointers to malloc’d blocks are lost forever)

malloc/free/new/delete申請和釋放內存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])

src和dst的重疊(Overlapping src and dst pointers in memcpy() and related functions)

Callgrind

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

Cachegrind

它模擬 CPU中的一級緩存I1,D1和L2二級緩存,能夠精確地指出程序中 cache的丟失和命中。如果需要,它還能夠爲我們提供cache丟失次數,內存引用次數,以及每行代碼,每個函數,每個模塊,整個程序產生的指令數。這對優化程序有很大的幫助。

Helgrind

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

Massif

堆棧分析器,它能測量程序在堆棧中使用了多少內存,告訴我們堆塊,堆管理塊和棧的大小。

Massif能幫助我們減少內存的使用,在帶有虛擬內存的現代系統中,它還能夠加速我們程序的運行,減少程序停留在交換區中的機率。

安裝

http://valgrind.org/downloads/current.html
源碼下載
http://valgrind.org/docs/manual/manual.html
文檔

tar jxvf valgrind-3.14.0.tar.bz2

cd valgrind-3.14.0/

./autogen.sh

./configure --prefix=/home/renz/rz/opt/valgrind

make

make install

應用

1、檢查內存錯誤:

例如我們原來有一個程序sec_infod,這是一個用gcc –g參數編譯的程序,運行它需要:

#./a.out

如果我們想用valgrind的內存檢測工具,我們就要用如下方法調用:

#valgrind --leak-check=full --show-reachable=yes --trace-children= yes   ./a.out

其中--leak-check=full指的是完全檢查內存泄漏,--show-reachable=yes是顯示內存泄漏的地點,--trace-children=yes是跟入子進程。

如果您的程序是會正常退出的程序,那麼當程序退出的時候valgrind自然會輸出內存泄漏的信息。如果您的程序是個守護進程,那麼也不要緊,我們 只要在別的終端下殺死memcheck進程(因爲valgrind默認使用memcheck工具,就是默認參數—tools=memcheck):

#killall memcheck

這樣我們的程序(./a.out)就被kill了

2、檢查代碼覆蓋和性能瓶頸:

我們調用valgrind的工具執行程序:

#valgrind --tool=callgrind ./sec_infod

會在當前路徑下生成callgrind.out.pid(當前生產的是callgrind.out.19689),如果我們想結束程序,可以:

#killall callgrind

然後我們看一下結果:

#callgrind_annotate --auto=yes callgrind.out.19689   >log

#vim log

用法:

概要用法:

       valgrind [[valgrind] [options]] [your-program] [[your-program-options]]

一般像下面這樣調用Valgrind:

valgrind program args

這樣將在Valgrind使用Memcheck運行程序program(帶有參數args)。內存檢查執行一系列的內存檢查功能,包括檢測訪問未初始化的內存,已經分配內存的錯誤使用(兩次釋放,釋放後再訪問,等等)並檢查內存泄漏。

可用--tool指定使用其它工具:

valgrind --tool=toolname program args

可使用的工具如下:

cachegrind是一個緩衝模擬器。它可以用來標出你的程序每一行執行的指令數和導致的緩衝不命中數。

callgrind在cachegrind基礎上添加調用追蹤。它可以用來得到調用的次數以及每次函數調用的開銷。作爲對cachegrind的補充,callgrind可以分別標註各個線程,以及程序反彙編輸出的每條指令的執行次數以及緩存未命中數。

helgrind能夠發現程序中潛在的條件競爭。

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

massif是一個堆剖析器,它測量你的程序使用了多少堆內存。

memcheck是一個細粒度的的內存檢查器。

none沒有任何功能。它一般用於Valgrind的調試和基準測試。

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

-h --help

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

--help-debug

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

--version

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

-q --quiet

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

-v --verbose

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

-d

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

--tool= [default: memcheck]

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

--trace-children= [default: no]

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

--track-fds= [default: no]

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

--time-stamp= [default: no]

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

--log-fd= [default: 2, stderr]

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

--log-file=

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

--log-file-exactly=

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

--log-file-qualifier=

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

--log-socket=

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

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

--xml= [default: no]

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

--xml-user-comment=

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

--demangle= [default: yes]

打開/關閉C++的名字自動解碼。默認打開。當打開時,Valgrind將嘗試着把編碼過的C++名字自動轉回初始狀態。這個解碼器可以處理g++版本爲2.X,3.X或4.X生成的符號。

一個關於名字編碼解碼重要的事實是,禁止文件中的解碼函數名仍然使用他們未解碼的形式。Valgrind在搜尋可用的禁止條目時不對函數名解碼,因爲這將使禁止文件內容依賴於Valgrind的名字解碼機制狀態, 會使速度變慢,且無意義。

--num-callers= [default: 12]

默認情況下,Valgrind顯示12層函數調用的函數名有助於確定程序的位置。可以通過這個選項來改變這個數字。這樣有助在嵌套調用的層次很深時確定程序的位置。注意錯誤信息通常只回溯到最頂上的4個函數。(當前函數,和它的3個調用者的位置)。所以這並不影響報告的錯誤總數。

這個值的最大值是50。注意高的設置會使Valgrind運行得慢,並且使用更多的內存,但是在嵌套調用層次比較高的程序中非常實用。

--error-limit= [default: yes]

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

--error-exitcode= [default: 0]

指定如果Valgrind在運行過程中報告任何錯誤時的退出返回值,有兩種情況;當設置爲默認值(零)時,Valgrind返回的值將是它模擬運行的程序的返回值。當設置爲非零值時,如果Valgrind發現任何錯誤時則返回這個值。

在Valgrind做爲一個測試工具套件的部分使用時這將非常有用,因爲使測試工具套件只檢查Valgrind返回值就可以知道哪些測試用例Valgrind報告了錯誤。

--show-below-main= [default: no]

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

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

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

--gen-suppressions= [default: no]

當設置爲yes時,Valgrind將會在每個錯誤顯示之後自動暫停並且打印下面這一行:

                  ---- Print suppression ? --- [Return/N/n/Y/y/C/c] ----

這個提示的行爲和--db-attach選項(見下面)相同。

如果選擇是,Valgrind會打印出一個錯誤的禁止條目,你可以把它剪切然後粘帖到一個文件,如果不希望在將來再看到這個錯誤信息。

當設置爲all時,Valgrind會對每一個錯誤打印一條禁止條目,而不向用戶詢問。

這個選項對C++程序非常有用,它打印出編譯器調整過的名字。

注意打印出來的禁止條目是儘可能的特定的。如果需要把類似的條目歸納起來,比如在函數名中添加通配符。並且,有些時候兩個不同的錯誤也會產生同樣的禁止條目,這時Valgrind就會輸出禁止條目不止一次,但是在禁止條目的文件中只需要一份拷貝(但是如果多於一份也不會引起什麼問題)。並且,禁止條目的名字像<在這兒輸入一個禁止條目的名字>;名字並不是很重要,它只是和-v選項一起使用打印出所有使用的禁止條目記錄。

--db-attach= [default: no]

當這個選項打開時,Valgrind將會在每次打印錯誤時暫停並打出如下一行:

                  ---- Attach to debugger ? --- [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= [default: gdb -nw %f %p]

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

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

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

--input-fd= [default: 0, stdin]

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

--max-stackframe= [default: 2000000]

棧的最大值。如果棧指針的偏移超過這個數量,Valgrind則會認爲程序是切換到了另外一個棧執行。

如果在程序中有大量的棧分配的數組,你可能需要使用這個選項。

valgrind保持對程序棧指針的追蹤。如果棧指針的偏移超過了這個數量,Valgrind假定你的程序切換到了另外一個棧,並且Memcheck行爲與棧指針的偏移沒有超出這個數量將會不同。通常這種機制運轉得很好。然而,如果你的程序在棧上申請了大的結構,這種機制將會表現得愚蠢,並且Memcheck將會報告大量的非法棧內存訪問。這個選項允許把這個閥值設置爲其它值。

應該只在Valgrind的調試輸出中顯示需要這麼做時才使用這個選項。在這種情況下,它會告訴你應該指定的新的閥值。

普遍地,在棧中分配大塊的內存是一個壞的主意。因爲這很容易用光你的棧空間,尤其是在內存受限的系統或者支持大量小堆棧的線程的系統上,因爲Memcheck執行的錯誤檢查,對於堆上的數據比對棧上的數據要高效很多。如果你使用這個選項,你可能希望考慮重寫代碼在堆上分配內存而不是在棧上分配。

下面是使用實際代碼進行的例子

具體的例子在https://blog.csdn.net/andylauren/article/details/82261743文章中,有改動的地方也在下面貼出源碼

    p_elec = (testlist_s *)malloc(sizeof(testlist_s));//此處申請未釋放
    INIT_LIST_HEAD(&(p_elec->list));
    p_elec->num = 8;
    p_elec->str = "NO. c";
    p_elea = (testlist_s *)malloc(sizeof(testlist_s));//此處申請未釋放
    INIT_LIST_HEAD(&(p_elea->list));
    p_elea->num = 6;
    p_elea->str = "NO. a";
    p_eleb = (testlist_s *)malloc(sizeof(testlist_s));//此處申請未釋放
    INIT_LIST_HEAD(&(p_eleb->list));
    p_eleb->num = 7;
    p_eleb->str = "NO. b";

int main(int argc, char *argv[])
{
    mtrace();
	element_new();
	LIST_HEAD(head);//雙向鏈表頭指針
	list_empty(&head)?printf("list is empty!\n"):printf("list is not empty!\n");
	
	testlist_s *pos, *temp_ele;
	list_add(&p_ele1->list, &head);//頭插入雙向鏈表中  head->ele1
	print_list_for_each(pos, &head, list);
	list_add(&p_ele2->list, &head);//頭插入雙向鏈表中  head->ele2->ele1
	print_list_for_each(pos, &head, list);
	list_add(&p_ele3->list, &head);//頭插入雙向鏈表中  head->ele3->ele2->ele1
	print_list_for_each(pos, &head, list);
	list_add_tail(&p_ele4->list, &head);//尾插入雙向鏈表中 head->ele3->ele2->ele1->ele4
	print_list_for_each(pos, &head, list);
	list_del(&p_ele1->list);//刪除1元素 head->ele3->ele2->ele4
    free(p_ele1);
	print_list_for_each(pos, &head, list);
	list_replace(&p_ele3->list, &p_ele5->list);//5替換3  head->ele5->ele2->ele4
    free(p_ele3);
	print_list_for_each(pos, &head, list);
	
	list_empty(&head)?printf("list is empty!\n"):printf("list is not empty!\n");
	list_is_last(&p_ele4->list, &head)?printf("ele4 is last!\n"):printf("ele4 is not last!\n");
	list_is_last(&p_ele3->list, &head)?printf("ele3 is last!\n"):printf("ele3 is not last!\n");//p_ele3這裏已經釋放,此處會報錯
	
	list_for_each_entry_safe(pos, temp_ele, &head, list){
		printf("num:%d, str:%s\n", pos->num, pos->str);
        list_del(&(pos->list));
        free(pos);
	}
	printf("\n");
	
	printf("list len:%d\n", list_len(&head));
	
	return 0;
}

輸出如下

renz@media-Lenovo-Product:~/rz/list$ gcc list.c -o list -g
renz@media-Lenovo-Product:~/rz/list$ ../opt/valgrind/bin/valgrind --leak-check=full --show-leak-kinds=all ./list
==20493== Memcheck, a memory error detector
==20493== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==20493== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==20493== Command: ./list
==20493== 
list is empty!
list:&head
num:1, str:NO. 1

list:&head
num:2, str:NO. 2
num:1, str:NO. 1

list:&head
num:3, str:NO. 3
num:2, str:NO. 2
num:1, str:NO. 1

list:&head
num:3, str:NO. 3
num:2, str:NO. 2
num:1, str:NO. 1
num:4, str:NO. 4

list:&head
num:3, str:NO. 3
num:2, str:NO. 2
num:4, str:NO. 4

list:&head
num:5, str:NO. 5
num:2, str:NO. 2
num:4, str:NO. 4

list is not empty!
ele4 is last!
==20493== Invalid read of size 8
==20493==    at 0x400822: list_is_last (rt_list.h:161)
==20493==    by 0x4011A5: main (list.c:152)
==20493==  Address 0x5205110 is 16 bytes inside a block of size 32 free'd
==20493==    at 0x4C2EF50: free (vg_replace_malloc.c:530)
==20493==    by 0x401091: main (list.c:147)
==20493==  Block was alloc'd at
==20493==    at 0x4C2DE56: malloc (vg_replace_malloc.c:299)
==20493==    by 0x400AA4: element_new (list.c:57)
==20493==    by 0x400CDB: main (list.c:130)
==20493== 
ele5 is not last!
num:5, str:NO. 5
num:2, str:NO. 2
num:4, str:NO. 4

list len:0
==20493== 
==20493== HEAP SUMMARY:
==20493==     in use at exit: 96 bytes in 3 blocks
==20493==   total heap usage: 9 allocs, 6 frees, 1,280 bytes allocated
==20493== 
==20493== 32 bytes in 1 blocks are still reachable in loss record 1 of 3
==20493==    at 0x4C2DE56: malloc (vg_replace_malloc.c:299)
==20493==    by 0x400BAF: element_new (list.c:69)
==20493==    by 0x400CDB: main (list.c:130)
==20493== 
==20493== 32 bytes in 1 blocks are still reachable in loss record 2 of 3
==20493==    at 0x4C2DE56: malloc (vg_replace_malloc.c:299)
==20493==    by 0x400C08: element_new (list.c:73)
==20493==    by 0x400CDB: main (list.c:130)
==20493== 
==20493== 32 bytes in 1 blocks are still reachable in loss record 3 of 3
==20493==    at 0x4C2DE56: malloc (vg_replace_malloc.c:299)
==20493==    by 0x400C61: element_new (list.c:77)
==20493==    by 0x400CDB: main (list.c:130)
==20493== 
==20493== LEAK SUMMARY:
==20493==    definitely lost: 0 bytes in 0 blocks
==20493==    indirectly lost: 0 bytes in 0 blocks
==20493==      possibly lost: 0 bytes in 0 blocks
==20493==    still reachable: 96 bytes in 3 blocks
==20493==         suppressed: 0 bytes in 0 blocks
==20493== 
==20493== For counts of detected and suppressed errors, rerun with: -v
==20493== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

可以看出內存泄漏的位置都已經明確的指出。

callgrind ------> 它主要用來檢查程序中函數調用過程中出現的問題

renz@media-Lenovo-Product:~/rz/list$ ../opt/valgrind/bin/valgrind --tool=callgrind -v  ./list
==21861== Callgrind, a call-graph generating cache profiler
==21861== Copyright (C) 2002-2017, and GNU GPL'd, by Josef Weidendorfer et al.
==21861== Using Valgrind-3.14.0-353a3587bb-20181007X and LibVEX; rerun with -h for copyright info
==21861== Command: ./list
==21861== 
--21861-- Valgrind options:
--21861--    --tool=callgrind
--21861--    -v
--21861-- Contents of /proc/version:
--21861--   Linux version 4.4.0-143-generic (buildd@lgw01-amd64-041) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10) ) #169-Ubuntu SMP Thu Feb 7 07:56:38 UTC 2019
--21861-- 
--21861-- Arch and hwcaps: AMD64, LittleEndian, amd64-cx16-lzcnt-rdtscp-sse3-avx-avx2-bmi
--21861-- Page sizes: currently 4096, max supported 4096
--21861-- Valgrind library directory: /home/renz/rz/opt/valgrind/lib/valgrind
==21861== For interactive control, run 'callgrind_control -h'.
--21861-- Reading syms from /home/renz/rz/list/list
--21861-- Reading syms from /lib/x86_64-linux-gnu/ld-2.23.so
--21861--   Considering /lib/x86_64-linux-gnu/ld-2.23.so ..
--21861--   .. CRC mismatch (computed aa979a42 wanted 9019bbb7)
--21861--   Considering /usr/lib/debug/lib/x86_64-linux-gnu/ld-2.23.so ..
--21861--   .. CRC is valid
--21861-- Reading syms from /home/renz/rz/opt/valgrind/lib/valgrind/callgrind-amd64-linux
--21861--    object doesn't have a dynamic symbol table
--21861-- Scheduler: using generic scheduler lock implementation.
==21861== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-21861-by-renz-on-???
==21861== embedded gdbserver: writing to   /tmp/vgdb-pipe-to-vgdb-from-21861-by-renz-on-???
==21861== embedded gdbserver: shared mem   /tmp/vgdb-pipe-shared-mem-vgdb-21861-by-renz-on-???
==21861== 
==21861== TO CONTROL THIS PROCESS USING vgdb (which you probably
==21861== don't want to do, unless you know exactly what you're doing,
==21861== or are doing some strange experiment):
==21861==   /home/renz/rz/opt/valgrind/lib/valgrind/../../bin/vgdb --pid=21861 ...command...
==21861== 
==21861== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==21861==   /path/to/gdb ./list
==21861== and then give GDB the following command
==21861==   target remote | /home/renz/rz/opt/valgrind/lib/valgrind/../../bin/vgdb --pid=21861
==21861== --pid is optional if only one valgrind process is running
==21861== 
--21861-- Reading syms from /home/renz/rz/opt/valgrind/lib/valgrind/vgpreload_core-amd64-linux.so
--21861-- Reading syms from /lib/x86_64-linux-gnu/libc-2.23.so
--21861--   Considering /lib/x86_64-linux-gnu/libc-2.23.so ..
--21861--   .. CRC mismatch (computed 7a8ee3e4 wanted a5190ac4)
--21861--   Considering /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.23.so ..
--21861--   .. CRC is valid
list is empty!
list:&head
num:1, str:NO. 1

list:&head
num:2, str:NO. 2
num:1, str:NO. 1

list:&head
num:3, str:NO. 3
num:2, str:NO. 2
num:1, str:NO. 1

list:&head
num:3, str:NO. 3
num:2, str:NO. 2
num:1, str:NO. 1
num:4, str:NO. 4

list:&head
num:3, str:NO. 3
num:2, str:NO. 2
num:4, str:NO. 4

list:&head
num:5, str:NO. 5
num:2, str:NO. 2
num:4, str:NO. 4

list is not empty!
ele4 is last!
ele5 is not last!
num:5, str:NO. 5
num:2, str:NO. 2
num:4, str:NO. 4

list len:0
==21861== 
--21861-- Start dumping at BB 40522 (Prg.Term.)...
--21861-- Dump to /home/renz/rz/list/callgrind.out.21861
--21861-- Dumping done.
==21861== Events    : Ir
==21861== Collected : 190206
==21861== 
==21861== I   refs:      190,206

生成可視化的圖形

生成可視化的圖形需要下載gprof2dot:https://pypi.org/project/gprof2dot/#files

這是個Python腳本,把它下載之後修改其權限chmod +7 gprof2dot.py ,並把這個腳本添加到$PATH路徑中的任一文件夾下,我是將它放到了/usr/bin目錄下,這樣就可以直接在終端下執行gprof2dot.py了。

首先執行 gcc -pg -o tmp tmp.c,然後運行該程序./tmp,程序運行完成後會在當前目錄下生成gmon.out文件(這個文件gprof在分析程序時需要),再執行gprof ./tmp | gprof2dot.py |dot -Tpng -o report.png,打開report.png結果:

再來看 Callgrind的生成調用圖過程吧,執行:valgrind --tool=callgrind ./tmp,執行完成後在目錄下生成"callgrind.out.XXX"的文件這是分析文件,可以直接利用:callgrind_annotate callgrind.out.XXX 打印結果,也可以使用:gprof2dot.py -f callgrind callgrind.out.XXX |dot -Tpng -o report.png 來生成圖形化結果:

它生成的結果非常詳細,甚至連函數入口,及庫函數調用都標識出來了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章