Introduction to valgrind

Introduction

Valgrind is a debugging tool. It runs target application in a virtual machine (this can cause larger programs to run slower).

Valgrind in Action

Lets write a simple program.

// testprogram.c
#include <stdio.h>
void exitfunction(void (*exitfun)());
void myexit();
struct add {
 int host;
 int port;
 };
struct datastruct {
 int data;
 int dataLength;
 struct add address;
 };
struct evt {
 struct datastruct *peer;
 struct datastruct *packet;
 int type;
 int channelID;
 };
int main()
 {
 const int ENET_EVENT_TYPE_NONE = 0;
 const int ENET_EVENT_TYPE_CONNECT = 1;
 const int ENET_EVENT_TYPE_RECEIVE = 2;
 const int ENET_EVENT_TYPE_DISCONNECT = 3;
 struct evt event;
 event.type = ENET_EVENT_TYPE_CONNECT;
 event.peer->data = 55;
 event.peer->address.host = 56;
 event.peer->address.port = 57;
 printf("Data : %d\n",  event.peer->data);
 printf("Host: %d\n", event.peer->address.host);
 printf("Port: %d\n", event.peer->address.port);
 exitfunction(myexit);
 return 0;
 }
void myexit()
{
 printf("You came to my exit function\n");
}
void exitfunction(void (*exitfun)()){
 exitfun();
}

###############

We can compile the application with

$ gcc -o testprogram testprogram.c
$ ./testprogram

This program will exit silently. What happened?

We at least had to see some text printed and myexit function executed. Where did the program go wrong? Why then it is not showing any error. In such a small program with careful observation it is very easy to find what I have missed. Even more, I have delibrately made that error to show how we are going to use valgrind.

However in a large application we can miss many things. Lets see how we can use valgrind.

Run this program once again in valgrind. Since we don’t need other parameters like leak-check memcheck, we are just going to run valgrind in its default setting.

$ valgrind ./testprogram

Here is the output

#####################
 $ valgrind ./testprogram
 ==16490== Memcheck, a memory error detector
 ==16490== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
 ==16490== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
 ==16490== Command: ./testprogram
 ==16490==
 ==16490== Use of uninitialised value of size 8
 ==16490==    at 0x400572: main (in /tmp/testprogram)
 ==16490==
 ==16490== Invalid write of size 4
 ==16490==    at 0x400572: main (in /tmp/testprogram)
 ==16490==  Address 0xf0b2e3 is not stack'd, malloc'd or (recently) free'd
 ==16490==
 ==16490==
 ==16490== Process terminating with default action of signal 11 (SIGSEGV)
 ==16490==  Access not within mapped region at address 0xF0B2E3
 ==16490==    at 0x400572: main (in /tmp/testprogram)
 ==16490==  If you believe this happened as a result of a stack
 ==16490==  overflow in your program's main thread (unlikely but
 ==16490==  possible), you can try to increase the size of the
 ==16490==  main thread stack using the --main-stacksize= flag.
 ==16490==  The main thread stack size used in this run was 8388608.
 ==16490==
 ==16490== HEAP SUMMARY:
 ==16490==     in use at exit: 0 bytes in 0 blocks
 ==16490==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
 ==16490==
 ==16490== All heap blocks were freed -- no leaks are possible
 ==16490==
 ==16490== For counts of detected and suppressed errors, rerun with: -v
 ==16490== Use --track-origins=yes to see where uninitialised values come from
 ==16490== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)
 Segmentation fault (core dumped)
 ################################

If you look at texts like

a. ==16490== Use of uninitialised value of size 8 at 0x400572
b. Invalid write of size 4 ==16490==    at 0x400572
c. Address 0xf0b2e3 is not stack’d, malloc’d or (recently) free’d
d. Process terminating with default action of signal 11 (SIGSEGV)
SIGSEGV (according to wikipedia) :

SIGSEGV is an abbreviation for Signal Segmentation Violation. On POSIX-compliant platforms, SIGSEGV is the signal sent to a process when it makes an invalid memory reference, or segmentation fault.

e. Segmentation fault (core dumped) :

Segmentation fault (according to wikipedia) :

A segmentation fault (often shortened to segfault), bus error or access violation is generally an attempt to access memory that the CPU cannot physically address. It occurs when the hardware notifies an operating system about a memory access violation. The OS kernel then sends a signal to the process which caused the exception. By default, the process receiving the signal dumps core and terminates.

If you look at any of these 5 messages you can clearly say we tried to use an uninitialized memory.

What caused this error?

Lets correct our source code and see this again.

My guess is we created a pointer of structure peer in structure evt, but failed to allocate memory for it. In main function above modify following code

struct evt event;
 event.type = ENET_EVENT_TYPE_CONNECT;

as

struct evt event;
 event.peer = (struct datastruct *) malloc (sizeof (struct datastruct));
 event.type = ENET_EVENT_TYPE_CONNECT;

and

printf("Port: %d\n", event.peer->address.port);
exitfunction(myexit);

as

printf("Port: %d\n", event.peer->address.port);
free(event.peer);
exitfunction(myexit);

We introduced one line to allocate memory for event.peer and another line to free it at the end. You have to do one more thing to make this program work. Add a header called stdlib.h at top as

#include <stdlib.h>

If you compile this program and run it will run.

$ gcc -o testprogram testprogram.c
 $ ./testprogram

If we now run the application in valgrind we see following output.

$ valgrind ./testprogram
 ################
 $ valgrind ./testprogram
 ==25874== Memcheck, a memory error detector
 ==25874== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
 ==25874== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
 ==25874== Command: ./testprogram
 ==25874==
 Data : 55
 Host: 56
 Port: 57
 You came to my exit function
 ==25874==
 ==25874== HEAP SUMMARY:
 ==25874==     in use at exit: 0 bytes in 0 blocks
 ==25874==   total heap usage: 1 allocs, 1 frees, 16 bytes allocated
 ==25874==
 ==25874== All heap blocks were freed -- no leaks are possible
 ==25874==
 ==25874== For counts of detected and suppressed errors, rerun with: -v
 ==25874== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
 ################

Everything worked perfectly here. One interesting thing added to this test is that it shows we allocated one heap memory (free memory outside stack) and freed it. We used 16 bytes for this.

Conclusion

Valgrind is an award winning application used for many large projects. I hope you were introduced about it in this tutorial. Hope you will explore more about it in http://valgrind.org/docs/manual/. Good Luck.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s