Debugging C++ templates is difficult. Debugging C++ templates with GDB can be an act of torture for even seasoned GDB users. I like GDB, but there are some tricks you should know when using it to debug templates. In this post, I deal with setting breakpoints.
Setting a breakpoint in GDB is supposed to be simple. Here we set a breakpoint at line 50 in file main.cpp:
(gdb) b main.cpp:50
Breakpoint 1 at 0x804937a: file main.cpp, line 50.
We can also use the function name and GDB will attempt to find the correct location for us:
(gdb) b DoSomething
Breakpoint 2 at 0×8049334: file main.cpp, line 150
Simple, right? Just wait…
GDB’s breakpoint logic is pretty handy for simple projects, but it can break down fast when things get more complicated.
For example, let’s say your application is plugin-driven, with each plugin being a separate library. Now assume each plugin has a Plugin.cpp file under it’s own Source directory. Try to set a breakpoint in the Initialize() method of the Plugin class:
(gdb) b Initialize
Breakpoint 3 at 0×8049717: file main.cpp, line 230
Oops! There is an Initialize() method in main.cpp and GDB thought that’s where we wanted to put it: wrong!
Okay, let’s be more specific:
(gbd) b Plugin::Initialize
Breakpoint 4 at 0×8046194: file Source/Plugin.cpp, line 89
Okay, that looks better, but which Plugin.cpp file did GDB put the breakpoint? How do we know it’s the file for the plugin we want?
If we used namespaces, then we can get more specific:
(gdb) b MY_PLUGIN_A::Plugin::Initialize
Breakpoint 5 at 0×8050039: file Source/Plugin.cpp, line 130
We can see from the address and line number that the previous breakpoint was at the wrong place. Okay, moving on…
Setting Breakpoint in Templates:
Templates can be much harder to set breakpoints in because we have to specify the exact prototype for the fully-defined template. We as programmers are used to the compiler handling the template type stuff for us, so it can be difficult to guess the correct type.
For example, assume we have some abstract class BarAbstract that uses the template Foo<> to make it concrete. Now assume we’re really clever and we want to hide this from the users of our class. We could use a typedef to hide the true type:
typedef Foo<BarAbstract> Bar;
Now, all the user needs to do is instantiate Bar without a thought to the Foo<> template.
So far so good? Okay, now how the heck do you set a breakpoint in the Baz() method of class Bar?
The quick answer: use objdump, c++filt, and grep to find the complete definition that GDB will need.
$ objdump -t libMyLib.so | c++filt | grep ‘BarAbstract.*Baz’
0000d2d6 w F .text 0000000a MY_PLUGIN_A::Foo<MY_PLUGIN_A::BarAbstract>::Baz()
Now, simply copy-n-paste the full method definition to GDB when setting the breakpoint:
(gdb) b MY_PLUGIN_A::Foo<MY_PLUGIN_A::BarAbstract>::Baz()
Breakpoint 6 at 0×8048890: file Source/Bar.cpp, line 355
That’s it! Happy debugging!