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.org :
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.
- Memcheck is a memory error detector. It helps you make your programs, particularly those written in C and C++, more correct.
- Cachegrind is a cache and branch-prediction profiler. It helps you make your programs run faster.
- Callgrind is a call-graph generating cache profiler. It has some overlap with Cachegrind, but also gathers some information that Cachegrind does not.
- Helgrind is a thread error detector. It helps you make your multi-threaded programs more correct.
- DRD is also a thread error detector. It is similar to Helgrind but uses different analysis techniques and so may find different problems.
- Massif is a heap profiler. It helps you make your programs use less memory.
- DHAT is a different kind of heap profiler. It helps you understand issues of block lifetimes, block utilisation, and layout inefficiencies.
- SGcheck is an experimental tool that can detect overruns of stack and global arrays. Its functionality is complementary to that of Memcheck: SGcheck finds problems that Memcheck can’t, and vice versa..
- BBV is an experimental SimPoint basic block vector generator. It is useful to people doing computer architecture research and development.
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 Memcheck
The memcheck tool is used as follows :
valgrind --tool=memcheck ./a.out
As 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 :
- Use of uninitialized memory
- Reading/writing memory after it has been freed
- Reading/writing off the end of malloc’d blocks
- Memory leaks
- Mismatched use of malloc/new/new[] vs free/delete/delete[]
- Doubly freed memory
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 memory
Code :
#include <stdio.h> #include <stdlib.h> int main(void) { char *p; char c = *p; printf("\n [%c]\n",c); return 0; }
In 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 freed
Code :
#include <stdio.h> #include <stdlib.h> int main(void) { char *p = malloc(1); *p = 'a'; char c = *p; printf("\n [%c]\n",c); free(p); c = *p; return 0; }
In 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 debug a c program use gdb.
3. Reading/writing off the end of malloc’d blocks
Code :
#include <stdio.h> #include <stdlib.h> int main(void) { char *p = malloc(1); *p = 'a'; char c = *(p+1); printf("\n [%c]\n",c); free(p); return 0; }
In 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 leaks
Code:
#include <stdio.h> #include <stdlib.h> int main(void) { char *p = malloc(1); *p = 'a'; char c = *p; printf("\n [%c]\n",c); return 0; }
In 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:
#include <stdio.h> #include <stdlib.h> #include<iostream> int main(void) { char *p = (char*)malloc(1); *p = 'a'; char c = *p; printf("\n [%c]\n",c); delete p; return 0; }
In 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 memory
Code :
#include <stdio.h> #include <stdlib.h> int main(void) { char *p = (char*)malloc(1); *p = 'a'; char c = *p; printf("\n [%c]\n",c); free(p); free(p); return 0; }
In 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.
Comments on this entry are closed.
Nice article. I agree that tools like valgrind can help locate issues though it is my hope that developers have good habits that generally prevent memory leaks. The biggest thing I find helpful is to visualize memory allocation and use through pictures as a part of the development process and thorough test cases covering each of the cases you mentioned above and more.
One problem I’ve seen in the past is when a developer issues a new or malloc against a variable that already contains a pointer to a previously allocated block but the code doesn’t attempt to garbage collect it first. This causes programs to lose access to previously allocated memory if it’s not done with great care.
int main(void) {
char *p = NULL ;
…
p = (char *) malloc( 4096 ) ;
….
if ( /* some condition that fires some times but not always */ ) {
free( p ) ;
p = NULL ; /* good */
}
….
/* bug here – code doesn’t check to see if there is already a buffer allocated to p */
p = (char *) malloc( 8192 ) ;
….
}
There are cases when one pointer is kept in multiple variables. When that’s true, it’s critical to clear any copies of pointers when the block those pointers reference is released back to the OS. There are other cases I can think of that would cause memory leaks. The good news is I’ve seen that these issues are becoming much less prevalent than they were 10-20 years ago. A lot of that is because of excellent libraries built to handle memory management for us as well as the large number of languages that do that work for us.
Regardless – tools like valgrind do help troubleshoot memory leaks but a healthy understanding of how memory allocation is supposed to work is no substitute for a tool like it. 🙂
Shouldn’t example 4 say:
*p = ‘a’;
instead of
*y = ‘a’;
Great thegeekstuff.com. I like this website much, learned much more, I was confused in debugging dump file by mdb and gdb, any weblog you have written? thank you. so much
would be nice if you put line numbers on the code snippets so that we can match the valgrind errors with the appropriate line numbers
You forgot to mention that Valgrind is open source/free ware distributed under GNU GPL version 2!
Nice article! I need to take a deeper look into Valgrind someday, it’s really useful for big projects.
Regards,
Júlio.
Is is possible to debug the cross compiled code using valgrind ?
excellent article but i use deleaker…
Excellant article….
very good Examples. Thanks you very much.
exellent article
Is it possible to debug the cross compiled code using valgrind ?
and
Is there any other possibilities for getting memory leak apart from ‘malloc’ ?
Excellent examples…Thank you very much.