February 11, 2007

Get the Better of Memory Leaks with Valgrind

This article was published in the "Linux For You" magazine in February, 2006.

Memory leaks can cause problems and bugs in software which can be hard to detect. In this article we shall discuss Valgrind--an open source tool that helps you to detect and fix memory leaks in your applications.


Modern software development techniques employ great innovation. Extreme care is taken to ensure that the code written performs well, whether it is on the User Interface (GUI) front, features or performance front. With the rapid advent of applications that are being developed and ported on GNU/Linux platform, it becomes essential for a GNU/Linux developer to develop software which caters to the user’s need and performs well on a variety of hardware--almost the entire gamut of hardware that GNU/Linux runs on.

With the embedded domain on GNU/Linux really catching up and the growing number of applications being developed for embedded devices like mobiles, PDAs, gaming consoles etc., it has become essential to have software which utilizes the limited memory available on these systems and makes best use of it.

In spite of great care being taken for developing quality software, developers being humans, are bound to err. This results in bugs in the software. One such bug which can cause problems on PC and spell catastrophe in embedded devices is a “Memory Leak”.

In this article, we shall discuss about a free and open-source tool called Valgrind, with which you can easily detect and fix memory leaks in your applications.

What Exactly is a Memory Leak?

According to Valgrind manual: A memory leak is a part of memory that has been allocated but not freed after its usage or when pointer to a memory allocation is deleted, thereby making the memory unusable. The more often this memory leak occurs the more valuable memory will be wasted and taken away from other processes thereby affecting the whole system. If your applications’ usage exceeds the virtual memory size, it will crash the system.

According to Valgrind manual, some of the common memory related errors are:

  • Use of uninitialized memory.
  • Reading/writing memory after it has been freed.
  • Reading/writing off the end of malloced blocks.
  • Reading/writing inappropriate areas on the stack.
  • Memory leaks – where pointers to malloced blocks are lost forever.
  • Mismatched use of malloc/new/new[] vs free/delete/delete[].
  • Some misuses of the POSIX pthreads API.

Memory leak detection tools

Various tools are available for detecting memory leaks and other bugs in your programs. The most popular and well known tools are Purify (IBM) and Valgrind. IBM’s Purify runs on GNU/Linux and Windows but is proprietary and expensive. Being tux lovers we shall focus on a free, open-source memory-leak detection tool called Valgrind.

Displaying Virtual Memory (VM) usage
To begin with, let’s check out the memory usage of your application using a commonly used utility ‘ps’ available on all *nix platforms.

‘ps’ command displays the process status. Using the various options available with it, it can be used for displaying an application’s process-id, memory usage, cpu-utilization etc. For example, to display the process name, process id and VM usage of currently running applications in the shell, following command should be used:

$ ps -o cmd,pid,vsize

Numerous options for the ‘ps’ command are available through which you can view resource utilization of an application.

If you have installed GNOME, you can use ‘gnome-system-monitor’ command to view the process id, VM usage etc. in a GUI window.

Using the above you can check the VM usage of your application, but to dig deep inside your code for memory leaks, you need Valgrind.

Tip: You can also use 'vmstat' command to display the VM usage of your applications.

Getting and installing Valgrind
You can download the latest version of Valgrind from this month’s LFY CD or from http://valgrind.org/downloads/source_code.html.

For installing Valgrind first extract it, using:
$ tar –jxvf valgrind-version.tar.bz2
$ cd valgrind-version

Follow the three well-known steps:
$ ./configure
$ make
$ make install

This would install Valgrind onto your system.

Using Valgrind
To check your application for memory leaks, place valgrind before the application name while executing the application.
$ valgrind

Valgrind can be used on existing GNU/Linux commands, for example:
$ valgrind ps

This will display the ‘ps’ command output along with detailed report by valgrind.

Valgrind includes various tools which can be used for detecting different types of problems. These are:

  • memcheck: checks for memory leaks, accesses to uninitialized memory etc.
  • addrcheck: similar to memcheck but doesn't perform thorough memory checking, runs faster and uses less memory than memcheck.
  • cachegrind: cachegrind is a cache simulator.
  • massif: massif is a heap profiler.
  • lackey: lackey is a sample tool that can be used as a template for generating your own tools.

Tip: You can check out the Valgrind man page for more details and options.

To specify the tool to be used, enter the following command:
$ valgrind --tool=

For e.g. for using the memcheck tool on an application a.out, the command would be:
$ valgrind --tool=memcheck ./a.out


Suppressing errors

Valgrind, by default reports errors in all programs/libraries installed on your GNU/Linux system on which your application is dependent. Since, we are interested in our own application; we can easily suppress these errors by creating a .supp file and giving its path while running Valgrind. Valgrind reads this file at startup and suppresses errors having entry is in the .supp file.
$ valgrind --suppressions=./suppfile.supp

