Add functions in gdb at runtime

What you are asking for is not directly possible as far as I know. There's a close alternative though (who said that about one more level of indirection? :)

Build a separate dynamic library with all your printer routines, then add lazy load print wrappers to your program. By that I mean something along the lines:

/// this is in your program, lazy print wrapper
void print_map( const std::map<int,int>& m ) // NOTE THE CONST REFERENCE
{
    static bool loaded = false;
    static void* print_lib = 0;
    static void (*print_func_ptr)( const std::map<int,int>& ) = 0;

    if ( !loaded )
    {
        // dlopen dynamic lib, check for errors, assign to print_lib
        // dlsym the library function by name, assign to print_func_ptr
        loaded = true;
    }

    print_func_ptr( m );
}

Then you can call print_map in the gdb session and the library would load automagically. Note that code above accepts the map by const reference. The function you put in the question would make a copy of its argument.

Also take a look here for some ways to make gdb produce better output for STL containers.


I'd suggest taking a look here: http://sourceware.org/gdb/wiki/STLSupport

There's a couple of different ways of displaying STL containers (and the leg work has already been done for you). Any option will require you to restart gdb after you configure it, but it'll be good to go for later use.


So my solution is to load a shared object containing my debugging routines at run time, using dlopen. Turns out it is even simpler than I thought when you get all the compile flags right.

On OS X this means you compile your application and debugging object like this:

all : application.x debug_helper.so

application.x : application.cpp
    g++ -g application.cpp -o application.x -fPIC

debug_helper.so : debug_helper.o
    g++ -dynamiclib -o debug_helper.so debug_helper.o

debug_helper.o : debug_helper.cpp
    g++ -Wall -g -fPIC -c debug_helper.cpp

The -fPIC on the application is critical, as is the -dynamiclib (rather than trying the linux -shared flag )

An example debug_helper.cpp might look like this

#include <map>
#include <stdio.h>

extern "C" 
void printMap( const std::map<int,int> &m )
{
  printf("Map of size %d\n", int(m.size()) );
  for( std::map<int,int>::const_iterator it = m.begin(); it!=m.end(); ++it )
  {
    printf("%d : %d \n", it->first, it->second );
  }
  fflush(stdout);
}

Don't know why I chose to use stdio rather than iostream stuff... I guess you can use either. (just don't forget to flush the streams...)

Now my application file looks like this:

#include <map>

int main()
{
  std::map<int,int> m;
  m[1]=2;
  m[2]=5;
  m[3]=10;
  m[4]=17;
}

And here's an example debugging session (some output removed)

Start the application and break at an interesting point

(gdb) break main
(gdb) run
Reading symbols for shared libraries +++. done   
Breakpoint 1, main () at test.cpp:5
5     std::map<int,int> m;

Load in the debug helper library

(gdb) print (void*) dlopen("./debug_helper.so",2)
Reading symbols for shared libraries . done
$1 = (void *) 0x100270
(gdb) n
6     m[1]=2;

GDB is smart and catches all the new symbols for us so we don't need to use dlsym etc. We can just call the functions directly.

(gdb) call printMap(m)
Map of size 0
(gdb) n
(gdb) n
(gdb) n
9     m[4]=17;
(gdb) call printMap(m)
Map of size 3
1 : 2 
2 : 5 
3 : 10 

Let's add some more info to printMap. First, unload the library.

(gdb) print (int) dlclose($1)
$2 = 0

Edit the source to add in the sum of the entries. Recompile and then load the new library back into gdb (without restarting the executable or gdb )

(gdb) print (void*) dlopen("./debug_helper.so",2)
Reading symbols for shared libraries . done
$3 = (void *) 0x100270

Use the modified function

(gdb) call printMap(m)
Map of size 3
1 : 2 
2 : 5 
3 : 10 
SUM = 17

I think this does everything that I need.

Tags:

C++

Gdb