天天看點

Linux C代碼調試除了GDB還可以用Valgrind

作者:亞洲程式員盟主

今天調試代碼,發現了一個很好用得工具,寫篇筆記,分享給大家。

Valgrind 是一款在 Linux 系統下常用的記憶體檢查工具。它可以用來發現程式中的記憶體洩漏、記憶體越界、使用未初始化的記憶體等錯誤。本文将介紹 Valgrind 工具的基本用法和一些常見的選項。

安裝

Valgrind 可以通過系統包管理器進行安裝。在 Ubuntu 上可以使用以下指令進行安裝:

sudo apt-get install valgrind           

安裝完成後,可以通過以下指令檢查 Valgrind 版本:

valgrind --version           

基本用法

Valgrind 工具需要運作在 Valgrind 的虛拟機上,是以需要使用以下指令運作程式:

valgrind [valgrind選項] <要運作的程式>           

例如,要檢查一個名為 myprogram 的可執行檔案,可以使用以下指令:

valgrind ./myprogram           

Valgrind 将會對 myprogram 進行檢查,并輸出相關的報告。如果程式中存在記憶體錯誤,Valgrind 将會列印出相應的錯誤資訊。

常見選項

--leak-check

使用 --leak-check 選項可以檢查程式中的記憶體洩漏。該選項的值可以是 yes、no 或 full。預設值為 no。

valgrind --leak-check=full ./myprogram           

--show-reachable

使用 --show-reachable 選項可以檢查程式中所有可達但未被釋放的記憶體塊。

valgrind --show-reachable=yes ./myprogram           

--track-origins

使用 --track-origins 選項可以跟蹤使用未初始化的記憶體變量的來源。該選項可以幫助找到使用未初始化記憶體的代碼位置。

valgrind --track-origins=yes ./myprogram           

--tool

使用 --tool 選項可以指定要使用的 Valgrind 工具。Valgrind 工具包括 memcheck、cachegrind、callgrind 等。

valgrind --tool=memcheck ./myprogram           

--vgdb

使用 --vgdb 選項可以将 Valgrind 與 gdb 調試器結合使用。這個選項将啟動 Valgrind 時附帶 gdbserver,并且将程式暫停等待 gdb 的連接配接。

valgrind --vgdb=yes ./myprogram           

工具

補充一下,除了上述常見的錯誤類型,Valgrind 還可以檢測其他類型的錯誤,比如記憶體洩漏、不正确的記憶體使用、線程問題等等。以下是一些常用的 Valgrind 工具:

1. Memcheck

Memcheck 是 Valgrind 的預設工具,用于檢測記憶體相關錯誤,比如使用未初始化的記憶體、使用已經釋放的記憶體、記憶體越界等等。

使用方式:

valgrind --tool=memcheck [program name]           

2. Helgrind

Helgrind 用于檢測線程相關的錯誤,比如競态條件、死鎖、未同步的記憶體通路等等。

使用方式:

valgrind --tool=helgrind [program name]           

3. Cachegrind

Cachegrind 用于檢測程式的緩存通路情況,可以幫助我們優化程式的性能。

使用方式:

valgrind --tool=cachegrind [program name]           

4. Callgrind

Callgrind 用于分析程式的函數調用情況,可以生成函數調用圖和統計資訊。

使用方式:

valgrind --tool=callgrind [program name]           

以上是一些常用的 Valgrind 工具及其使用方式,根據具體的需求選擇相應的工具進行檢測和優化。

常見錯誤

當使用 Valgrind 進行記憶體檢查時,會遇到多種不同的錯誤和警告資訊。下面是一些常見的錯誤示例及其分析。

1. Invalid read/write of size X

這種錯誤通常是由于越界通路記憶體或者使用了未初始化的記憶體引起的。例如:

==12345== Invalid read of size 4
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)
==12345==  Address 0x5a5a5a5a is not stack'd, malloc'd or (recently) free'd

