23 Jun 2014

Integrate valgrind with your Testing

When testing your development work (automatically, preferably), it would be nice if the case of "forgetting to free memory" (as detected by valgrind) could be reported as a failure. To be honest, it strikes me as odd that valgrind doesn't return some sort of error status by default when it detects memory which was not freed by the developer.

Given the following code (which has an obvious memory leak, but returns a good status):

/*
 * Copyright (C) 2014  Trevor Woerner <trevor.woerner@linaro.org>
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
int
main (void)
{
        char *ptr;
 
        ptr = malloc(sizeof(*ptr) * 50);
        if (ptr == NULL) {
                perror ("malloc()");
                exit(EXIT_FAILURE);
        }
 
        strcpy(ptr, "hello");
        printf("ptr: %s\n", ptr);
 
        return 0;
}

Running valgrind against it:

$ valgrind ./memleak
==31562== Memcheck, a memory error detector
==31562== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==31562== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==31562== Command: ./memleak
==31562==
ptr: hello
==31562==
==31562== HEAP SUMMARY:
==31562==     in use at exit: 50 bytes in 1 blocks
==31562==   total heap usage: 1 allocs, 0 frees, 50 bytes allocated
==31562==
==31562== LEAK SUMMARY:
==31562==    definitely lost: 50 bytes in 1 blocks
==31562==    indirectly lost: 0 bytes in 0 blocks
==31562==      possibly lost: 0 bytes in 0 blocks
==31562==    still reachable: 0 bytes in 0 blocks
==31562==         suppressed: 0 bytes in 0 blocks
==31562== Rerun with --leak-check=full to see details of leaked memory
==31562==
==31562== For counts of detected and suppressed errors, rerun with: -v
==31562== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
 
$ echo $?
0

By default valgrind always returns the return value of the application under test; if the app returns zero, valgrind will return zero. The first step toward getting valgrind to return something other than what the test application returns is to define what it should return if it detects an error:

--error-exitcode=<number>

By itself this is not enough. In addition you also need to explicitly ask valgrind to perform a leak check:

$ valgrind --error-exitcode=22 --leak-check=yes ./memleak
==32424== Memcheck, a memory error detector
==32424== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==32424== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==32424== Command: ./memleak
==32424==
ptr: hello
==32424==
==32424== HEAP SUMMARY:
==32424==     in use at exit: 50 bytes in 1 blocks
==32424==   total heap usage: 1 allocs, 0 frees, 50 bytes allocated
==32424==
==32424== 50 bytes in 1 blocks are definitely lost in loss record 1 of 1
==32424==    at 0x4C280CD: malloc (vg_replace_malloc.c:292)
==32424==    by 0x400651: main (in /home/trevor/devel/code/doodles/memleak/memleak)
==32424==
==32424== LEAK SUMMARY:
==32424==    definitely lost: 50 bytes in 1 blocks
==32424==    indirectly lost: 0 bytes in 0 blocks
==32424==      possibly lost: 0 bytes in 0 blocks
==32424==    still reachable: 0 bytes in 0 blocks
==32424==         suppressed: 0 bytes in 0 blocks
==32424==
==32424== For counts of detected and suppressed errors, rerun with: -v
==32424== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) 
$ echo $?
22
With these options, valgrind can be integrated into an automated test framework to provide failures if someone forgets to explicitly free allocated memory. Being able to specify the value valgrind will return in this case makes it easy to differentiate between cases where the app fails and cases where valgrind detects an issue.

No comments: