全部博文(106)
分类: C/C++
2012-09-07 05:41:05
One major aspect of system programming is to handle memory related issues effectively. The more you work close to the system, the more memory related issues you need to face.
Sometimes these issues are very trivial while many times it becomes a nightmare to debug memory related issues. So, as a practice many tools are used for debugging memory related issues.
In this article, we will discuss the most popular open source memory management framework VALGRIND.
From :
Valgrind is an instrumentation framework for building dynamic analysis tools. It comes with a set of tools each of which performs some kind of debugging, profiling, or similar task that helps you improve your programs. Valgrind’s architecture is modular, so new tools can be created easily and without disturbing the existing structure.
A number of useful tools are supplied as standard.
There are also a couple of minor tools that aren’t useful to most users: Lackey is an example tool that illustrates some instrumentation basics; and Nulgrind is the minimal Valgrind tool that does no analysis or instrumentation, and is only useful for testing purposes.
Here in this article we will focus on the tool ‘memcheck’.
Using Valgrind MemcheckThe memcheck tool is used as follows :
valgrind --tool=memcheck ./a.outAs clear from the command above, the main binary is ‘Valgrind’ and the tool which we want to use is specified by the option ‘–tool’. The ‘a.out’ above signifies the executable over which we want to run memcheck.
This tool can detect the following memory related problems :
Note : The above list is not exhaustive but contains the popular problems detected by this tool.
Lets discuss the above scenarios one by one:
Note : All the test code described below should be compiled using gcc with -g option(to generate line numbers in memcheck output) enabled. As we discussed earlier for a C program to get compiled into an executable, it has to go through 4 different stages.
1. Use of uninitialized memoryCode :
#includeIn the above code, we try to use an uninitialized pointer ‘p’.
Lets run memcheck and see the result.
$ valgrind --tool=memcheck ./val ==2862== Memcheck, a memory error detector ==2862== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2862== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2862== Command: ./val ==2862== ==2862== Use of uninitialised value of size 8 ==2862== at 0x400530: main (valgrind.c:8) ==2862== [#] ==2862== ==2862== HEAP SUMMARY: ==2862== in use at exit: 0 bytes in 0 blocks ==2862== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==2862== ==2862== All heap blocks were freed -- no leaks are possible ==2862== ==2862== For counts of detected and suppressed errors, rerun with: -v ==2862== Use --track-origins=yes to see where uninitialized values come from ==2862== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)As seen from the output above, Valgrind detects the uninitialized variable and gives a warning(see the lines in bold above).
2. Reading/writing memory after it has been freedCode :
#includeIn the above piece of code, we have freed a pointer ‘p’ and then again we have tried to access the value help by the pointer.
Lets run memcheck and see what Valgrind has to offer for this scenario.
$ valgrind --tool=memcheck ./val ==2849== Memcheck, a memory error detector ==2849== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2849== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2849== Command: ./val ==2849== [a] ==2849== Invalid read of size 1 ==2849== at 0x400603: main (valgrind.c:30) ==2849== Address 0x51b0040 is 0 bytes inside a block of size 1 free'd ==2849== at 0x4C270BD: free (vg_replace_malloc.c:366) ==2849== by 0x4005FE: main (valgrind.c:29) ==2849== ==2849== ==2849== HEAP SUMMARY: ==2849== in use at exit: 0 bytes in 0 blocks ==2849== total heap usage: 1 allocs, 1 frees, 1 bytes allocated ==2849== ==2849== All heap blocks were freed -- no leaks are possible ==2849== ==2849== For counts of detected and suppressed errors, rerun with: -v ==2849== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)As seen in the output above, the tool detects the invalid read and prints the warning ‘Invalid read of size 1′.
On a side note, to .
3. Reading/writing off the end of malloc’d blocksCode :
#includeIn the above piece of code, we have allocated 1 byte for ‘p’ but we access the the address p+1 while reading the value into ‘c’.
Now we run Valgrind on this piece of code :
$ valgrind --tool=memcheck ./val ==2835== Memcheck, a memory error detector ==2835== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2835== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2835== Command: ./val ==2835== ==2835== Invalid read of size 1 ==2835== at 0x4005D9: main (valgrind.c:25) ==2835== Address 0x51b0041 is 0 bytes after a block of size 1 alloc'd ==2835== at 0x4C274A8: malloc (vg_replace_malloc.c:236) ==2835== by 0x4005C5: main (valgrind.c:22) ==2835== [] ==2835== ==2835== HEAP SUMMARY: ==2835== in use at exit: 0 bytes in 0 blocks ==2835== total heap usage: 1 allocs, 1 frees, 1 bytes allocated ==2835== ==2835== All heap blocks were freed -- no leaks are possible ==2835== ==2835== For counts of detected and suppressed errors, rerun with: -v ==2835== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)Again, this tool detects the invalid read done in this case.
4. Memory leaksCode:
#includeIn this code, we have malloced one byte but haven’t freed it. Now lets run Valgrind and see what happens :
$ valgrind --tool=memcheck --leak-check=full ./val ==2888== Memcheck, a memory error detector ==2888== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2888== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2888== Command: ./val ==2888== [a] ==2888== ==2888== HEAP SUMMARY: ==2888== in use at exit: 1 bytes in 1 blocks ==2888== total heap usage: 1 allocs, 0 frees, 1 bytes allocated ==2888== ==2888== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==2888== at 0x4C274A8: malloc (vg_replace_malloc.c:236) ==2888== by 0x400575: main (valgrind.c:6) ==2888== ==2888== LEAK SUMMARY: ==2888== definitely lost: 1 bytes in 1 blocks ==2888== indirectly lost: 0 bytes in 0 blocks ==2888== possibly lost: 0 bytes in 0 blocks ==2888== still reachable: 0 bytes in 0 blocks ==2888== suppressed: 0 bytes in 0 blocks ==2888== ==2888== For counts of detected and suppressed errors, rerun with: -v ==2888== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)The lines (in bold above) shows that this tool was able to detect the leaked memory.
Note: In this case we added an extra option ‘–leak-check=full’ to get verbose details of the memory leak.
5. Mismatched use of malloc/new/new[] vs free/delete/delete[]Code:
#includeIn the above code, we have used malloc() to allocate memory but used delete operator to delete the memory.
Note : Use g++ to compile the above code as delete operator was introduced in C++ and to compile c++ code, g++ tool is used.
Lets run this tool and see :
$ valgrind --tool=memcheck --leak-check=full ./val ==2972== Memcheck, a memory error detector ==2972== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2972== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2972== Command: ./val ==2972== [a] ==2972== Mismatched free() / delete / delete [] ==2972== at 0x4C26DCF: operator delete(void*) (vg_replace_malloc.c:387) ==2972== by 0x40080B: main (valgrind.c:13) ==2972== Address 0x595e040 is 0 bytes inside a block of size 1 alloc'd ==2972== at 0x4C274A8: malloc (vg_replace_malloc.c:236) ==2972== by 0x4007D5: main (valgrind.c:7) ==2972== ==2972== ==2972== HEAP SUMMARY: ==2972== in use at exit: 0 bytes in 0 blocks ==2972== total heap usage: 1 allocs, 1 frees, 1 bytes allocated ==2972== ==2972== All heap blocks were freed -- no leaks are possible ==2972== ==2972== For counts of detected and suppressed errors, rerun with: -v ==2972== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)We see from the output above (see lines in bold), the tool clearly states ‘Mismatched free() / delete / delete []‘
You can try and use the combination ‘new’ and ‘free’ in a test code and see what result this tool gives.
6. Doubly freed memoryCode :
#includeIn the above peice of code, we have freed the memory pointed by ‘p’ twice. Now, lets run the tool memcheck :
$ valgrind --tool=memcheck --leak-check=full ./val ==3167== Memcheck, a memory error detector ==3167== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==3167== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==3167== Command: ./val ==3167== [a] ==3167== Invalid free() / delete / delete[] ==3167== at 0x4C270BD: free (vg_replace_malloc.c:366) ==3167== by 0x40060A: main (valgrind.c:12) ==3167== Address 0x51b0040 is 0 bytes inside a block of size 1 free'd ==3167== at 0x4C270BD: free (vg_replace_malloc.c:366) ==3167== by 0x4005FE: main (valgrind.c:11) ==3167== ==3167== ==3167== HEAP SUMMARY: ==3167== in use at exit: 0 bytes in 0 blocks ==3167== total heap usage: 1 allocs, 2 frees, 1 bytes allocated ==3167== ==3167== All heap blocks were freed -- no leaks are possible ==3167== ==3167== For counts of detected and suppressed errors, rerun with: -v ==3167== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)As seen from the output above(lines in bold), the tool detects that we have called free twice on the same pointer.
In this article, we concentrated on memory management framework Valgrind and used the tool memcheck (provided by this framework) to describe how it makes life easy for a developer working close to memory. This tool can detect many memory related problems that are very hard to find manually.