Valgrind 的介紹
Valgrind 能夠用來檢測程序是否有非法使用內存的問題,例如訪問未初始化的內存、訪問數組時越界、忘記釋放動態內存等問題。在 Linux 可使用下面的命令安裝 Valgrind:linux
1
2
3
4
5
6
|
$ wget ftp://sourceware.org/pub/valgrind/valgrind-3.13.0.tar.bz2
$ bzip2
-d valgrind-3.13.0.tar.bz2
$ tar -xf valgrind-3.13.0.tar
$
cd valgrind-3.13.0
$ ./configure && make
$ sudo make install
|
檢測內存泄漏
Valgrind 能夠用來檢測程序在哪一個位置發生內存泄漏,例以下面的程序:ios
1
2
3
4
5
6
7
8
|
#include <stdlib.h>
int main()
{
int *array = malloc(sizeof(int));
return 0;
}
|
編譯程序時,須要加上-g
選項:c++
1
|
$ gcc -g -o main_c main.c
|
使用 Valgrind 檢測內存使用狀況:數組
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
$ valgrind --tool=memcheck --leak-check=full ./main_c
==31416== Memcheck, a memory error detector
==31416== Copyright (C) 2002-2017, and GNU GPL
'd, by Julian Seward et al.
==31416== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==31416== Command: ./main_c
==31416==
==31416==
==31416== HEAP SUMMARY:
==31416== in use at exit: 4 bytes in 1 blocks
==31416== total heap usage: 1 allocs, 0 frees, 4 bytes allocated
==31416==
==31416== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==31416== at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
==31416== by 0x400537: main (main.c:5)
==31416==
==31416== LEAK SUMMARY:
==31416== definitely lost: 4 bytes in 1 blocks
==31416== indirectly lost: 0 bytes in 0 blocks
==31416== possibly lost: 0 bytes in 0 blocks
==31416== still reachable: 0 bytes in 0 blocks
==31416== suppressed: 0 bytes in 0 blocks
==31416==
==31416== For counts of detected and suppressed errors, rerun with: -v
==31416== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
|
先看看輸出信息中的HEAP SUMMARY
,它表示程序在堆上分配內存的狀況,其中的1 allocs
表示程序分配了 1 次內存,0 frees
表示程序釋放了 0 次內存,4 bytes allocated
表示分配了 4 個字節的內存。
另外,Valgrind 也會報告程序是在哪一個位置發生內存泄漏。例如,從下面的信息能夠看到,程序發生了一次內存泄漏,位置是main.c
文件的第 5 行:spa
1
2
3
|
==31416== 4 bytes
in 1 blocks are definitely lost in loss record 1 of 1
==31416== at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
==31416== by 0x400537: main (main.c:5)
|
Valgrind 也能夠用來檢測 C++ 程序的內存泄漏,下面是一個正常的 C++ 程序,沒有發生內存泄漏:操作系統
1
2
3
4
5
6
7
8
9
|
#include <string>
int main()
{
auto ptr = new std::string("Hello, World!");
delete ptr;
return 0;
}
|
使用 Valgrind 分析這段程序:c++11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
$ valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./main_cpp
==31438== Memcheck, a memory error detector
==31438== Copyright (C) 2002-2017, and GNU GPL
'd, by Julian Seward et al.
==31438== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==31438== Command: ./main_cpp
==31438==
==31438==
==31438== HEAP SUMMARY:
==31438== in use at exit: 72,704 bytes in 1 blocks
==31438== total heap usage: 2 allocs, 1 frees, 72,736 bytes allocated
==31438==
==31438== 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1
==31438== at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
==31438== by 0x4EC3EFF: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==31438== by 0x40104E9: call_init.part.0 (dl-init.c:72)
==31438== by 0x40105FA: call_init (dl-init.c:30)
==31438== by 0x40105FA: _dl_init (dl-init.c:120)
==31438== by 0x4000CF9: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==31438==
==31438== LEAK SUMMARY:
==31438== definitely lost: 0 bytes in 0 blocks
==31438== indirectly lost: 0 bytes in 0 blocks
==31438== possibly lost: 0 bytes in 0 blocks
==31438== still reachable: 72,704 bytes in 1 blocks
==31438== suppressed: 0 bytes in 0 blocks
==31438==
==31438== For counts of detected and suppressed errors, rerun with: -v
==31438== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
|
使用 Valgrind 分析 C++ 程序時,有一些問題須要留意。例如,這個程序並無發生內存泄漏,可是從HEAP SUMMARY
能夠看到,程序分配了 2 次內存,但卻只釋放了 1 次內存,爲何會這樣呢?
實際上這是因爲 C++ 在分配內存時,爲了提升效率,使用了它本身的內存池。當程序終止時,內存池的內存纔會被操做系統回收,因此 Valgrind 會將這部份內存報告爲 reachable 的,須要注意,reachable 的內存不表明內存泄漏,例如,從上面的輸出中能夠看到,有 72704 個字節是 reachable 的,但沒有報告內存泄漏。code
檢測越界訪問
C++ 程序常常出現的 Bug 就是數組越界訪問,例以下面的程序出現了越界訪問:ip
1
2
3
4
5
6
7
8
9
10
|
#include <vector>
#include <iostream>
int main()
{
std::vector<int> v(10, 0);
std::cout << v[10] << std::endl;
return 0;
}
|
使用 Valgrind 分析這段程序,Valgrind 會提示越界訪問:內存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
$ g++ -std=c++11 -g -o main_cpp main.cpp
$ valgrind --tool=memcheck --leak-check=full ./main_cpp
==31523== Memcheck, a memory error detector
==31523== Copyright (C) 2002-2017, and GNU GPL
'd, by Julian Seward et al.
==31523== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==31523== Command: ./main_cpp
==31523==
==31523== Invalid read of size 4
==31523== at 0x400AD7: main (main.cpp:7)
==31523== Address 0x5ab5ca8 is 0 bytes after a block of size 40 alloc'd
==31523== at 0x4C2E216: operator new(unsigned long) (vg_replace_malloc.c:334)
==31523== by 0x4010D3: __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (new_allocator.h:104)
==31523== by 0x401040: std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (alloc_traits.h:491)
==31523== by 0x400F91: std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) (stl_vector.h:170)
==31523== by 0x400E7E: std::_Vector_base<int, std::allocator<int> >::_M_create_storage(unsigned long) (stl_vector.h:185)
==31523== by 0x400D1E: std::_Vector_base<int, std::allocator<int> >::_Vector_base(unsigned long, std::allocator<int> const&) (stl_vector.h:136)
==31523== by 0x400C11: std::vector<int, std::allocator<int> >::vector(unsigned long, int const&, std::allocator<int> const&) (stl_vector.h:291)
==31523== by 0x400AB9: main (main.cpp:6)
|
Invalid read of size 4
表示越界讀取 4 個字節,這個操做出如今main.cpp
文件的第 7 行。另外能夠看到,vector
分配了一塊 40 字節的內存,程序越界訪問緊急着這塊內存以後的 4 個字節。
檢測未初始化的內存
另外一種常常出現的 Bug,就是程序訪問了未初始化的內存。例如:
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <iostream>
int main()
{
int x;
if (x == 0)
{
std::cout << "X is zero" << std::endl;
}
return 0;
}
|
使用 Valgrind 檢測這個程序:
1
2
3
4
5
6
7
8
9
|
$ g++ -std=c++11 -g -o main_cpp main.cpp
$ valgrind --tool=memcheck --leak-check=full ./main_cpp
==31554== Memcheck, a memory error detector
==31554== Copyright (C) 2002-2017, and GNU GPL
'd, by Julian Seward et al.
==31554== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==31554== Command: ./main_cpp
==31554==
==31554== Conditional jump or move depends on uninitialised value(s)
==31554== at 0x400852: main (main.cpp:6)
|
輸出中提示了main.cpp
文件的第 6 行訪問了未初始化的內存。