1. 程式人生 > 實用技巧 >C++軟體開發常用輔助軟體——Valgrind

C++軟體開發常用輔助軟體——Valgrind

Valgrind

Valgrind是一款用於記憶體除錯、記憶體洩漏檢測以及效能分析的軟體開發工具。該工具內部又包含多個子工具集(如memcheck, cachegrind, callgrind, helgrind等等),每個子工具集相互獨立,每一次分析只能選擇一個子工具,預設子工具為memcheck。

用法

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

示例

演示程式碼:

1 #include <stdio.h>
2 
3 int main()
4 {
5     int *a = new int
[3]; 6 printf("%d\n", a[0]); 7 printf("%d\n", a[4]); 8 return 0; 9 }

顯然上述程式碼存在3處問題。

1. 第5行申請的記憶體未釋放

2. 第6行訪問了未初始化的變數

3. 第7行訪問了非法地址

執行檢測工具

valgrind ./a.out

輸出如下

 1 ==6945== Memcheck, a memory error detector
 2 ==6945== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
 3 ==6945
== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info 4 ==6945== Command: ./a.out 5 ==6945== 6 ==6945== Conditional jump or move depends on uninitialised value(s) 7 ==6945== at 0x57BF132: vfprintf (in /lib64/libc-2.26.so) 8 ==6945== by 0x57C69B5: printf (in /lib64/libc-2.26.so) 9
==6945== by 0x4005D3: main (in /home/nosoul/DEV/test/a.out) 10 ==6945== 11 ==6945== Use of uninitialised value of size 8 12 ==6945== at 0x57BAF3B: _itoa_word (in /lib64/libc-2.26.so) 13 ==6945== by 0x57BE820: vfprintf (in /lib64/libc-2.26.so) 14 ==6945== by 0x57C69B5: printf (in /lib64/libc-2.26.so) 15 ==6945== by 0x4005D3: main (in /home/nosoul/DEV/test/a.out) 16 ==6945== 17 ==6945== Conditional jump or move depends on uninitialised value(s) 18 ==6945== at 0x57BAF45: _itoa_word (in /lib64/libc-2.26.so) 19 ==6945== by 0x57BE820: vfprintf (in /lib64/libc-2.26.so) 20 ==6945== by 0x57C69B5: printf (in /lib64/libc-2.26.so) 21 ==6945== by 0x4005D3: main (in /home/nosoul/DEV/test/a.out) 22 ==6945== 23 ==6945== Conditional jump or move depends on uninitialised value(s) 24 ==6945== at 0x57BE8D8: vfprintf (in /lib64/libc-2.26.so) 25 ==6945== by 0x57C69B5: printf (in /lib64/libc-2.26.so) 26 ==6945== by 0x4005D3: main (in /home/nosoul/DEV/test/a.out) 27 ==6945== 28 ==6945== Conditional jump or move depends on uninitialised value(s) 29 ==6945== at 0x57BF32C: vfprintf (in /lib64/libc-2.26.so) 30 ==6945== by 0x57C69B5: printf (in /lib64/libc-2.26.so) 31 ==6945== by 0x4005D3: main (in /home/nosoul/DEV/test/a.out) 32 ==6945== 33 0 34 ==6945== Invalid read of size 4 35 ==6945== at 0x4005DC: main (in /home/nosoul/DEV/test/a.out) 36 ==6945== Address 0x5b38c90 is 4 bytes after a block of size 12 alloc'd 37 ==6945== at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) 38 ==6945== by 0x4005B8: main (in /home/nosoul/DEV/test/a.out) 39 ==6945== 40 0 41 ==6945== 42 ==6945== HEAP SUMMARY: 43 ==6945== in use at exit: 12 bytes in 1 blocks 44 ==6945== total heap usage: 3 allocs, 2 frees, 73,740 bytes allocated 45 ==6945== 46 ==6945== LEAK SUMMARY: 47 ==6945== definitely lost: 12 bytes in 1 blocks 48 ==6945== indirectly lost: 0 bytes in 0 blocks 49 ==6945== possibly lost: 0 bytes in 0 blocks 50 ==6945== still reachable: 0 bytes in 0 blocks 51 ==6945== suppressed: 0 bytes in 0 blocks 52 ==6945== Rerun with --leak-check=full to see details of leaked memory 53 ==6945== 54 ==6945== Use --track-origins=yes to see where uninitialised values come from 55 ==6945== For lists of detected and suppressed errors, rerun with: -s 56 ==6945== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)

檢測結果先看“HEAP SUMMARY”開始往後的內容。參考第52行以及第54的提示,因此我們執行

valgrind --track-origins=yes --leak-check=full ./a.out

輸出如下

 1 ==7080== Memcheck, a memory error detector
 2 ==7080== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
 3 ==7080== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
 4 ==7080== Command: ./a.out
 5 ==7080== 
 6 ==7080== Conditional jump or move depends on uninitialised value(s)
 7 ==7080==    at 0x57BF132: vfprintf (in /lib64/libc-2.26.so)
 8 ==7080==    by 0x57C69B5: printf (in /lib64/libc-2.26.so)
 9 ==7080==    by 0x4005D3: main (test.cpp:6)
