All Articles

A Powerful Tool for Memory Leak Detection "valgrind"

valgrind

Valgrind is a powerful tool for detecting memory leaks and profiling. It’s available on Linux and Mac OS X. A few years ago, I heard about Valgrind from my colleague a few years ago. When I encountered a real memory leak problem, I decided to try it.

Installation

I’m using Debian as my development environment. So I installed Valgrind with the following command:

(root)# apt install valgrind

Alternatively, you can download the source code from the valgrind official website and compile it yourself.

$ git clone https://sourceware.org/git/valgrind.git
$ cd valgrind
$ ./autogen.sh
$ ./configure --prefix=<installation-directory>
$ make
(root)# make install

Usage

An example of a memory leak problem could be like this:

// memleak.cpp
#include <cstdio>

void leak(int bytes)
{
    char *p = new char[bytes];
    printf("Allocated %d bytes at %p\n", bytes, p);
    p[bytes] = 0;
    printf("Wrote to %p\n", p + bytes);
}

int main()
{
    leak(10);
    leak(20);
    leak(30);
    return 0;
}

This program allocates memory with new but forgets to release it with delete. We can compile it with the following command:

$ g++ -O0 -g -o memleak memleak.cpp

Then we can run the program with Valgrind:

$ valgrind --leak-check=yes ./memleak

The output of Valgrind could be like this:

==4788== Memcheck, a memory error detector
==4788== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==4788== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==4788== Command: ./memleak
==4788==
Allocated 10 bytes at 0x4d72c80
==4788== Invalid write of size 1
==4788==    at 0x10884C: leak(int) (memleak.cpp:7)
==4788==    by 0x108887: main (memleak.cpp:13)
==4788==  Address 0x4d72c8a is 0 bytes after a block of size 10 alloc'd
==4788==    at 0x4886B58: operator new[](unsigned long) (vg_replace_malloc.c:640)
==4788==    by 0x108827: leak(int) (memleak.cpp:5)
==4788==    by 0x108887: main (memleak.cpp:13)
==4788==
Wrote to 0x4d72c8a
Allocated 20 bytes at 0x4d73110
==4788== Invalid write of size 1
==4788==    at 0x10884C: leak(int) (memleak.cpp:7)
==4788==    by 0x10888F: main (memleak.cpp:14)
==4788==  Address 0x4d73124 is 0 bytes after a block of size 20 alloc'd
==4788==    at 0x4886B58: operator new[](unsigned long) (vg_replace_malloc.c:640)
==4788==    by 0x108827: leak(int) (memleak.cpp:5)
==4788==    by 0x10888F: main (memleak.cpp:14)
==4788==
Wrote to 0x4d73124
Allocated 30 bytes at 0x4d73170
==4788== Invalid write of size 1
==4788==    at 0x10884C: leak(int) (memleak.cpp:7)
==4788==    by 0x108897: main (memleak.cpp:15)
==4788==  Address 0x4d7318e is 0 bytes after a block of size 30 alloc'd
==4788==    at 0x4886B58: operator new[](unsigned long) (vg_replace_malloc.c:640)
==4788==    by 0x108827: leak(int) (memleak.cpp:5)
==4788==    by 0x108897: main (memleak.cpp:15)
==4788==
Wrote to 0x4d7318e
==4788==
==4788== HEAP SUMMARY:
==4788==     in use at exit: 60 bytes in 3 blocks
==4788==   total heap usage: 5 allocs, 2 frees, 73,788 bytes allocated
==4788==
==4788== 10 bytes in 1 blocks are definitely lost in loss record 1 of 3
==4788==    at 0x4886B58: operator new[](unsigned long) (vg_replace_malloc.c:640)
==4788==    by 0x108827: leak(int) (memleak.cpp:5)
==4788==    by 0x108887: main (memleak.cpp:13)
==4788==
==4788== 20 bytes in 1 blocks are definitely lost in loss record 2 of 3
==4788==    at 0x4886B58: operator new[](unsigned long) (vg_replace_malloc.c:640)
==4788==    by 0x108827: leak(int) (memleak.cpp:5)
==4788==    by 0x10888F: main (memleak.cpp:14)
==4788==
==4788== 30 bytes in 1 blocks are definitely lost in loss record 3 of 3
==4788==    at 0x4886B58: operator new[](unsigned long) (vg_replace_malloc.c:640)
==4788==    by 0x108827: leak(int) (memleak.cpp:5)
==4788==    by 0x108897: main (memleak.cpp:15)
==4788==
==4788== LEAK SUMMARY:
==4788==    definitely lost: 60 bytes in 3 blocks
==4788==    indirectly lost: 0 bytes in 0 blocks
==4788==      possibly lost: 0 bytes in 0 blocks
==4788==    still reachable: 0 bytes in 0 blocks
==4788==         suppressed: 0 bytes in 0 blocks
==4788==
==4788== For lists of detected and suppressed errors, rerun with: -s
==4788== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)

As we can see, Valgrind detected the memory leak problem inside the leak() function and reported it. It also showed the memory address where the memory was allocated and the memory address where the invalid write happened.

Published May 16, 2022

Flying code monkey