Easy things to do with GDB #1
I’ve been meaning to write some introductory articles to GDB ever since back in February at FOSDEM, where I was surprised by just how many people do not use GDB–or even know what it is! It’s taken me a while to figure out a nice example, but I finally found them.
Last week I wanted to extend ogg123
, a tool for playing music files on the commandline. It supports several formats–Ogg Vorbis, FLAC and Speex–and the way it abstracts that is by passing around a struct format_t
full of function pointers to various callbacks. I was adding a new format, and wanted to know what kind of arguments it was calling its callbacks with. I could have tried to figure it out by reading the code, but it was easier to just run it in GDB and see what was happening. Here’s how:
- Build a copy of ogg123 with debugging support:
wget http://downloads.xiph.org/releases/vorbis/vorbis-tools-1.4.0.tar.gz tar xf vorbis-tools-1.4.0.tar.gz cd vorbis-tools-1.4.0 CFLAGS="-g -O0" ./configure make
The important bit here is the
CFLAGS="-g -O0"
. This causesconfigure
to pass these extra options to GCC in the makefiles it generates.-g
instructs GCC to include debugging information in the files it generates to allow GDB to relate the executable files you’re debugging to the source files they were generated from.-O0
instructs GCC not to optimise the code, which makes for easier debugging. - Start up GDB on the
ogg123
you just built:gdb -args ogg123/ogg123 ~/music/Various/Disco\ Demands/4-08\ -\ Give\ It\ Up.flac
Everything after the
-args
option is the command you’d normally type to run the program. GDB will print out some stuff as it starts up, then present you with a(gdb)
prompt and wait for you to enter some commands:GNU gdb (GDB) 7.4.50.20120313-cvs Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-unknown-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /home/gary/vorbis-tools/ogg123/ogg123...done. (gdb) >
- Ok, the callback I’m interested in is the “read” callback, and we’re playing a FLAC file so the callback is called
flac_read
. I’d like to run the program untilflac_read
is called, so I set a breakpoint onflac_read
by enteringbreak flac_read
and then using therun
command to setogg123
going:(gdb) break flac_read Breakpoint 1 at 0x40dde5: file flac_format.c, line 253. (gdb) run Starting program: /home/gary/vorbis-tools/ogg123/ogg123 /home/gary/music/Various/Disco\ Demands/4-08\ -\ Give\ It\ Up.flac [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". [New Thread 0x7fffeb863700 (LWP 10312)] [Thread 0x7fffeb863700 (LWP 10312) exited] Audio Device: PulseAudio Output Playing: /home/gary/music/Various/Disco Demands/4-08 - Give It Up.flac Breakpoint 1, flac_read (decoder=0x637560, ptr=0x6182a0, nbytes=30240, eos=0x7fffffffe37c, audio_fmt=0x7fffffffe380) at flac_format.c:253 253 flac_private_t *priv = decoder->private; (gdb)
- GDB now has a running copy of
ogg123
stopped at the very start offlac_read
. Pretty jazzy huh? You can see the values of the arguments in the second-to-last line it printed, so for example you can see that the caller has requested to read 30240 bytes of data. The various structures (decoder=0x637560
etc) aren’t so useful as they’re pointers, but we can deference them with theprint
command:(gdb) print *decoder $1 = {source = 0x637060, request_fmt = {big_endian = 0, word_size = 2, signed_sample = 1, rate = 0, channels = 0, matrix = 0x0}, actual_fmt = { big_endian = 0, word_size = 2, signed_sample = 1, rate = 0, channels = 0, matrix = 0x0}, format = 0x617ec0, callbacks = 0x7fffffffe3a0, callback_arg = 0x0, private = 0x6375d0}
That’s pretty nice, we can see for example what format it wants the data in: little endian, signed, etc. Also, notice the line of code it stopped on:
253 flac_private_t *priv = decoder->private;
That’s the next line of code GDB will execute if we set the program going again in some way. We can advance over just that line with the
next
command:(gdb) next 254 decoder_callbacks_t *cb = decoder->callbacks;
Now it’s initialised the local variable
priv
, which we can also print out:(gdb) p *priv $2 = {decoder = 0x637000, is_oggflac = 0, channels = 2, rate = 44100, bits_per_sample = 16, totalsamples = 13337016, currentsample = 0, samples_decoded = 4096, samples_decoded_previous = 0, bytes_read = 24576, bytes_read_previous = 0, comments = 0x638240, bos = 1, eos = 0, buf = 0x637020, buf_len = 4096, buf_start = 0, buf_fill = 4096, stats = { total_time = 0, current_time = 0, instant_bitrate = 0, avg_bitrate = 0}}
Did you see what I did there? Most GDB commands have abbreviated forms, and the abbreviation for
print
isp
. The commands I’ve introduced in this article are some of the most commonly used, and their abbreviated forms are all simply their first letter:b
forbreakpoint
,r
forrun
andn
fornext
. You can also repeat the previous command by pressing Return, which is handy for doing a load ofnext
commands one after the other, for instance.
Ok, that’s all for today. Go and have a play!