==12345== Invalid write of size 4
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)
==12345==  Address 0x5a5a5a5a is not stack'd, malloc'd or (recently) free'd           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式試圖通路位址 0x5a5a5a5a,這個位址未被配置設定給程式或者已經被釋放了。這是一個非法的記憶體通路行為。

2. Conditional jump or move depends on uninitialised value(s)

這種錯誤通常是由于使用未初始化的變量進行條件分支或指派操作引起的。例如:

==12345== Conditional jump or move depends on uninitialised value(s)
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式使用了一個未初始化的變量進行條件分支或指派操作。這可能會導緻程式出現未定義的行為。

3. Memory leak

這種錯誤通常是由于程式配置設定了記憶體但是沒有及時釋放引起的。例如:

==12345== 100 bytes in 1 blocks are definitely lost in loss record 1 of 2
==12345==    at 0x4C2FB0F: malloc (vg_replace_malloc.c:299)
==12345==    by 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式配置設定了 100 位元組的記憶體空間,但是在程式結束時沒有釋放這些記憶體,造成了記憶體洩漏。

以上是一些常見的 Valgrind 錯誤示例及其分析。在實際的程式設計過程中,使用 Valgrind 可以幫助我們及早發現搞定記憶體洩漏和錯誤不僅可以避免程式崩潰,也有助于提高程式性能和穩定性,是以使用 Valgrind 進行記憶體檢查是非常有益的。

4. Use of uninitialised value of size X

這種錯誤通常是由于使用未初始化的變量引起的。例如:

==12345== Use of uninitialised value of size 4
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式使用了一個未初始化的 4 位元組變量。這可能會導緻程式出現未定義的行為。

5. Syscall param write(buf) points to uninitialised byte(s)

這種錯誤通常是由于調用系統函數時,向未初始化的記憶體寫入資料引起的。例如:

==12345== Syscall param write(buf) points to uninitialised byte(s)
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式調用了寫入資料的系統函數,但是向一個未初始化的記憶體區域寫入了資料。這可能會導緻程式出現未定義的行為。

6. Invalid free()

這種錯誤通常是由于釋放了未配置設定或已經被釋放的記憶體空間引起的。例如:

==12345== Invalid free() / delete / delete[] / realloc()
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)
==12345==  Address 0x5a5a5a5a is not stack'd, malloc'd or (recently) free'd           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式試圖釋放一個未配置設定或已經被釋放的記憶體空間,這是非法的。

7. Uninitialised value was created by a heap allocation

這種錯誤通常是由于使用未初始化的堆記憶體引起的。例如:

==12345== Conditional jump or move depends on uninitialised value(s)
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)
==12345==  Uninitialised value was created by a heap allocation
==12345==    at 0x4C2FB0F: malloc (vg_replace_malloc.c:299)
==12345==    by 0x1234567: myFunction (myFile.c:10)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式使用了一個未初始化的值,該值是通過堆記憶體配置設定函數 malloc 建立的。這可能會導緻程式出現未定義的行為。

8. Conditional jump or move depends on uninitialised value(s)

這種錯誤通常是由于使用未初始化的值進行條件判斷引起的。例如:

==12345== Conditional jump or move depends on uninitialised value(s)
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式使用了一個未初始化的值進行條件判斷。這可能會導緻程式出現未定義的行為。

9. Memory leak summary

這種錯誤通常是由于程式在堆上配置設定了記憶體空間,但是在程式結束時沒有釋放這些記憶體空間。例如:

==12345== 24 bytes in 1 blocks are definitely lost in loss record 1 of 10
==12345==    at 0x4C2FB0F: malloc (vg_replace_malloc.c:299)
==12345==    by 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式在堆上配置設定了 24 位元組的記憶體空間,但是在程式結束時沒有釋放這些記憶體空間,導緻記憶體洩漏。

以上是一些常見的 Valgrind 錯誤類型及其示例分析,當我們運作程式時,如果出現這些錯誤,就需要使用 Valgrind 進行記憶體檢查并修複錯誤。

