Home Download as txt valgrind_ex1.c valgrind_ex2.c

Intro to Debugging With Valgrind

This is the page for my valgrind presentation. Here I will give some background that may be helpful for understanding the code examples and explain them in some depth. ALL explanations of the code are now available!

Background and Motivation

First some basic ideas from C that will be needed to understand the examples.

In C if you want the ability to dynamically creating structures (such as matrices) you will have to do memory allocation by hand. This gives you a lot of power since you no longer need to define matrix sizes before compilation. However this also adds more responsibility on you as the programmer since once you allocate memory you are responsible for it until your program exits.

In order to dynamically allocate memory the function ''malloc()'' is needed from ''stdlib.h''. The following line includes this library.

#include <stdlib.h>

The ''malloc()'' function's declaration is as follows from the man page.

void *malloc( size_t size );

In C ''void *'' is a general pointer that may be cast (have the interpretation changed) to any type of pointer you wish. The ''void *'' type declaration for the function means that ''malloc()'' with return a general pointer to the amount of memory (number of bytes) you specify in the ''size'' argument. Let's take a look at an example.

struct matrix { int m, n; float **data; };

Above we have a declaration for a matrix structure. In order to allocate memory for it, we would use the follow.

struct matrix * m = malloc( sizeof(struct matrix) );

This tells ''malloc()'' we want enough memory for a ''struct matrix'' through the ''sizeof()'' function call and to assign it to the ''struct matrix *'' variable m. We are now able to use this variable and allocate the memory we need for the data elements of the struct. However we are also responsible for returning this memory to the operating system after we are finished with it through the ''free()'' function. Used as follows.

struct matrix * m = malloc( sizeof(struct matrix) );
free( m );

This will return or free the memory that was allocated for the matrix. In doing this however, it is very easy to create bugs often with drastic consequences. If we attempt to use a variable without allocating memory for it, it can create segmentation faults or undefined behavior. If we allocate memory and than don't free it, it causes what are called memory leaks. These are often not too important since the memory the program uses will be returned to the operating system automatically when it exits. However it you are writing a program that runs continuously or with multiple threads memory leaks can eat up a lot of memory on a system and affect program performance.

These bugs are the motivation for using a memory checker tool such as valgrind and here I will explain some of the most common uses for valgrind and the types of bugs it can locate.

One note on gcc that I believe that is necessary to make. You must compile your code with the -g flag in order to tell the compiler to insert information into the executable that valgrind will be able to use to tell you useful debugging info. Valgrind will work on programs not compiled with this flag, however it's usefulness as a debugging tool will be greatly diminished.

valgrind_ex1.c

The first bug in possible in this program that can be tracked down with valgrind is a very simple memory leak. Consider the following line uncommented.

char * s = malloc(sizeof(char) * 100);

This simply allocates enough space for a size 100 character array. If we compile and run that code through as follows.

$ gcc -g -Wall -o valgrind_ex1 valgrind_ex1.c
$ valgrind ./valgrind_ex1

We get the following as part of the valgrind output.

==4597== HEAP SUMMARY:
==4597== in use at exit: 100 bytes in 1 blocks
==4597== total heap usage: 1 allocs, 0 frees, 100 bytes allocated

So valgrind has found our memory leak, now we would like it to tell us where it is, which would be more useful in a very large application. In order to do this we rerun valgrind with the --leak-check=full flag.

$ valgrind --leak-check=full ./valgrind_ex1 It now tells us the following. ==4830== HEAP SUMMARY: ==4830== in use at exit: 100 bytes in 1 blocks ==4830== total heap usage: 1 allocs, 0 frees, 100 bytes allocated ==4830== ==4830== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==4830== at 0x4C2993D: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==4830== by 0x400656: main (valgrind_ex1.c:46)

