UC3M

Telematic/Audiovisual Syst./Communication Syst. Engineering

Systems Architecture

September 2017 - January 2018

Chapter 17.  The memory profiler Valgrind

Valgrind is a system for debugging and profiling Linux programs useful to automatically detect many memory management and threading bugs, making programs more stable and robust. Unfortunately, it cannot solve out all issues in a piece of code and requires interaction with the programmer which should carefully analyze the output of a program. In this document, we introduce two Valgrind tools: Memcheck and Helgrind.

17.1.  Memcheck tool

There are several tools included with Valgrind. The default and most used tool is Memcheck which inserts extra instrumentation code around most instructions, which keeps track of the validity and addressability. Memcheck replaces the standard C memory allocator with its own implementation, which also includes memory guards around all allocated blocks. The problems Memcheck may detect and warn include the following issues: (1) use of uninitialized memory, (2) reading/writing memory after it has been released, (3) reading/writing in illegal sections, and (4) potential memory leaks.

Before listing the diferent types of problems detected by the tool, we show how to interact with the tool by means of a simple example (without any anomaly) but able to illustrate the functionality. It consists on a hello-world program which does not accomplish any functionality.

1
2
3
4
5
6
7
8
9
// Save this code as valgrind_hello_good.c 
// Compile with compiler:   $ gcc -Wall -gstabs valgrind_hello_good.c -o valgrind_hello_good
// Test with  valgrind:     $ valgrind --tool=memcheck  ./valgrind_hello_good
//
#include <stdlib.h>
int main()
{
  return 0;
} 

This simple example has not any type of anomaly Valgrind can detect with Memcheck. As we show later, after the compilation of the source code valgrind_hello_good.c and running the image created with gcc by means of Valgrind-Memcheck, there are zero errors.

$ gcc -Wall -gstabs valgrind_hello_good.c -o valgrind_hello_good
$ valgrind --tool=memcheck  --leak-check=full -v ./valgrind_hello_good

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

==17635== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==17635== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

So, let's introduce a simple memory leak in our code. A simple way of doing so it is with a malloc in the main code:

1
2
3
4
5
6
7
8
9
10
// Save as valgrind_hello_bad.c
// Compile with debug info: $ gcc  -Wall -gstabs valgrind_hello_bad.c -o valgrind_hello_bad
// Test with  valgrind:     $ valgrind --tool=memcheck  ./valgrind_hello_bad
//
#include <stdlib.h>
int main()
{
  void* ptr=malloc(1); 
  return 0;
}

And now, Valgrind reports about a memory leak of 1 byte in its output:

$ gcc -Wall -gstabs valgrind_hello_bad.c -o valgrind_hello_bad
$ valgrind --tool=memcheck  --leak-check=full -v ./valgrind_hello_bad 

==17722== HEAP SUMMARY:
==17722==     in use at exit: 1 bytes in 1 blocks
==17722==   total heap usage: 1 allocs, 0 frees, 1 bytes allocated
==17722== 
==17722== Searching for pointers to 1 not-freed blocks
==17722== Checked 55,996 bytes
==17722== 
==17722== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
==17722==    at 0x402BE68: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==17722==    by 0x80483F8: main (valgrind_hello_bad.c:8)
==17722== 
==17722== LEAK SUMMARY:
==17722==    definitely lost: 1 bytes in 1 blocks
==17722==    indirectly lost: 0 bytes in 0 blocks
==17722==      possibly lost: 0 bytes in 0 blocks
==17722==    still reachable: 0 bytes in 0 blocks
==17722==         suppressed: 0 bytes in 0 blocks
==17722== 
==17722== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==17722== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) 

If the malloc statement is to be kept in the modifyied version, one way of getting rid of the memory leak is to use a free just before the end of the main:

1
2
3
4
5
6
7
8
9
10
11
// Save as valgrind_hello_bad_solved.c
// Compile with debug info: $ gcc -gstabs valgrind_hello_bad_solved.c -o valgrind_hello_bad_solved
// Test with  valgrind:     $ valgrind --tool=memcheck  ./valgrind_hello_bad_solved
//
#include <stdlib.h>
int main()
{
  void* ptr=malloc(1); 
  free(ptr);
  return 0;
} 

17.1.1.  Illegal read/write

One type of problems detected is warning about the code that it is reading/writing illegal positions from a program. Let's look at an example that consists of an illegal writing on the zero memory position. This illegal access is performed by writing a memory address that is not in the program and releasing the alarm Memcheck:

1
2
3
4
5
6
7
8
9
10
// Illegal write
// gcc -gstabs valgrind_illegal_write.c -o valgrind_illegal_write
// $ valgrind  -v ./valgrind_illegal_write 
#include <stdlib.h>
int main()
{
  int *ptr=0;
  (*ptr)=33;
  return 0;
} 

Let's see how Valgrind reports (with a illegal write) about this issue in the main function:

$ gcc -gstabs -Wall valgrind_illegal_read_write.c -o valgrind_illegal_read_write
$ valgrind -v ./valgrind_illegal_read_write 
  
