tags:

views:

123

answers:

3

I'm trying to debug some STL based C++ code in gdb. The code has something like

int myfunc()
{
   std::map<int,int> m;
   ...
}

Now in gdb, inside myfunc using "print m" gives something very ugly. What I've seen recommended is compiling something like

void printmap( std::map<int,int> m )
{
   for( std::map<int,int>::iterator it = ... )
   {
      printf("%d : %d", it->first, it->second );
   }
}

Then in gdb doing

(gdb) call printmap( m )

This seems like a good way to handle the issue... but can I put printmap into a seperate object file (or even dynamic library) that I then load into gdb at runtime rather than compiling it into my binary - as recompiling the binary every time I want to look at another STL variable is not fun .. while compiling and loading a single .o file for the print routine may be acceptable.


UPDATE:

Prompted by Nikolais suggestion I'm looking at dlopen/dlsym.

So I haven't yet got this working but it feels like I'm getting closer.

In printit.cpp

#include <stdio.h>

extern "C" void printit()
{
  printf("OMG Fuzzies");
}

Compile to a .so using

g++ -Wall -g -fPIC -c printit.cpp
g++ -shared -Wl,-undefined,dynamic_lookup -o printit.so printit.o

Start my test application and load the .so using dlopen ( 2 = RTLD_NOW ) then try to get the symbol for a debugging function using dlsym.

(gdb) break main
(gdb) run
(gdb) print (void*) dlopen("printit.so", 2 )
$1 = (void *) 0x100270
(gdb) print (void*) dlsym( 0x100270, "_printit" )
$2 = (void *) 0x0

So close but for some reason I cant get that symbol... ( I cant even get it if I put the dlopen/dlsym calls in my executable) I'm guessing I'm either compiling the lib wrong or using dlsym incorrectly.

If I can get the symbol I'm assuming I can call the function using something like

(gdb) print (( void(*)() )(0x....))()

I'm compiling this on OS X 10.4, which might be causing some of my .so woes... any pointers would be appreciated.


Found out how to get all this working. Have posted as a solution below.

+2  A: 
Nikolai N Fetissov
Yeah I didn't really care whether the call copied the arguments or not. But this solution is still impractical for me since if I want to look at another STL class I need to recompile my binary and restart gdb. However you may be onto something with using dlopen. I will investigate further.
Michael Anderson
Oh, you wanted a solution *now*, without even leaving the gdb console? Kids these days ... :)
Nikolai N Fetissov
+1  A: 

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.

Nathan Ernst
+2  A: 

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 heres 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 dont 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 

Lets 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

Use the modified function

$3 = (void *) 0x100270
(gdb) call printMap(m)
Map of size 3
1 : 2 
2 : 5 
3 : 10 
SUM = 17

I think this does everything that I need.

Michael Anderson