Now this is much more helpful. If you have the valgrind_ex1.c file open in an editor line 46 should be exactly where the memory for the char * was allocated. If you now uncomment the call to free, valgrind will say there are no memory leaks, as it should. It will still note that memory was allocated, but has been freed, as follows.

==4925== HEAP SUMMARY:
==4925== in use at exit: 0 bytes in 0 blocks
==4925== total heap usage: 1 allocs, 1 frees, 100 bytes allocated
==4925==
==4925== All heap blocks were freed -- no leaks are possible

The next bug that valgrind is useful for finding it uninitialized variables. If we re-comment lines 46 and 47 and uncomment lines 48 and 51 (the declaration of s2 and the print_array( s2 ) lines) then recompile and run you'll get something like the following.

$ ./valgrind_ex1
5658608 0 4195936 0 0 0 4195488 0 -973338160 32767

Your output may be slightly different because this data is junk, it's just whatever data was in the memory that was given to s2. This is clearly not the output data we want and sometimes when results of computations aren't coming out correctly, uninitialized data may be to blame. If we now run this with valgrind we see the following.

==4178== Conditional jump or move depends on uninitialised value(s)
==4178== at 0x4E7453E: vfprintf (in /lib/libc-2.15.so)
==4178== by 0x4E7E9C8: printf (in /lib/libc-2.15.so)
==4178== by 0x40061C: print_array (valgrind_ex1.c:35)
==4178== by 0x400658: main (valgrind_ex1.c:51)
==4178==
==4178== Use of uninitialised value of size 8
==4178== at 0x4E740DB: _itoa_word (in /lib/libc-2.15.so)
==4178== by 0x4E76640: vfprintf (in /lib/libc-2.15.so)
==4178== by 0x4E7E9C8: printf (in /lib/libc-2.15.so)
==4178== by 0x40061C: print_array (valgrind_ex1.c:35)
==4178== by 0x400658: main (valgrind_ex1.c:51)
==4178==
==4178== Conditional jump or move depends on uninitialised value(s)
==4178== at 0x4E740E5: _itoa_word (in /lib/libc-2.15.so)
==4178== by 0x4E76640: vfprintf (in /lib/libc-2.15.so)
==4178== by 0x4E7E9C8: printf (in /lib/libc-2.15.so)
==4178== by 0x40061C: print_array (valgrind_ex1.c:35)
==4178== by 0x400658: main (valgrind_ex1.c:51)

A lot of this may not make much sense, but not to worry, there's some specific information we can parse out of this that we can actually use to debug the program. Each of the three sections lists a memory error that valgrind has detected. It then lists exactly where the error happened and the function call trace to get to that point. If we look at the errors they each talk about uninitialized values, and the last line listed is line 51 in the main. This is really the only information that we need to fix this bug. Looking at line 51, it is the call to print_array, which uses the s2 variable, so that is where the problem must be. Now uncommenting the init_array function call, which fills the array with values from 0..N-1 and running the problem through valgrind gives the following output.

0 1 2 3 4 5 6 7 8 9
==1898==
==1898== HEAP SUMMARY:
==1898== in use at exit: 0 bytes in 0 blocks
==1898== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==1898==
==1898== All heap blocks were freed -- no leaks are possible

The expected output and no errors, exactly what we want.

Moving on to the next bug, uncomment the call to the allocate_stuff function in main, which just allocates memory in an infinite loop. Run the program now and it will just hang. It is very possible that you could end up with infinite loops in your program, and possibly they have nothing to do with memory errors in which case valgrind is little help. However many times especially when one is creating nodes for a tree or graph structure, the infinite loop ends up causing you to allocate memory indefinitely, in which case valgrind can be helpful. Now run the program through valgrind (with leak-check=full) and use CRTL+C to kill it and it produces the following output.