--6429--   .. CRC is valid
--6429-- REDIR: 0x40d01e0 (strnlen) redirected to 0x40254a0 (_vgnU_ifunc_wrapper)
--6429-- REDIR: 0x40d1730 (strncasecmp) redirected to 0x40254a0 (_vgnU_ifunc_wrapper)
--6429-- REDIR: 0x40d0380 (__GI_strrchr) redirected to 0x402c1b0 (__GI_strrchr)
==6429== Invalid write of size 4
==6429==    at 0x80483C4: main (valgrind_illegal_read_write.c:8)
==6429==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==6429== 
==6429== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

17.1.2.  Uninitialised variables

Another type of issue that Valgrind detects is uninitialised variables. To illustrate this issue , the following listing contains a variable: number which has not been properly initialised.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Unitialized
// gcc -gstabs valgrind_illegal_write.c -o valgrind_illegal_write
// $ valgrind  -v ./valgrind_illegal_write 
#include <stdlib.h>
#include <stdio.h>
int main()
{
  int number; //Should be initialized (i.e. to zero).
  if (number==0)
  {
    printf("number is zero");
  }
    
  return 0;
} 

This is how Memcheck reports on this error:

$ gcc -gstabs -Wall valgrind_unitialized.c -o valgrind_unitialized
$ valgrind -v --track-origins=yes ./valgrind_unitialized

==6684== 
==6684== 1 errors in context 1 of 1:
==6684== Conditional jump or move depends on uninitialised value(s)
==6684==    at 0x80483F2: main (valgrind_unitialized.c:9)
==6684==  Uninitialised value was created by a stack allocation
==6684==    at 0x80483EA: main (valgrind_unitialized.c:7)
==6684== 
==6684== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

17.1.3.  Illegal memory frees

Another issue checked by Memcheck is illegal uses of free statement.

1
2
3
4
5
6
7
8
9
10
11
12
13
// illegal free
// gcc -gstabs valgrind_illegal_free.c -o valgrind_illegal_free
// $ valgrind  -v ./valgrind_illegal_free 
#include <stdlib.h>
#include <stdio.h>
int main()
{
  int * ptr=malloc(4);
  free(ptr);
  ptr++; //To mismatch 
  free(ptr);  
  return 0;
} 

In this case, the free statement is peformed on a memory position which has not been allocated with malloc, being the root of the problem.

$ gcc -gstabs -Wall valgrind_illegal_free.c -o valgrind_illegal_free
$ valgrind -v --track-origins=yes ./valgrind_illegal_free
==12950== Memcheck, a memory error detector
==12950== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==12950== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==12950== Command: ./valgrind_illegal_free
==12950== 
==12950== Invalid free() / delete / delete[] / realloc()
==12950==    at 0x402B06C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==12950==    by 0x8048449: main (valgrind_illegal_free.c:11)
==12950==  Address 0x41fd02c is 0 bytes after a block of size 4 free'd
==12950==    at 0x402B06C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==12950==    by 0x8048438: main (valgrind_illegal_free.c:9)
==12950== 
==12950== 
==12950== HEAP SUMMARY:
==12950==     in use at exit: 0 bytes in 0 blocks
==12950==   total heap usage: 1 allocs, 2 frees, 4 bytes allocated
==12950== 
==12950== All heap blocks were freed -- no leaks are possible
==12950== 
==12950== For counts of detected and suppressed errors, rerun with: -v
==12950== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

17.1.4.  Memory leak

Lastly, there is a functionality in charge of detecting memory leaks, which is one of the most interesting features of valgrind. The following listing include an example which has a function leak that performs a memory leak of 100 bytes, per invocation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// memory_leak
// gcc -gstabs valgrind_memory_leak.c -o valgrind_memory_leak
// $ valgrind  -v ./valgrind_memory_leak
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void leak()
{  
  void *ptr=malloc(100);
  ptr++;
}
int main()
{
  printf("#Let's leak 100 bytes");
  leak();
  printf("#100 bytes leaked");
  return 0;
} 

This the trace of Valgrind, which corresponds to the previous listing, which shows how to Memcheck warns about this issue:

$ gcc -g -Wall valgrind_memory_leak.c -o valgrind_memory_leak
$ valgrind -v --leak-check=full --show-reachable=yes  ./valgrind_memory_leak
    
--7436-- REDIR: 0x40cbe70 (malloc) redirected to 0x402be00 (malloc)
Let's leak 100 bytes100 bytes leaked==7436== 
==7436== HEAP SUMMARY:
==7436==     in use at exit: 100 bytes in 1 blocks
==7436==   total heap usage: 1 allocs, 0 frees, 100 bytes allocated
==7436== 
==7436== Searching for pointers to 1 not-freed blocks
==7436== Checked 55,964 bytes
==7436== 
==7436== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==7436==    at 0x402BE68: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==7436==    by 0x8048425: leak (valgrind_memory_leak.c:10)
==7436==    by 0x8048445: main (valgrind_memory_leak.c:16)
==7436== 
==7436== LEAK SUMMARY:
==7436==    definitely lost: 100 bytes in 1 blocks
==7436==    indirectly lost: 0 bytes in 0 blocks
==7436==      possibly lost: 0 bytes in 0 blocks
==7436==    still reachable: 0 bytes in 0 blocks
==7436==         suppressed: 0 bytes in 0 blocks
==7436== 
==7436== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==7436== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)