UPDATE: if you have new gdb (7.12.1), scroll to the bottom.
Ability to inline functions and methods in C++ is one of the reasons programs in C++ are so fast. As such code provided by libraries effectively becomes part of its client application and the great and mighty gdb knows what inline functions are, this might cause inconveniences during debugging. The problem is that while stepping into a method one can get into one of many inlined functions that aren’t interesting.
This issue is quite evident in case of template-based libraries like standard C++ library itself. Consider an example in C++11:
class Config { // ... void setRolesChain(std::vector<std::string> roles); // ... }; // ... cfg.setRolesChain({ "a", "b" }); // <-- current line // ...
step
command will take you to allocator constructor and then to one of
std::vector<std::string>
constructors (std::string
constructors are skipped,
not sure why). The way out is to repeat finish
and then step
commands until
you reach Config::setRolesChain()
, which can be boring and error prone (one
off breakpoints is a solution, but not very convenient one).
gdb doesn’t treat standard headers as anything special, so stepping into methods can be quite annoying.
The skip file
command
The good news is that there is skip file [<path>]
command
since gdb 7.4. It can also ignore functions
(skip function <function>
or skip <function>
). It’s easy to use skip
in
debug session, but what we want is to permanently ignore whole directory tree
(e.g. by pre-configuring it via ~/.gdbinit
), this unfortunately isn’t that
straightforward.
Limitations of the command
skip
doesn’t accept patterns. This means that one can’t easily ignore
everything system related. Moreover, files being ignored should be mentioned in
debug information of the inferior process, which doesn’t even exist when
~/.gdbinit
is executed. We’ll need a combination of several gdb features to
overcome such limitations.
Solution components
First, we need command post hooks for run
, start
and attach
commands. The hooks are executed after gdb command and can do anything you
want.
Second, we need list of files to skip. At first, I generated them via
find /usr/include/c++/5.3.0 -type f
, but it’s not extensible. So the list
will be present, but it’s better to generate it dynamically.
Third, as gdb can be extended via python, we’ll use it to make the list and
instruct gdb to skip those files. Here, using python bindings allows us to
easily ignore error output of skip file
command (to_string
parameter), which
prevents hundreds of useless output cluttering our screen (two lines for each
header unused by process being debugged). Mind that because python is
indentation based (bad idea) you can’t indent its code relative to other
commands properly. By the way, gdb can also be extended via
Guile, which would be more native, but I’m barely familiar with
Lisp dialects.
Solution
Putting it all together we get this simple solution ready to be inserted into
~/.gdbinit
(path to headers might need correction in some cases):
define skipstdcxxheaders python def skipAllIn(root): import os for root, dirs, files in os.walk(root, topdown=False): for name in files: path = os.path.join(root, name) gdb.execute('skip file %s' % path, to_string=True) # do this for C++ only if 'c++' in gdb.execute('show language', to_string=True): skipAllIn('/usr/include/c++') end end define hookpost-run skipstdcxxheaders end define hookpost-start skipstdcxxheaders end define hookpost-attach skipstdcxxheaders end
Troubleshooting
(Derived from discussion with Dhiraj Reddy in comments.)
info skip
can be used too see what’s being ignored.
Language check might not work in IDEs, so one might need to replace
if 'c++' in gdb.execute('show language', to_string=True): skipAllIn('/usr/include/c++')
with
skipAllIn('/usr/include/c++')
Solution for new GDB (7.12.1)
(See comments.)
Something like this (didn’t try it myself) in ~/.gdbinit
should do:
skip -gfi /usr/include/c++/*/*/* skip -gfi /usr/include/c++/*/* skip -gfi /usr/include/c++/*