==2221== Command: ./valgrind_ex1
==2221==
^C==2221==
==2221== HEAP SUMMARY:
==2221== in use at exit: 5,971,576 bytes in 746,447 blocks
==2221== total heap usage: 746,447 allocs, 0 frees, 5,971,576 bytes allocated
==2221==
==2221== 5,971,568 bytes in 746,446 blocks are definitely lost in loss record 2 of 2
==2221== at 0x4C2993D: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2221== by 0x400595: allocate_stuff (valgrind_ex1.c:19)
==2221== by 0x400651: main (valgrind_ex1.c:58)

Here we see valgrind has located the function call in main that originated the problem and line in the allocate_stuff function which is allocating memory in the infinite loop, and if this were actually code worth using, we could fix it.

Moving down to line 60, uncommenting the declaration of A[10000000], compiling and running will produce a segfault. This is due to the fact that each function is given a stack frame in which to hold all the information it needs to run. Things like local variables, code location and return location. The space for this stack frame, like everything else in a computer is finite. When you declare a variable in the way A was here, instead of allocating memory for it, the entire thing must be stored in the stack frame and if it runs out of space you will get a segfault. Now run through valgrind and we see the following.

==2331== Warning: client switching stacks? SP change: 0x7ff000850 --> 0x7fa3b54b8
==2331== to suppress, use: --max-stackframe=79999896 or greater
==2331== Invalid write of size 4
==2331== at 0x400649: main (valgrind_ex1.c:45)
==2331== Address 0x7fa3b544c is on thread 1's stack
==2331==
==2331==
==2331== Process terminating with default action of signal 11 (SIGSEGV)
==2331== Access not within mapped region at address 0x7FA3B544C
==2331== at 0x400649: main (valgrind_ex1.c:45)

There is also a bunch of other information you needn't worry about. The useful information here is the line number and the reference to --max-stackframe. The line number, 45, refers to the open parenthesis of main function, where the invalid write to the stack occurred. This indicates the problem of having used the entire stack frame and of course in this case we know it is due to A, but this could also happen due to just having to many large enough arrays and none may cause the problem on their own.

Switching out focus to the next line we see that it is indeed to possible to get arrays of size 10000000, they just need to be allocated dynamically which means the memory is in what it called the heap, where all memory you allocate dynamically is stored and this can hold much more than a stack for a function.

The last bug in this program is caused by the last line and the function call to stack_overflow, which as its name suggests causes a stack overflow (by calling itself recursively with no end). This again has to do with the stack frame that each function has. Each program has a finite amount of space for a stack which holds all the stack frames for each function that gets called, pushing them and popping the on and off the stack with each call. When a recursive function get called to many times, this stack will run out of space, which is called stack overflow. Running the program through valgrind give the following output.

==2855== Stack overflow in thread 1: can't grow stack to 0x7fe801ff8
==2855==
==2855== Process terminating with default action of signal 11 (SIGSEGV)
==2855== Access not within mapped region at address 0x7FE801FF8
==2855== at 0x400637: stack_overflow (valgrind_ex1.c:41)

Which gives us the exact error and location of the trouble function call on line 41.

/************************************************
 * valgrind_ex1.c
 *
 * Code for some valgrind examples.
 *
 * Author: Jeffrey Picard
 ***********************************************/

#include <stdlib.h>
#include <stdio.h>

#define N 10

void allocate_stuff( void )
{
  double * stuff;
  while(1)
  {
    stuff = malloc( sizeof(double) * 1 );
    *stuff = 0;
  }
}

void init_array( int s[N] )
{
  int i = 0;
  for( i = 0; i < N; i++ )
    s[i] = i;
}

void print_array( int s[N] )
{
  int i = 0;
  for( i = 0; i < N; i++ )
    printf("%d ", s[i]);
  puts("");
}

void stack_overflow( void )
{
  stack_overflow();
}

