Here is a quick how to use Valgrind tool to check if you have invalid usage of memory in C.
Configuration
Install valgrind:
1 |
sudo apt-get install valgrind |
compile your program with debug informations using gcc -g flag
1 |
gcc main.c -g |
Launch your program with valgrind:
1 |
valgrind a.out |
Normal usage
If we write a normal C program that allocate memory and free it normally the valgrind output will be like this:
main.c:
1 2 3 4 5 6 7 |
#include <stdlib.h> int main() { char *test = malloc(sizeof(char) * 10); free(test); } |
output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
jdourlens@vleteren:~/Documents$ valgrind ./a.out ==5041== Memcheck, a memory error detector ==5041== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==5041== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info ==5041== Command: ./a.out ==5041== ==5041== ==5041== HEAP SUMMARY: ==5041== in use at exit: 0 bytes in 0 blocks ==5041== total heap usage: 1 allocs, 1 frees, 10 bytes allocated ==5041== ==5041== All heap blocks were freed -- no leaks are possible ==5041== ==5041== For counts of detected and suppressed errors, rerun with: -v |
Memory leaks:
Memory leaks are bad in two way:
- They can slow down your program.
- They can crash your program in case it does not have enough memory.
How valgrind notify us of memory leaks? Let’s find writting a leaking program:
1 2 3 4 5 6 7 |
#include <stdlib.h> int main() { char *test = malloc(sizeof(char) * 10); // free(test); } |
The valgrind output will be:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
==5074== Memcheck, a memory error detector ==5074== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==5074== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info ==5074== Command: ./a.out ==5074== ==5074== ==5074== HEAP SUMMARY: ==5074== in use at exit: 10 bytes in 1 blocks ==5074== total heap usage: 1 allocs, 0 frees, 10 bytes allocated ==5074== ==5074== LEAK SUMMARY: ==5074== definitely lost: 10 bytes in 1 blocks ==5074== indirectly lost: 0 bytes in 0 blocks ==5074== possibly lost: 0 bytes in 0 blocks ==5074== still reachable: 0 bytes in 0 blocks ==5074== suppressed: 0 bytes in 0 blocks ==5074== Rerun with --leak-check=full to see details of leaked memory ==5074== ==5074== For counts of detected and suppressed errors, rerun with: -v ==5074== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) |
In the LEAK SUMMARY section, we can read that we didn’t free 10 bytes that correspond to our test variable that was allocated but not free’d.
Invalid access to memory
Sometimes in C, it’s possible to access memory that was not defined by you, valgrind list those access in the invalid read section. For example the following program tries to access a part of memory which is out of bound of our allocated char *:
1 2 3 4 5 6 7 8 |
#include <stdlib.h> int main() { char *test = malloc(sizeof(char) * 10); char a = test[11]; free(test); } |
In this case the program runs good on most environment but could face undefined state while using the read value. valgrind list those access as invalid read:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
jdourlens@vleteren:~/Documents$ valgrind ./a.out ==5102== Memcheck, a memory error detector ==5102== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==5102== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info ==5102== Command: ./a.out ==5102== ==5102== Invalid read of size 1 ==5102== at 0x400597: main (main.c:7) ==5102== Address 0x51fd04b is 1 bytes after a block of size 10 alloc'd ==5102== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==5102== by 0x40058E: main (main.c:6) ==5102== ==5102== ==5102== HEAP SUMMARY: ==5102== in use at exit: 0 bytes in 0 blocks ==5102== total heap usage: 1 allocs, 1 frees, 10 bytes allocated ==5102== ==5102== All heap blocks were freed -- no leaks are possible ==5102== ==5102== For counts of detected and suppressed errors, rerun with: -v ==5102== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) |
We can see that in main.c line 7 we attempted to read a part of memory which was not allocated by our program. The size of the read is useful in debugging as you can also guess the type of the data from the size.
Invalid writing of memory
The same behavior is available for writing memory out of allocated space:
1 2 3 4 5 6 7 8 |
#include <stdlib.h> int main() { char *test = malloc(sizeof(char) * 10); test[11] = '0'; free(test); } |
Which output invalid write:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
==5124== Memcheck, a memory error detector ==5124== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==5124== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info ==5124== Command: ./a.out ==5124== ==5124== Invalid write of size 1 ==5124== at 0x40059B: main (main.c:7) ==5124== Address 0x51fd04b is 1 bytes after a block of size 10 alloc'd ==5124== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==5124== by 0x40058E: main (main.c:6) ==5124== ==5124== ==5124== HEAP SUMMARY: ==5124== in use at exit: 0 bytes in 0 blocks ==5124== total heap usage: 1 allocs, 1 frees, 10 bytes allocated ==5124== ==5124== All heap blocks were freed -- no leaks are possible ==5124== ==5124== For counts of detected and suppressed errors, rerun with: -v ==5124== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) |
Conclusion
- Valgrind is really useful while building complex projects where memory leaks could happen.
- I found that sometimes, while using external libraries that the output could be flooded by those leaking libs.
- Even if the program runs good with inavalid read/write, this should NEVER happen in your code!
- valgrind can be invoked with lot of different options that can fine tune its output or activity!
Have fun!
Leave a Reply