Debugging LIBPF applications with gdb
GNU debugger (gdb) is the standard command-line debugger on many Unix-like systems for troubleshooting C++ programs.
To prepare for debugging your application, compile it with debugging symbols enabled; for example assuming you want to debug Qpepper and use bjam to build:
cd ~/LIBPF/pepper
bjam debug Qpepper
or if you use qmake/make to build:
cd ~/LIBPF/pepper
qmake
make debug
A typical debugging session starts by launching gdb with the relative path to the executable as a parameter:
cd ~/LIBPF/bin
gdb ./pepper/gcc-4.9.2/debug/Qpepper
Next we typically want to set up a breakpoint at the Error::Error function, which is where the control flow will pass if an exception is thrown; to do that, use the b (breakpoint) command:
b Error::Error
Then you launch your application with the required command-line parameters with the r (run) command:
r new jjj
When the exception is thrown, the debugger will stop at the breakpoint:
Breakpoint 1, Error::Error (this=0xed2080,
cf=0xa03dc0 "Node* NodeFactory::create(std::string, Libpf::User::Defaults, uint32_t, Persistency*, Persistent*, Persistent*)") at ../utility/src/Error.cc:56
56 Error::Error(const char *cf) : msg_("Error was thrown by function: ") {
From here you can:
-
examine the call stack with the where command, which will return something like:
#0 Error::Error (this=0xed2080, cf=0xa03dc0 "Node* NodeFactory::create(std::string, Libpf::User::Defaults, uint32_t, Persistency*, Persistent*, Persistent*)") at ../utility/src/Error.cc:56 #1 0x00000000006097b2 in ErrorObjectFactory::ErrorObjectFactory (this=0xed2080, cf=0xa03dc0 "Node* NodeFactory::create(std::string, Libpf::User::Defaults, uint32_t, Persistency*, Persistent*, Persistent*)", ty=0xed09e8 "type jjj not found") at ../utility/src/Error.cc:117 #2 0x00000000007d30c1 in NodeFactory::create (this=0x7fffffffd7ef, type="jjj", defaults=..., id=0, persistency=0x0, parent=0x0, root=0x0) at src/NodeFactory.cc:57 #3 0x00000000004263ec in createCase_ (type="jjj", defaults=..., error=@0x7fffffffdffc: 32767, svgs=true) at src/Kernel.cc:228 #4 0x0000000000427901 in Libpf::User::createCase (type="jjj", tag="jjj", description="", jcd="", error=@0x7fffffffdffc: 32767) at src/Kernel.cc:317 #5 0x000000000040e64d in main (argc=3, argv=0x7fffffffe158) at ../user/src/main.cc:189
notice the first column that is the frame number, and the error message details found as ty parameter to the function call in frame #1: type jjj not found
-
jump to the frame that occurred in your own code and not in the library, such as frame #5, using the f (frame) command:
f 5
-
list the source code around the current execution point with the l (list) command, which will return something like:
189 Libpf::User::Handle caseHandle = Libpf::User::createCase(type, tag, description, options, error); (gdb) l 184 std::string options(""); 185 if (argc > 5) { 186 options = argv[5]; 187 } // if options are passed 188 189 Libpf::User::Handle caseHandle = Libpf::User::createCase(type, tag, description, options, error); 190 if (error < 0) 191 quitNow(error); 192 else 193 quitNow(caseHandle.id()); (gdb)
Issuing the same commands repeatedly at the gdb command prompt is common, therefore it’s handy to enable gdb command history:
cat >> ~/.gdbinit
set history save
set history filename ~/.gdb_history
^d
For more debugging tips, check the excellent RMS gdb tutorial or the gdb manual.