int main( int argc, char **argv )
{
  //char * s = malloc(sizeof(char) * 100);
  //free( s );
  //int s2[N];

  //init_array( s2 );
  //print_array( s2 );

  //void (*lol)( int[N] ) = NULL;
  //lol = &print_array;
  //lol( s2 );


  //allocate_stuff();

  //double A[10000000];
  //double *A = malloc(sizeof(double) * 100000000);

  //stack_overflow();

  return 0;
}

valgrind_ex2.c

This example attempts to a few bugs that can crop up in code that one may actually wish to use. Here I have written a simple matrix struct and a few simple utility functions to go along with it. 'matrix_alloc' takes a specification for the size of the matrix as two integers, allocates the space for it and returns a pointer to the matrix. 'matrix_init' takes a matrix and initializes it with some data or no particular persuasion. 'matrix_print' prints the matrix to stdout. 'matrix_free' takes a matrix pointer and frees the memory that was allocated for the matrix.

The first bug in this code is a NULL pointer deference. the matrix A is set to null and then 'print_matrix' is called. If we run this program we get a segfault. If we run it through valgrind we should something like the following.

==6033== Command: ./valgrind_ex2
==6033==
==6033== Invalid read of size 4
==6033== at 0x40075E: matrix_print (valgrind_ex2.c:43)
==6033== by 0x4007EB: main (valgrind_ex2.c:66)
==6033== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==6033==

Indeed, looking at the code on line 43, we are trying to access 'A->m' an integer which is usually 4 bytes in size. This indicates that perhaps A is NULL, which is our case it is. To fix this a check for NULL should be used before anything is done in a function with 'matrix_print'. This code be done by simple adding the lines:

if( !A )
{
  fprintf( stderr, "Error! Matrix is NULL");
  return;
}

Or something to that affect. The '!A' works in C, as would 'if( A == NULL )' or 'if( A == 0 )' because boolean values are merely considered to be non-zero is true and zero is false and NULL is equal to zero.

The next bug in this program follows logically from the first. If we decide to fix that my initializing the matrix to say a 3x3 matrix, as the commented code on line 62 indicates, and then attempt to print it we get an uninitialized value error. Unfortunately in this case if we just run the program nothing dramatic happens and the data might even turn out to be zero, which is what happened when I ran it.

$ ./valgrind_ex2
[0.00][0.00][0.00]
[0.00][0.00][0.00]
[0.00][0.00][0.00]

The zeros don't seem like a red flag right away, but that's probably not the data you wanted, or maybe it is, either way running the program through valgrind we get the following.

==6259== Conditional jump or move depends on uninitialised value(s)
==6259== at 0x4E7B598: __printf_fp (in /lib/libc-2.15.so)
==6259== by 0x4E77586: vfprintf (in /lib/libc-2.15.so)
==6259== by 0x4E7E9C8: printf (in /lib/libc-2.15.so)
==6259== by 0x40073B: matrix_print (valgrind_ex2.c:46)
==6259== by 0x4007F6: main (valgrind_ex2.c:66)
==6259==
==6259== Syscall param write(buf) points to uninitialised byte(s)
==6259== at 0x4F081A0: __write_nocancel (in /lib/libc-2.15.so)
==6259== by 0x4EA41C2: _IO_file_write@@GLIBC_2.2.5 (in /lib/libc-2.15.so)
==6259== by 0x4EA4089: new_do_write (in /lib/libc-2.15.so)
==6259== by 0x4EA53C4: _IO_do_write@@GLIBC_2.2.5 (in /lib/libc-2.15.so)
==6259== by 0x4EA5706: _IO_file_overflow@@GLIBC_2.2.5 (in /lib/libc-2.15.so)
==6259== by 0x4E9BAF9: puts (in /lib/libc-2.15.so)
==6259== by 0x400755: matrix_print (valgrind_ex2.c:47)
==6259== by 0x4007F6: main (valgrind_ex2.c:66)
==6259== Address 0x4023003 is not stack'd, malloc'd or (recently) free'd
==6259==
[0.00][0.00][0.00]
[0.00][0.00][0.00]
[0.00][0.00][0.00]
==6259==
==6259== HEAP SUMMARY:
==6259== in use at exit: 76 bytes in 5 blocks
==6259== total heap usage: 5 allocs, 0 frees, 76 bytes allocated

