Finding memory bugs like memory-leaks, memory corruptions and memory access violations can be difficult if you don't have the right tool to help you narrow down the scope and provide clues. This is what Valgrind does good for code written in C/C++, it can save you hours of frustration.

How to run

You can install valgrind using your package manager (for Ubuntu sudo apt install valgrind). Your program can be complied with any compiler, but try to keep it on no optimizations to get most of valgrind)

For your program named hello, run

valgrind ./hello

Understanding Valgrind

Valgrind is a debugging tool which is available on Linux, it's an opensource project and is free to use. Valgrind helps with memory leak detection, threading bugs and can help optimize code using its profiling support. Valgrind is designed to work non-intrusively as possible with existing executable, this means you don't need to re-link or re-build a binary in order to use Valgrind features. Simply run Valgrind passing it operational parameters and the executable to be tested along with any parameters the executable might accept.

The executable is run on a synthetic CPU provided by the Valgrind core, every single instruction executed is simulated, which will cause the executable to run much slower. Lets look at Valgrind by testing the Linux date command from the shell.

==20943== Memcheck, a memory error detector
==20943== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==20943== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==20943== Command: ./hello
==20943== 
==================================================================================================
Hello World!
==================================================================================================
==20943== 
==20943== HEAP SUMMARY:
==20943==     in use at exit: 72,768 bytes in 3 blocks
==20943==   total heap usage: 6,247 allocs, 6,244 frees, 1,071,757 bytes allocated
==20943== 
==20943== 32 bytes in 1 blocks are still reachable in loss record 1 of 3
==20943==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20943==    by 0x6340E77: CRYPTO_malloc (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==20943==    by 0x63F841E: sk_new (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==20943==    by 0x60B94F9: ??? (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
==20943==    by 0x60BB5F8: SSL_COMP_get_compression_methods (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
==20943==    by 0x60C0D82: SSL_library_init (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
==20943==    by 0x52BA77: boost::asio::ssl::detail::openssl_init_base::do_init::do_init() (openssl_init.ipp:39)
==20943==    by 0x52BE42: boost::asio::ssl::detail::openssl_init_base::instance() (openssl_init.ipp:131)
==20943==    by 0x52C955: boost::asio::ssl::detail::openssl_init<true>::openssl_init() (openssl_init.hpp:60)
==20943==    by 0x562DB2B: __static_initialization_and_destruction_0(int, int) (openssl_init.hpp:90)
==20943==    by 0x562E538: _GLOBAL__sub_I_http_client.cpp (http_client.cpp:426)
==20943==    by 0x40106C9: call_init.part.0 (dl-init.c:72)
==20943== 
==20943== 32 bytes in 1 blocks are still reachable in loss record 2 of 3
==20943==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20943==    by 0x6340E77: CRYPTO_malloc (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==20943==    by 0x63F843C: sk_new (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==20943==    by 0x60B94F9: ??? (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
==20943==    by 0x60BB5F8: SSL_COMP_get_compression_methods (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
==20943==    by 0x60C0D82: SSL_library_init (in /lib/x86_64-linux-gnu/libssl.so.1.0.0)
==20943==    by 0x52BA77: boost::asio::ssl::detail::openssl_init_base::do_init::do_init() (openssl_init.ipp:39)
==20943==    by 0x52BE42: boost::asio::ssl::detail::openssl_init_base::instance() (openssl_init.ipp:131)
==20943==    by 0x52C955: boost::asio::ssl::detail::openssl_init<true>::openssl_init() (openssl_init.hpp:60)
==20943==    by 0x562DB2B: __static_initialization_and_destruction_0(int, int) (openssl_init.hpp:90)
==20943==    by 0x562E538: _GLOBAL__sub_I_http_client.cpp (http_client.cpp:426)
==20943==    by 0x40106C9: call_init.part.0 (dl-init.c:72)
==20943== 
==20943== 72,704 bytes in 1 blocks are still reachable in loss record 3 of 3
==20943==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20943==    by 0x67ACEFF: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==20943==    by 0x40106C9: call_init.part.0 (dl-init.c:72)
==20943==    by 0x40107DA: call_init (dl-init.c:30)
==20943==    by 0x40107DA: _dl_init (dl-init.c:120)
==20943==    by 0x4000C69: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==20943== 
==20943== LEAK SUMMARY:
==20943==    definitely lost: 0 bytes in 0 blocks
==20943==    indirectly lost: 0 bytes in 0 blocks
==20943==      possibly lost: 0 bytes in 0 blocks
==20943==    still reachable: 72,768 bytes in 3 blocks
==20943==         suppressed: 0 bytes in 0 blocks
==20943== 
==20943== For counts of detected and suppressed errors, rerun with: -v
==20943== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

As you can see, there are no errors reported by Valgrind (shown in the last line). There were 6247 allocations and 6244 frees on the heap, with 3 memory leaks. Also reported is the total bytes allocated over the run-period. This lets us peek inside an process to see how memory is getting utilized.

The general Report format will be:

==99999== some valgrind message:

Where 99999 is the process id, above 20943 is the process id that was monitored.

If you can, when debugging, make sure to debug code that is not optimized. Otherwise you will encounter odds results. Also build with -Wall to eliminate all possible compile time warnings as Valgrind does not report these.

When fixing errors reported by Valgrind, fix them in the order reported, as subsequent errors may be caused by earlier errors. If you want to know how many times an error has occurred, use the "-v" option. This option will also turn on verbose reporting. The link below explains extra warnings you might see as a result of using this option.

Logging to file

Since Valgrind takes everything as an input parameter, the following Valgrind invocations will not work as you expect(No Piping possible for output). The file operation and any other program passed to Valgrind will get instrumented which is not what you want.

valgrind some_exe > valtest.log
valgrind some_exe | tee valtest.log

The correct way to log is to use the option, "--log-file=filename"

Threading

Valgrind fully supports threaded programs. The executable code will use the native thread library, however Valgrind will serialize the calls and restrict all thread operations to a single CPU core. Valgrind will only run one thread at a time, this is done to avoid implementation problems with Valgrind. Thread scheduling is still handled by the OS. Multi-threaded executable will run much slower, also on average Valgrind instrumentation results in a 10 to 50 times slow down of execution for a single threaded program.

Detecting memory leaks with Valgrind

Memcheck is the memory error detector tool in Valgrind, to use it, you "may" specify the option "--tool=memcheck", but you don't need to since Memcheck is the default tool of Valgrind. To get detailed memory check, you can run as:

valgrind --leak-check=full --show-leak-kinds=all ./hello

Lets try to debug and study a test program. The sample program provided below allocates memory and fails to delete it. The simple leak-test app takes a single parameter which is the size in bytes of the memory to be allocated.

#include <iostream>
#include <cstring>
#include <stdlib.h>

using namespace std;

int main(int argc, char* argv[])
{
   int size = 10;
   if( argc == 2 ) size = atoi(argv[1]);
   int* p = new int[size];
   memset( p, 0, 10 );
   return 0;
}
main.cpp

Create a test folder and save this file as main.cpp

Building with GNU GCC

The program can be built with any compiler, here we are using GNU g++, no special steps are required.

mkdir build
cd build
g++ ../main.cpp -o testapp
valgrind --leak-check=yes ./testapp 1000

Note: Valgrind needs the system debug symbols installed in order for it to work correctly. If you see the error below, you will need to install the debug symbols for libc on Linux. Just read the Valgrind message carefully for instructions on must be done.

==8838== Memcheck, a memory error detector
==8838== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==8838== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==8838== Command: ./testapp
==8838==

valgrind:  Fatal error at startup: a function redirection
valgrind:  which is mandatory for this platform-tool combination
valgrind:  cannot be set up.  Details of the redirection are:
valgrind: 
valgrind:  A must-be-redirected function
valgrind:  whose name matches the pattern:      strlen
valgrind:  in an object with soname matching:   ld-linux-x86-64.so.2
valgrind:  was not found whilst processing
valgrind:  symbols from the object with soname: ld-linux-x86-64.so.2
valgrind: 
valgrind:  Possible fixes: (1, short term): install glibc's debuginfo
valgrind:  package on this machine.  (2, longer term): ask the packagers
valgrind:  for your Linux distribution to please in future ship a non-
valgrind:  stripped ld.so (or whatever the dynamic linker .so is called)
valgrind:  that exports the above-named function using the standard
valgrind:  calling conventions for this platform.  The package you need
valgrind:  to install for fix (1) is called
valgrind: 
valgrind:    On Debian, Ubuntu:                 libc6-dbg
valgrind:    On SuSE, openSuSE, Fedora, RHEL:   glibc-debuginfo
valgrind: 
valgrind:  Cannot continue -- exiting now.  Sorry.

On Ubuntu, to install libc debug symbols, type:

sudo apt-get install libc6-dbg

If Valgrind runs correctly you will see something similar to the following output.

==29618== Memcheck, a memory error detector
==29618== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==29618== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==29618== Command: ./testapp 1000
==29618== 
==29618== 
==29618== HEAP SUMMARY:
==29618==     in use at exit: 76,704 bytes in 2 blocks
==29618==   total heap usage: 2 allocs, 0 frees, 76,704 bytes allocated
==29618== 
==29618== 4,000 bytes in 1 blocks are definitely lost in loss record 1 of 2
==29618==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==29618==    by 0x4007F0: main (in /tmp/test/build/testapp)
==29618== 
==29618== LEAK SUMMARY:
==29618==    definitely lost: 4,000 bytes in 1 blocks
==29618==    indirectly lost: 0 bytes in 0 blocks
==29618==      possibly lost: 0 bytes in 0 blocks
==29618==    still reachable: 72,704 bytes in 1 blocks
==29618==         suppressed: 0 bytes in 0 blocks
==29618== Reachable blocks (those to which a pointer was found) are not shown.
==29618== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==29618== 
==29618== For counts of detected and suppressed errors, rerun with: -v
==29618== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

This output clearly states that there is a memory-leak. There are many kinds of memory-leaks; two important ones are:

  • "definitely lost": you have a memory-leak.
  • "probably lost": you may have a memory-leak, unless you are doing something funny with moving heap pointers around.

Let's add the missing delete.

#include <iostream>
#include <cstring>
#include <stdlib.h>

using namespace std;

int main(int argc, char* argv[])
{
   int size = 10;
   if( argc == 2 ) size = atoi(argv[1]);
   int* p = new int[size];
   memset( p, 0, 10 );
   delete[] p;
   return 0;
}

Build and run Valgrind one more time. This time there is no error/ memory leak.

==30609== Memcheck, a memory error detector
==30609== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==30609== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==30609== Command: ./testapp 1000
==30609== 
==30609== 
==30609== HEAP SUMMARY:
==30609==     in use at exit: 72,704 bytes in 1 blocks
==30609==   total heap usage: 2 allocs, 1 frees, 76,704 bytes allocated
==30609== 
==30609== LEAK SUMMARY:
==30609==    definitely lost: 0 bytes in 0 blocks
==30609==    indirectly lost: 0 bytes in 0 blocks
==30609==      possibly lost: 0 bytes in 0 blocks
==30609==    still reachable: 72,704 bytes in 1 blocks
==30609==         suppressed: 0 bytes in 0 blocks
==30609== Reachable blocks (those to which a pointer was found) are not shown.
==30609== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==30609== 
==30609== For counts of detected and suppressed errors, rerun with: -v
==30609== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Valgrind tells us 2 allocation took place and 1 free of 76,704 bytes and no memory leaks on exit.

NOTE: if you want to get more detailed memory-leak output, use Valgrind like this:

valgrind --leak-check=full

You can also add the "-v" verbose option.

Few other types of issues which can be detected are :

  • Uninitialized variable detection (These errors can be difficult to track down in larger code, so to get more information to find the root cause use, "--track-origin=yes".  This option will cause slow down in the execution speed, so use it when you need to)
  • Invalid memory access
  • Buffer overrun detection
  • Source and destination memory overlap
  • System calls with inadequate read/write permission
  • Mismatch memory allocation and deletion
  • Double delete

Taken from the Valgrind site:

In C++ it's important to deallocate memory in a way compatible with how it was allocated. The deal is:

  • If allocated with malloc, calloc, realloc, valloc or memalign, you must deallocate with free.
  • If allocated with new, you must deallocate with delete.
  • If allocated with new[], you must deallocate with delete[].

Last words

Even if you are not having to track down a memory bug, as a professional you should be in the practice of executing all your code through Valgrind. You've seen how easy it is to use Valgrind, so turn it into a new habit.

Tools and Setup

colour-valgrind (python wrapper)

Heaptrack - A Heap Memory Profiler for Linux
Hello everyone, with a tingly feeling in my belly, I’m happy to announce heaptrack, a heap memory profiler for Linux. Over the last couple of months I’ve worked on this new tool in my free time. What started as a “what if” experiment quickly became such a promising tool that I couldn’t stop working …