Document directory
- Use valgrind to find Memory leakage and Invalid Memory Access
Valgrind is a multi-purpose code analysis and memory debugging tool for x86 Linux. You can run your program in its environment to monitor memory usage, such as malloc and free in C language or new and delete in C ++. If you use uninitialized memory, set the memory outside the end of the array or forget to release the pointer, valgrind can detect it. Although valgrind can do other work, this tutorial focuses on how to use it to discover memory-related errors, because they are also common errors for programmers.
Windows users don't have to be frustrated. Although valgrind is not available on Windows, you can try IBM purify, which is similar in functionality to valgrind.
Obtain valgrind
If you are using Linux but have not installed valgrind, you can download a free copy here.
The installation process is very simple. You only need to decompress the downloaded software package with Bzip2 and expand it (XYZ in the following example is the version number ).
Bzip2-D valgrind-XYZ.tar.bz2
Tar-XF valgrind-XYZ.tar
Or use a simpler method:
Tar jxf valgrind-XYZ.tar.bz2
This will create a directory named valgrind-XYZ, enter the Directory and run
./Configure
Make
Make install
Now that you have installed valgrind, you can start to learn how to use it.
Use valgrind to find Memory leakage
Memory leakage is one of the most difficult common errors, because it will not cause any problems unless the memory is used up or the malloc call fails. In fact, when you use languages like C or C ++ that do not have a garbage collection mechanism, you spend most of your time dealing with how to properly release the memory. If the program runs for a long enough time, a small mistake will also have a major impact on the program.
Valgrind supports many tools: memcheck, addrcheck, cachegrind, massif, helgrind, and callgrind. When running valgrind, you must specify the desired tool. In this tutorial, we mainly focus on the memory check tool, which can help us check the memory usage (I will not use other tools ). If there are no other parameters, valgrind will give a briefing on the total number of free and malloc calls after the program ends: (Note that 18490 is the process number and may be another value on your machine)
% Valgrind -- tool = memcheck program_name
...
= 18515 = malloc/free: in use at Exit: 0 bytes in 0 blocks.
= 18515 = malloc/free: 1 allocs, 1 frees, 10 bytes allocated.
= 18515 = for a detailed leak analysis, Rerun with: -- leak-check = Yes
If there is a memory leak in the program, the number of memory allocations is inconsistent with the number of memories released (you cannot use one free call to release multiple allocated memories ).
If the program memory allocation and release quantity are inconsistent, you can add the leak-check parameter to re-run the program, so that you can see the code that is allocated but not released.
To demonstrate this function, I wrote a simple C program and compiled the "example1" application.
# Include
Int main ()
{
Char * x = malloc (100);/* or, in C ++, "char * x = new char [100] */
Return 0;
}
% Valgrind -- tool = memcheck -- leak-check = Yes example1
In the running result, a list of functions that call malloc but do not call free is provided.
= 2116 = 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
= 2116 = at 0x1b900dd0: malloc (vg_replace_malloc.c: 131)
= 2116 = by 0x804840f: Main (in/home/cprogram/example1)
The above results do not tell us more information. We only know that the malloc call in the main function causes memory leakage, but do not know which line of the program calls malloc. This is because the-G parameter is not added to GCC during program compilation, and the related debugging information is lost. Re-compile the code and run it again. We get more information (fragments ).
= 2330 = 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
= 2330 = at 0x1b900dd0: malloc (vg_replace_malloc.c: 131)
= 2330 = by 0x804840f: Main (example1.c: 5)
Now we know exactly which line of code causes memory leakage. Although it is still a problem to know where to release the memory, at least we already know where to start. Because for each memory that needs to be dynamically allocated, you have a plan for when to allocate and when to release it. Since you already know the allocation point that causes memory leakage, in this way, the memory usage plan is clarified, which helps to locate the correct location for memory release.
Before the -- leak-check = Yes parameter is added and the memory leakage error is no longer displayed, you may need to modify the code multiple times, the software without memory leakage was born like this :-). When valgrind is run, add the -- show-reachable = Yes parameter to find free or new matching results in the future. The output results are similar to those above, but more unreleased memory is displayed.
Use valgrind to find invalid pointers
With the memcheck tool, valgrind can also identify invalid heap memory usage. For example, if you use malloc or new to allocate an array and access the memory at the end of the array:
Char * x = malloc (10 );
X [10] = 'a ';
Valgrind can detect this error. Run the following sample program with valgrind: example2
# Include
Int main ()
{
Char * x = malloc (10 );
X [10] = 'a ';
Return 0;
}
% Valgrind -- tool = memcheck -- leak-check = Yes example2
The result is (segment)
==9814 = Invalid write of size 1
==9814 = at 0x804841e: Main (TST. C: 6)
= 9814 = address 0x1ba3607a is 0 bytes after a block of size 10 alloc 'd
= 9814 = at 0x1b900dd0: malloc (vg_replace_malloc.c: 131)
= 9814 = by 0x804840f: Main (example2.c: 5)
This information indicates that we allocated 10 bytes of memory but accessed the memory out of the range. Therefore, we performed an 'invalid write' operation. If we try to read data from that memory, we will get the warning of 'invalid read of size x' (X is the size of the data to be read, char is a byte, int may be 2 or 4 bytes depending on the system ). Generally, valgrind displays the function call stack information to help us locate errors accurately.
Uninitialized variables used for detection
Another type of valgrind can detect the use of uninitialized variables in condition judgment statements. Maybe you should develop the habit of initializing variables when declaring them, But valgrind can still help you find out where uninitialized variables are used. For example, run the sample program generated by the following code, example3
# Include
Int main ()
{
Int X;
If (x = 0)
{
Printf ("X is zero");/* replace with cout and include
Iostream for C ++ */
}
Return 0;
}
Valgrind will give the following result (segment)
= 17943 = conditional jump or move depends on uninitialised value (s)
= 17943 = at 0x804840a: Main (example3.c: 6)
Valgrind even knows that if a variable is assigned an uninitialized variable, it is still in the "uninitialized" state. For example, run the following code:
# Include
Int Foo (int x)
{
If (x <10)
{
Printf ("X is less than 10 \ n ");
}
}
Int main ()
{
Int y;
Foo (y );
}
Valgrind will give the following warning:
= 4827 = conditional jump or move depends on uninitialised value (s)
= 4827 = at 0x8048366: Foo (example4.c: 5)
= 4827 = by 0x8048394: Main (example4.c: 14)
You may think that the error is in Foo, and it has nothing to do with calling other functions on the stack. But because the main function passes an uninitialized value to Foo, we can find the code that really does not initialize the variable based on the call stack information.
Valgrind only helps you detect these errors when you are able to run the code. Be sure to override every branch of the Code in the test.
What else can valgrind find?
Valgrind can also find other incorrect memory usage errors: If you have released the same memory twice, valgrind will detect it, and you will obtain Invalid Free call stack information.
Valgrind can also detect the error of releasing memory using incorrect methods. For example, there are three basic memory release methods in C ++: free, delete, and delete []. The free function should only correspond to the malloc function. In some systems, you may not have to deal with this problem, but it is not portable. Delete [] must correspond to new [] (array allocation) again. (Some compilers may allow you to ignore these rules, but cannot ensure that all compilers allow you to do so. After all, it is not part of the standard .)
If these problems exist in the program, you will get the following error message:
Mismatched free ()/delete/Delete []
All these errors should be fixed immediately, even if your program runs normally by chance.
Valgrind cannot identify which errors?
Valgrind does not perform a boundary check on static arrays (allocated on the stack. If an array is declared in the program:
Int main ()
{
Char X [10];
X [11] = 'a ';
}
Valgrind will not warn you! For testing purposes, you can change the array to an array dynamically allocated on the heap, so that the boundary check may be performed. This method seems to be worth the candle.
More warnings
What are the negative effects of valgrind? It occupies more memory-up to two times the normal usage of your program. If you use valgrind to detect programs that use a large amount of memory, it may take a long time to run the test. In most cases, this is not a problem. Even if the speed is slow, it is only slow during detection. If you use valgrind to detect a program that is slow during normal operation, this is a big problem.
Valgrind cannot detect all the errors you make in the program-if you do not check the buffer overflow, valgrind will not tell you that the Code writes the memory that it should not write.
Summary
Valgrind is a tool on the X86 architecture and can only run on Linux (related versions of FreeBSD and NetBSD are under development ). It allows programmers to test programs in their environments to detect unpaired malloc call errors and other illegal memory usage (uninitialized memory) and Invalid Memory operations (such as releasing the same memory twice or calling an incorrect destructor ). Valgrind does not check the usage of static allocation arrays.