There will also be many more 'Conditional jump or move depends on uninitialized value(s)' errors, but we only need one for the info we want. The mere fact we're getting them is an indication something is wrong and indeed valgrind has hit the nail on the head. We haven't put any data in our matrix!

Uncommenting the call to 'matrix_init' solves this problem and our output should not looking something like the following.

$ ./valgrind_ex2
[0.00][1.00][2.00]
[3.00][4.00][5.00]
[6.00][7.00][8.00]

And in valgrind.

==6580== Command: ./valgrind_ex2
==6580==
[0.00][1.00][2.00]
[3.00][4.00][5.00]
[6.00][7.00][8.00]
==6580==
==6580== HEAP SUMMARY:
==6580== in use at exit: 76 bytes in 5 blocks
==6580== total heap usage: 5 allocs, 0 frees, 76 bytes allocated
==6580==
==6580== LEAK SUMMARY:
==6580== definitely lost: 16 bytes in 1 blocks
==6580== indirectly lost: 60 bytes in 4 blocks
==6580== possibly lost: 0 bytes in 0 blocks
==6580== still reachable: 0 bytes in 0 blocks
==6580== suppressed: 0 bytes in 0 blocks

Yay! No more uninitialized data and it's printing right! However we still have one last bug and valgrind is currently yelling at us about it, as it has been the last few times we've run it. We aren't freeing the matrix after we use, and are leaking memory. In practice this might not always be a problem if you're exiting soon after doing any calculations or there's a ton of memory on the machine you're using, but it's good programming practice to free all your memory regardless.

So finally uncomment the call to 'matrix_free' and at last valgrind is happy with us. You should get something like the following

==6705== Command: ./valgrind_ex2
==6705==
[0.00][1.00][2.00]
[3.00][4.00][5.00]
[6.00][7.00][8.00]
==6705==
==6705== HEAP SUMMARY:
==6705== in use at exit: 0 bytes in 0 blocks
==6705== total heap usage: 5 allocs, 5 frees, 76 bytes allocated
==6705==
==6705== All heap blocks were freed -- no leaks are possible


/************************************************
 * valgrind_ex2.c
 *
 * Code for some valgrind examples.
 *
 * Author: Jeffrey Picard
 ***********************************************/

#include <stdlib.h>
#include <stdio.h>

typedef struct matrix_struct
{
  int m, n;
  float **data;
} matrix;

matrix * matrix_alloc( int m, int n )
{
  matrix * A = malloc(sizeof(matrix));
  A->m = m;
  A->n = n;
  A->data = malloc( sizeof(float*) * m );
  int i;
  for( i = 0; i < m; i++ )
    A->data[i] = malloc( sizeof(float) * n );

  return A;
}

void matrix_init( matrix * A )
{
  int i, j;
  float z = 0;
  for( i = 0; i < A->m; i++ )
    for( j = 0; j < A->n; j++ )
      A->data[i][j] = z++;
}

void matrix_print( matrix * A )
{
  int i, j;
  for( i = 0; i < A->m; i++ )
  {
    for( j = 0; j < A->n; j++ )
      printf("[%0.2f]", A->data[i][j] );
    puts("");
  }
}

void matrix_free( matrix *A )
{
  int i;
  for( i = 0; i < A->m; i++ )
    free( A->data[i] );
  free( A->data );
  free( A );
}

int main( int argc, char **argv )
{
  matrix * A = NULL; //matrix_alloc(3,3);

  //matrix_init( A );

  matrix_print( A );

  //matrix_free( A );

  return 0;
}

Hooray! You know how to use valgrind now and maybe learned some interesting stuff about C as well.