Adding functions to gdb at runtime

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

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

Now in gdb inside myfunc using "print m" it turns out something very ugly. What I saw is recommended to compile 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 do

 (gdb) call printmap( m ) 

This seems like a good way to handle the problem ... but I can put printmap in a separate object file (or even a dynamic library), which I then load into gdb at runtime, rather than compiling it into my binary - as recompiling binary each the time I want to look at another STL variable is not fun .. when compiling and downloading one .o file for the printing procedure may be acceptable.


UPDATE:

Prompted by the proposal of Nicholas. I am looking at dlopen / dlsym.

So I have not got this job yet, but it seems to me that I'm getting closer.

In printit.cpp

 #include <stdio.h> extern "C" void printit() { printf("OMG Fuzzies"); } 

Compile .so using

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

Launch my test application and download .so using dlopen (2 = RTLD_NOW), then try to get the symbol for the 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 can't get this character ... (I can't even get it if I put dlopen / dlsym in my executable) I assume that I either combined lib or used dlsym incorrectly .

If I can get the character that I assume, I can call the function using something like

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

I am compiling this on OS X 10.4, which may cause some of my .so problems ... any pointers would be appreciated.


Learned how to make it all work. Published as a solution below.

+7
c ++ gdb
source share
3 answers

So my solution is to load a shared resource containing my debugging routines at runtime using dlopen. Turns out this is even easier than I thought when you get all the flag compilations.

In OS X, this means that you compile the application and debug it as follows:

 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 value for the application is critical, as is the -dynamiclib (instead of 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); } 

I don’t know why I decided to use stdio and not iostream. I think you can use it too. (just remember to clear the threads ...)

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 of debugging a session (some output removed)

Launch the app 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; 

Download to 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 new characters for us, so we don’t need to use dlsym etc. We can just call 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 

Allows you to add additional information to printMap. First unload the library.

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

Edit the source you want to add to the sum of the records. Recompile and 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 I need.

+6
source share

What you are asking is not directly possible, as far as I know. There is a close alternative there (who said another level of indirection? :)

Create a separate dynamic library with all your printer routines, and then add lazy load to your print wrapper program. By this I mean something line by line:

 /// 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 will load automatically. Note that the code above accepts the map using the const link . The function that you asked in the question will make a copy of its argument.

Also look here for some ways to make gdb a better way out for STL containers.

+2
source share

I would suggest looking here: http://sourceware.org/gdb/wiki/STLSupport

There are several different ways to display STL containers (and footwork has already been done for you). Any option will require a reboot of gdb after its configuration, but it will be useful to continue using it.

+1
source share

All Articles