Tip: Details about the format of suppression files can be found in the Valgrind manual.


A simple example using Valgrind
Let’s explore the beauty of Valgrind using a simple example given below (Listing 1):

--------------------------------CODE-----------------------------

Listing 1:
#include
#include
int main()
{
char *p = (char *)malloc(20*sizeof(char));
strcpy(p, “linux rocks!”);
printf(“%s\n”, p);
return 0;
}

--------------------------------CODE-----------------------------

In this program, we have allocated a memory of 20 bytes but it is not freed before the program exits. Let’s analyze this program using Valgrind.

Compile this program using the following command:
$ gcc -g -o listing1 listing1.c

Run the program using valgrind:
$ valgrind --tool=memcheck ./listing1

This will display a brief memory leak summary about our program:

ERROR SUMMARY: 0 errors from 0 contexts (suppressed 18 from 1)
malloc/free: in use at exit: 20 bytes in 1 blocks.
malloc/free: 1 allocs, 0 frees, 20 bytes allocated.

LEAK SUMMARY:
definitely lost: 20 bytes in 1 blocks.
possibly lost: 0 bytes in 0 blocks.
still reachable: 0 bytes in 0 blocks.
suppressed: 0 bytes in 0 blocks.

The memory leak summary clearly displays that we have allocated 20 bytes using malloc() but we haven’t freed the memory after use. The above memory leak summary is very brief and doesn’t display the source-code where we have allocated the memory. For detailed memory-leak report, plug-in the following command:
$ valgrind --tool=memcheck --leak-check=full ./listing1

This command displays the following output:

ERROR SUMMARY: 0 errors from 0 contexts (suppressed 18 from 1)
malloc/free: in use at exit: 20 bytes in 1 blocks.
malloc/free: 1 allocs, 0 frees, 20 bytes allocated.

20 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x1B8FEA35: malloc (vg_replace_malloc.c:149)
by 0x8048375: main (listing1.c:4)

LEAK SUMMARY:
definitely lost: 20 bytes in 1 blocks.
possibly lost: 0 bytes in 0 blocks.
still reachable: 0 bytes in 0 blocks.
suppressed: 0 bytes in 0 blocks

From the above output we can conclude that 20 bytes of memory was allocated in listing1.c at line 4 but was not freed after use. So, the correct code would be:

--------------------------------CODE-----------------------------

Listing 2:
#include
#include
int main()
{
char *p = (char *)malloc(20*sizeof(char)); /* Allocate 20 bytes of memory */
strcpy(p, “linux rocks!”);
printf(“%s\n”, p);
free(p); /* Free the allocated memory */
return 0;
}

--------------------------------CODE-----------------------------

The Valgrind output for the above program is:

ERROR SUMMARY: 0 errors from 0 contexts (suppressed 18 from 1)
malloc/free: in use at exit: 0 bytes in 0 blocks.
malloc/free: 1 allocs, 1 frees, 20 bytes allocated.

The above output indicates that all the allocates memory has been freed and there are no memory leaks in the application.

You can also save the Valgrind output to a log file and view the memory leak details using a graphical front-end such as Alleyoop as shown in the following figure:

Figure: Memory Leak Usage using Alleyoop

So, by using Valgrind and various tools available with it, you can easily detect and fix memory leaks and other memory-related bugs in your applications.

Advantages of Valgrind
Valgrind currently supports all major GNU/Linux distributions on x86 architecture. Some of its benefits include:

  • Valgrind works directly with executables, so there is no need to modify, recompile or relink your applications.
  • Valgrind can be used to debug small as well as large applications.
  • Valgrind can be used with other tools like GDB (GNU Debugger), KDevelop (as a plug-in).
  • It can be used with almost any kind of software written in any language.
  • It can be used as a platform for writing new debugging tools.
  • Valgrind is free, open-source and available under GPL 2.
  • Several graphical front-ends are available for Valgrind, some of which include Alleyoop, Valgui, GNUGrind, KDevelop (with Valgrind as a plug-in)

Limitations of Valgrind
Every software has some limitations and Valgrind is no exception.

  • Application runs 25 to 50 times slower through Valgrind.
  • Memory consumption is increased while running the application through Valgrind.
  • Highly optimized code can sometimes cheat Valgrind.
  • Works only on GNU/Linux, x86 platforms.

Summary
Memory related bugs in software can be hard to detect and fix. Using Valgrind and various graphical front-ends available with it, you can easily detect and fix memory leaks in your application. Efficient utilization of memory, especially on embedded systems is a must and Valgrind allows you to achieve just that!

No comments: