Reporting of Errors

Thanks to the Memcheck feature Valgrind is excellent free software for code debugging. It is suitable for making quality control checks of code. It is able to detect two kinds of errors: access at bad memory address and find uninitialised values. We will now show how Valgrind error messages of such can be interpreted and some common program errors that can be tracked easily.

It is easy to see the presence of the errors in the following code. There are two incorrect instructions for accessing at memory. The instructions make refer to 4 bytes beyond the space allocated for the buffer a. Valgrind checks them and prints a message on the standard output. When Valgrind finds an error it tells you what kind of error it is and below the stack traces tells you where the problem occurred.

Example 1 - Can you spot the error?:

#include <stdlib.h>
#include <stdio.h>
#define DIM 1000

int main(int argc, char *argv[]) {

   float *a, k = 2.0, sup;
   int i;         
   a = (float *) malloc( sizeof(float) * DIM );   

   for( i = 0; i < DIM; ++i) a[i] = 0.0;                  
      sup = k;             
      k = a[i]; // memory out-of-bound error in read operation        
      a[i] = sup; // memory out-of-bound error in write operation   
      printf("GOOD END \n");
      free(a); 

      return(EXIT_SUCCESS);     
} 

Resulting Valgrind Output:

$module load dev valgrind/gcc/3.10.1
$gcc -O0 -g -Wall err_memory_access.c
$valgrind ./a.out
==16503== Memcheck, a memory error detector
==16503== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==16503== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==16503== Command: ./a.out
==16503==
==16503== Invalid read of size 4
==16503== at 0x4006B2: main (ex1.c:17)
==16503== Address 0x51a6fe0 is 0 bytes after a block of size 4,000 alloc'd
==16503== at 0x4C2707F: malloc (vg_replace_malloc.c:296)
==16503== by 0x40065D: main (ex1.c:12)
==16503==
==16503== Invalid write of size 4
==16503== at 0x4006D4: main (ex1.c:18)
==16503== Address 0x51a6fe0 is 0 bytes after a block of size 4,000 alloc'd
==16503== at 0x4C2707F: malloc (vg_replace_malloc.c:296)
==16503== by 0x40065D: main (ex1.c:12)
==16503==
GOOD END
==16503==
==16503== HEAP SUMMARY:
==16503== in use at exit: 0 bytes in 0 blocks
==16503== total heap usage: 1 allocs, 1 frees, 4,000 bytes allocated
==16503==
==16503== All heap blocks were freed -- no leaks are possible
==16503==
==16503== For counts of detected and suppressed errors, rerun with: -v
==16503== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 3 from 3)

 

Example 2 - And this time ?

#include <stdlib.h>
#include <stdio.h>
void f(void)
{
   int* x = malloc(10 * sizeof(int));
   x[9] = 2;
} // problem: memory leak -- x not freed

int main(int argc, char * argv[])
{
   f();
   printf("GOOD END \n");
   return 0;
}

 

Resulting Valgrind Output:

$module load dev valgrind/gcc/3.10.1
$gcc -O0 -g -Wall err_heap_memory_leak.c
$./a.out
GOOD END
$valgrind --leak-check=full ./a.out
==38820== Memcheck, a memory error detector
==38820== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==38820== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==38820== Command: ./a.out
==38820==
GOOD END
==38820==
==38820== HEAP SUMMARY:
==38820== in use at exit: 40 bytes in 1 blocks
==38820== total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==38820==
==38820== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==38820== at 0x4C2707F: malloc (vg_replace_malloc.c:296)
==38820== by 0x4005F9: f (ex2.c:6)
==38820== by 0x400622: main (ex2.c:12)
==38820==
==38820== LEAK SUMMARY:
==38820== definitely lost: 40 bytes in 1 blocks
==38820== indirectly lost: 0 bytes in 0 blocks
==38820== possibly lost: 0 bytes in 0 blocks
==38820== still reachable: 0 bytes in 0 blocks
==38820== suppressed: 0 bytes in 0 blocks
==38820==
==38820== For counts of detected and suppressed errors, rerun with: -v
==38820== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 3 from 3

Here you can see that Valgrind tracks the bytes of memory lost. It keeps trace of all the data allocated at runtime and at the end it checks which haven't been correctly freed. In this case the function f() has been called only once and it wasn't dangerous. Usually a memory leak is a tedious problem that simply grows the memory requirement unnecessarily. If a code has memory leak increasing the problem size at some point may well trigger a crash.

 

Example 3 - And this time ?

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

int main(int argc, char *argv[]) {   

   float *a;   
   int i, j;   
   int mydim = DIM;   
   
   a = (float *)malloc(sizeof(float)*mydim);   
   printf("I'm here but how do I find the ERROR!\n");   
   j = mydim; 
 
   for(i=0; i < mydim; i++)  {     
     a[i] = i;     
     
     if(i > mydim-2)     {       
     a[j+1000] = a[i];     
     a[mydim-1] = a[i];   
     }   
   }   

  printf("a = %f \n", a[0]);  
  printf("GOOD END \n"); 

  free(a);
  return(EXIT_SUCCESS);
}

 

Resulting Valgrind Output:

$module load dev valgrind/gcc/3.10.1
$gcc -O0 -g -Wall illegal.c
$./a.out
Segmentation fault (core dumped)
$valgrind --leak-check=full ./a.out
==37314== Memcheck, a memory error detector
==37314== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==37314== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==37314== Command: ./a.out
==37314==
I'm here but how do I find the ERROR!
==37314== Invalid write of size 4
==37314== at 0x40071E: main (ex3.c:21)
==37314== Address 0x559e390 is 3,936 bytes inside an unallocated block of size 35,760 in arena "client"
==37314==
a = 0.000000
GOOD END
==37314==
==37314== HEAP SUMMARY:
==37314== in use at exit: 0 bytes in 0 blocks
==37314== total heap usage: 1 allocs, 1 frees, 4,158,384 bytes allocated
==37314==
==37314== All heap blocks were freed -- no leaks are possible
==37314==
==37314== For counts of detected and suppressed errors, rerun with: -v
==37314== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 3 from 3)

Here, when we try to read the value of a[j+1000] we get an error message, because we attempted to access a memory location that it is not allowed. A segmentation fault (often shortened to segfault) is a particular error condition that can occur during the operation of computer software. A segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed. This is managed by the operating system’s memory management layer.

Supported By

File Browser Reference
Department FHERIS
University of Galway
HEA Logo