10. Syscall param write(buf) points to uninitialised byte(s)

這種錯誤通常是由于程式使用未初始化的記憶體空間進行寫操作引起的。例如:

==12345== Syscall param write(buf) points to uninitialised byte(s)
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)
==12345==  Address 0x5ab2a30 is 24 bytes inside a block of size 100 alloc'd
==12345==    at 0x4C2FB0F: malloc (vg_replace_malloc.c:299)
==12345==    by 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式使用了一個未初始化的記憶體空間進行寫操作。該記憶體空間是通過堆記憶體配置設定函數 malloc 建立的,位于 0x5ab2a30 位址處,大小為 100 位元組。

11. Invalid read of size N

這種錯誤通常是由于程式嘗試讀取未配置設定或已釋放的記憶體空間引起的。例如:

==12345== Invalid read of size 4
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)
==12345==  Address 0x5ab2a30 is 24 bytes inside a block of size 100 alloc'd
==12345==    at 0x4C2FB0F: malloc (vg_replace_malloc.c:299)
==12345==    by 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式嘗試讀取了一個未配置設定或已釋放的記憶體空間,該記憶體空間是通過堆記憶體配置設定函數 malloc 建立的,位于 0x5ab2a30 位址處,大小為 100 位元組。

12. Invalid write of size N

這種錯誤通常是由于程式嘗試寫入未配置設定或已釋放的記憶體空間引起的。例如:

==12345== Invalid write of size 4
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)
==12345==  Address 0x5ab2a30 is 24 bytes inside a block of size 100 alloc'd
==12345==    at 0x4C2FB0F: malloc (vg_replace_malloc.c:299)
==12345==    by 0x1234567: myFunction           

13. Conditional jump or move depends on uninitialised value(s)

這種錯誤通常是由于程式使用未初始化的記憶體空間進行條件判斷或跳轉引起的。例如:

==12345== Conditional jump or move depends on uninitialised value(s)
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)
==12345==  Uninitialised value was created by a heap allocation
==12345==    at 0x4C2FB0F: malloc (vg_replace_malloc.c:299)
==12345==    by 0x1234567: myFunction (myFile.c:5)
==12345==    by 0x1234567: main (myFile.c:20)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式使用了一個未初始化的記憶體空間進行條件判斷或跳轉。該記憶體空間是通過堆記憶體配置設定函數 malloc 建立的,其位址位于已釋放的記憶體區域中。

14. Use of uninitialised value of size N

這種錯誤通常是由于程式使用未初始化的記憶體空間進行計算或指派引起的。例如:

==12345== Use of uninitialised value of size 4
==12345==    at 0x1234567: myFunction (myFile.c:10)
==12345==    by 0x1234567: main (myFile.c:20)
==12345==  Uninitialised value was created by a heap allocation
==12345==    at 0x4C2FB0F: malloc (vg_replace_malloc.c:299)
==12345==    by 0x1234567: myFunction (myFile.c:5)
==12345==    by 0x1234567: main (myFile.c:20)           

上述錯誤資訊表示,在 myFunction 函數的第 10 行和 main 函數的第 20 行,程式使用了一個未初始化的記憶體空間進行計算或指派。該記憶體空間是通過堆記憶體配置設定函數 malloc 建立的,其位址位于已釋放的記憶體區域中。

以上是 Valgrind 常見的錯誤類型及其示例分析。當你遇到這些錯誤時,需要根據錯誤資訊所提供的上下文資訊進行分析和定位。有時候錯誤資訊并不是直接反映代碼中的問題,需要通過跟蹤代碼進行進一步的排查。

結論

Valgrind 是一款強大的記憶體檢查工具,它可以幫助程式員發現程式中的記憶體錯誤。本文介紹了 Valgrind 工具的基本用法和一些常見的選項。使用 Valgrind 可以幫助我們編寫更加健壯的程式,提高程式的可靠性和穩定性。

Linux C代碼調試除了GDB還可以用Valgrind

繼續閱讀