10 ==7080==  Uninitialised value was created by a heap allocation
11 ==7080==    at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
12 ==7080==    by 0x4005B8: main (test.cpp:5)
13 ==7080== 
14 ==7080== Use of uninitialised value of size 8
15 ==7080==    at 0x57BAF3B: _itoa_word (in /lib64/libc-2.26.so)
16 ==7080==    by 0x57BE820: vfprintf (in /lib64/libc-2.26.so)
17 ==7080==    by 0x57C69B5: printf (in /lib64/libc-2.26.so)
18 ==7080==    by 0x4005D3: main (test.cpp:6)
19 ==7080==  Uninitialised value was created by a heap allocation
20 ==7080==    at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
21 ==7080==    by 0x4005B8: main (test.cpp:5)
22 ==7080== 
23 ==7080== Conditional jump or move depends on uninitialised value(s)
24 ==7080==    at 0x57BAF45: _itoa_word (in /lib64/libc-2.26.so)
25 ==7080==    by 0x57BE820: vfprintf (in /lib64/libc-2.26.so)
26 ==7080==    by 0x57C69B5: printf (in /lib64/libc-2.26.so)
27 ==7080==    by 0x4005D3: main (test.cpp:6)
28 ==7080==  Uninitialised value was created by a heap allocation
29 ==7080==    at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
30 ==7080==    by 0x4005B8: main (test.cpp:5)
31 ==7080== 
32 ==7080== Conditional jump or move depends on uninitialised value(s)
33 ==7080==    at 0x57BE8D8: vfprintf (in /lib64/libc-2.26.so)
34 ==7080==    by 0x57C69B5: printf (in /lib64/libc-2.26.so)
35 ==7080==    by 0x4005D3: main (test.cpp:6)
36 ==7080==  Uninitialised value was created by a heap allocation
37 ==7080==    at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
38 ==7080==    by 0x4005B8: main (test.cpp:5)
39 ==7080== 
40 ==7080== Conditional jump or move depends on uninitialised value(s)
41 ==7080==    at 0x57BF32C: vfprintf (in /lib64/libc-2.26.so)
42 ==7080==    by 0x57C69B5: printf (in /lib64/libc-2.26.so)
43 ==7080==    by 0x4005D3: main (test.cpp:6)
44 ==7080==  Uninitialised value was created by a heap allocation
45 ==7080==    at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
46 ==7080==    by 0x4005B8: main (test.cpp:5)
47 ==7080== 
48 0
49 ==7080== Invalid read of size 4
50 ==7080==    at 0x4005DC: main (test.cpp:7)
51 ==7080==  Address 0x5b38c90 is 4 bytes after a block of size 12 alloc'd
52 ==7080==    at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
53 ==7080==    by 0x4005B8: main (test.cpp:5)
54 ==7080== 
55 0
56 ==7080== 
57 ==7080== HEAP SUMMARY:
58 ==7080==     in use at exit: 12 bytes in 1 blocks
59 ==7080==   total heap usage: 3 allocs, 2 frees, 73,740 bytes allocated
60 ==7080== 
61 ==7080== 12 bytes in 1 blocks are definitely lost in loss record 1 of 1
62 ==7080==    at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
63 ==7080==    by 0x4005B8: main (test.cpp:5)
64 ==7080== 
65 ==7080== LEAK SUMMARY:
66 ==7080==    definitely lost: 12 bytes in 1 blocks
67 ==7080==    indirectly lost: 0 bytes in 0 blocks
68 ==7080==      possibly lost: 0 bytes in 0 blocks
69 ==7080==    still reachable: 0 bytes in 0 blocks
70 ==7080==         suppressed: 0 bytes in 0 blocks
71 ==7080== 
72 ==7080== For lists of detected and suppressed errors, rerun with: -s
73 ==7080== ERROR SUMMARY: 7 errors from 7 contexts (suppressed: 0 from 0)

第6行訪問了未初始化的變數的相關檢測如下(1個問題有時會多次檢測出來)

==7080== Conditional jump or move depends on uninitialised value(s)
==7080==    at 0x57BF32C: vfprintf (in /lib64/libc-2.26.so)
==7080==    by 0x57C69B5: printf (in /lib64/libc-2.26.so)
==7080==    by 0x4005D3: main (test.cpp:6)
==7080==  Uninitialised value was created by a heap allocation
==7080==    at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==7080==    by 0x4005B8: main (test.cpp:5)

第7行訪問了非法地址的檢測如下

==7080== Invalid read of size 4
==7080==    at 0x4005DC: main (test.cpp:7)
==7080==  Address 0x5b38c90 is 4 bytes after a block of size 12 alloc'd
==7080==    at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==7080==    by 0x4005B8: main (test.cpp:5)

第5行申請的記憶體未釋放的檢測如下

==7080== 12 bytes in 1 blocks are definitely lost in loss record 1 of 1
==7080==    at 0x4C2F06F: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==7080==    by 0x4005B8: main (test.cpp:5)

將bug都修復了,再次執行檢測工具,輸出如下

==7313== Memcheck, a memory error detector
==7313== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7313== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==7313== Command: ./a.out
==7313== 
0
==7313== 
==7313== HEAP SUMMARY:
==7313==     in use at exit: 0 bytes in 0 blocks
==7313==   total heap usage: 3 allocs, 3 frees, 73,740 bytes allocated
==7313== 
==7313== All heap blocks were freed -- no leaks are possible
==7313== 
==7313== For lists of detected and suppressed errors, rerun with: -s
==7313== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

參考網址

https://www.valgrind.org/docs/manual/manual.html
https://linux.die.net/man/1/valgrind