Skip to main content

Investigating a coredump with GDB on linux

· 3 min read
Hreniuc Cristian-Alexandru

Some things were taken from this post, be sure to check it out.

Prepare pretty printers for GDB

The first thing you should always do is to get the pretty printers and use them to print the variables in gdb. To do this you will need to find the install path of thec gcc that was used to build the executable that has generated a coredump, and inside that path there should be some pretty printers. Usually, this path can be found here(on ubuntu): /usr/share/gcc-10/python/.

In my case it was in /opt/gcc/9.2.0/share/gcc-9.2.0/python/, because the gcc that I was using was built from sources.

That folder contains a folder called: libstdcxx which contains some python scripts that are used to print the variables nicer.

If you are investigating a coredump on a different server, you can copy that folder to the server using scp: scp -r /usr/share/gcc-10/python/ [email protected]:/path/to/server/.

Activate the pretty printers from the gdb config file

Pretty printers, added this in ~/.gdbinit:

import sys
# gcc-9
sys.path.insert(0, '/usr/share/gcc/python')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)

# Will print the whoile object
set pagination off
set print array on

# It will format the output
set print pretty on

Open the coredump using GDB

gdb executable coredump

Activate the pretty printers inside gdb console

Inside the gdb console you should paste the following code(after you change the path):

set detach-on-fork
set auto-load safe-path /
import sys
# !!CHANGE!!
sys.path.insert(0, '/PATH/TO/PRETTY/PRINTERS') # The one from above, ex: `/usr/share/gcc-10/python/`
from libstdcxx.v6.printers import register_libstdcxx_printers

This enables pretty printers, which means that this:

high_priority_msgs = {<std::_Vector_base<types::interf>, std::allocator<std::shared_ptr<types::interf> > >> = {
_M_impl = {
<std::allocator<std::shared_ptr<types::interf> >> = {<__gnu_cxx::new_allocator<std::shared_ptr<types::interf> >> = {<No data fields>},
<No data fields>},
<std::_Vector_base<std::shared_ptr<types::interf>, std::allocator<std::shared_ptr<types::interf> > >::_Vector_impl_data> = {
_M_start = 0x7f7498007290, _M_finish = 0x7f74980072e0, _M_end_of_storage = 0x7f74980072e0},
<No data fields>}}, <No data fields>}

Becomes this:

high_priority_msgs = std::vector of length 5, capacity 5 = {
std::shared_ptr<types::interf> (use count 1, weak count 0) = {get() = 0x7f7498006d60},
std::shared_ptr<types::interf> (use count 1, weak count 0) = {get() = 0x7f74980071a0},
std::shared_ptr<types::interf> (use count 1, weak count 0) = {get() = 0x7f749802c8b0},
std::shared_ptr<types::interf> (use count 1, weak count 0) = {get() = 0x7f7498022dd0},
std::shared_ptr<types::interf> (use count 2, weak count 0) = {get() = 0x7f749801c770}}

Investigating the coredump

Show the full backtrace:

bt full

Go to a specific frame:

frame 1

Print the args and the locals from that frame:

print args
print locals

Print a variable:

print high_priority_msgs

Tip 1: If an argument is optimized out, go one frame back, that arg might not be optimized in the previous frame.

Other tips can be found here