Skip to content

Valgrind and Seer

Ernie Pasveer edited this page Nov 7, 2023 · 2 revisions

Introduction

Valgrind is a great debugging tool for programmers to find (and fix) memory related errors in their code.

At one time, valgrind had a --db-attach option to launch a gdb session at the first error encountered when running the target program. This is now deprecated in favor of valgrind's version of a gdbserver called vdbg.

Seer works well with this. This wiki describes how to do that.

You will need:

  • Seer installed
  • valgrind and it's vdbg program
  • And your program to run valgrind on

For the purpose of this wiki, the below program will be used. It has two memory errors.

  1. A 'out-of-bounds' error.
  2. A memory leak.

Here's the example code. (hellovalgrind.c)

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

// https://developers.redhat.com/articles/2021/11/01/debug-memory-errors-valgrind-and-gdb#using_valgrind_and_gdb_together

typedef struct foo {
    int flag1;
    int flag2;
    int size;
    int **buf;
} foo;

void print_buf(struct foo *s)
{

   printf("buf[0][0]: %d\n", s->buf[0][0]);
   free(s->buf);
}

void setup_foo(struct foo *s)
{
    s->flag2 = 2;
    s->buf = malloc(20 * sizeof(int));
    for (int i = 0; i < 20; i++)
        s->buf[i] = malloc(20 * sizeof(int));
}

int main(void)
{
   struct foo s;

   setup_foo(&s);
   print_buf(&s);

   if (s.flag1 || s.flag2)
       printf("hey\n");

   return 0;
}

Compile it with:

$ gcc -g -o hellovalgrind hellovalgrind.c
$ ls hellovalgrind
hellovalgrind

When you run it, it will crash with the 'out-of-bounds' error.

$ ./hellovalgrind 
buf[0][0]: 32691440
free(): invalid pointer
Aborted (core dumped)

Running it with valgrind shows more details.

$ valgrind ./hellovalgrind 
==12313== Memcheck, a memory error detector
==12313== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12313== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==12313== Command: ./hellovalgrind
==12313== 
==12313== Invalid write of size 8
==12313==    at 0x400675: setup_foo (hellovalgrind.c:25)
==12313==    by 0x40069E: main (hellovalgrind.c:32)
==12313==  Address 0x523a090 is 0 bytes after a block of size 80 alloc'd
==12313==    at 0x4C346A4: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12313==    by 0x400640: setup_foo (hellovalgrind.c:23)
==12313==    by 0x40069E: main (hellovalgrind.c:32)
==12313== 
buf[0][0]: 86223600
==12313== Conditional jump or move depends on uninitialised value(s)
==12313==    at 0x4006B0: main (hellovalgrind.c:35)
==12313== 
hey
==12313== 
==12313== HEAP SUMMARY:
==12313==     in use at exit: 1,600 bytes in 20 blocks
==12313==   total heap usage: 22 allocs, 2 frees, 2,704 bytes allocated
==12313== 
==12313== LEAK SUMMARY:
==12313==    definitely lost: 1,440 bytes in 18 blocks
==12313==    indirectly lost: 160 bytes in 2 blocks
==12313==      possibly lost: 0 bytes in 0 blocks
==12313==    still reachable: 0 bytes in 0 blocks
==12313==         suppressed: 0 bytes in 0 blocks
==12313== Rerun with --leak-check=full to see details of leaked memory
==12313== 
==12313== Use --track-origins=yes to see where uninitialised values come from
==12313== For lists of detected and suppressed errors, rerun with: -s
==12313== ERROR SUMMARY: 9 errors from 2 contexts (suppressed: 0 from 0)

Using Valgrind and Seer

This shows how to launch Seer when valgrind detects an error.

In one terminal, launch your program using valgrind's vdbg program. There are many options with vdbg, read up on them for a better understanding. The most important option is the --vgdb-error option. This tells vdbg when to initiate communication with the debugger. A value of 0 means to communicate right away. Any other value means to initiate after that many errors.

$ valgrind -q --vgdb-error=0 ./hellovalgrind 
==12596== (action at startup) vgdb me ... 
==12596== 
==12596== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==12596==   /path/to/gdb ./hellovalgrind
==12596== and then give GDB the following command
==12596==   target remote | /usr/lib/valgrind/../../bin/vgdb --pid=12596
==12596== --pid is optional if only one valgrind process is running
==12596== 

In another terminal, start Seer using the --connect method from the command line.

$ seergdb --connect '| vgdb' --sym hellovalgrind

Or the connect method with the Seer Debug dialog. The important part is the | vdbg

image

Connected

Once vdbg and Seer are connected, vdbg actually stops you just before main().

image

This will give you time to set breakpoints. Or you can just hit Continue. Then valgrind will wake up Seer (with a SIGTRAP) and Seer will show the source and line number of the error.

image

Start debugging!!! If you Step, Next, or Continue, valgrind will look for new errors and will wake up Seer with a SIGTRAP.

vdbg monitor mode.

vdbg provides the gdb debugger a monitor mode that can be interacted with. Seer supports this. The full help for the vdbg monitor is here.

Once vdbg and Seer are connected, the vdbg monitor commands can be entered in Seer's Manual Command entry line.

image

And all output from the monitor is sent to the GDB output tab.

image

It is recommended to detach the GDB output tab so it is in its separate window. RMB on the tab to bring up the detach menu.

image

image

A common valgrind operation is to check for memory leaks. Set a breakpoint on the line just before the main() exits. When the program reaches the breakpoint, enter this command in the Manual Command entry line:

monitor leak_check

It will tell vdbg to report any new leaks in the GDB ouput tab.

image

image

Additional links.

https://www.redhat.com/en/blog/valgrind-and-gdb-close-cooperation

Feedback.

Send me email or create a task for errors/suggestions/etc.