Przejdź do głównej zawartości

GDB - beyond basics

GDB is console debugger that every Linux-using programmer heard about. It is not however easy to learn. Greg Law in his CppCon talk presented some of obscure, but useful features.

Text user interface

Normally we use GDB with command line interface (CI). Beyond this, GDB has TUI based on Curses library. To activate it, use keyboard shortcut ctrl-x-a (hold ctrl, press x, unpress x, press a). Now you can see the code as you go through it.
  • ctrl-x-a - activate/deactivate TUI
  • ctrl-l - when screen gets messed up, use it to redraw. Happens when program prints to stdout/stderr
  • ctrl-p / ctrl-n - since you can't use arrows to reuse previously written command, use ctr-p/n instead of arrow up/down
    ctrl-f / ctrl-n are arrows left / right
    ctrl-a / ctrl-e are home / end (all those are copied from Emacs)
  • ctrl-x-2 - second window (assembly).

Shell

You can run shell commands inside GDB command line. Just use keyword "shell" at the beginning. Examples:
  • shell ps
  • shell cat temporary_file.txt
  • shell killall child_process

Python

GDB has built-in Python interpreter. You can use it like shell with keyword "python" and one-liner python commands. Alternatively write "python", enter python commands in multiple lines, write end to run them. Of course you can source python scripts written outside gdb.
Beyond basics, python has gdb module to import. With this, you can set breakpoints from python code, define custom pretty printers for structs and other things.
You can read module documentation here.

Pretty print

When printing (command 'p') contents of variables, output can be ugly. You can improve it with command "set print pretty on" which improves matters considerably. 
Alternatively you can implement pretty robust printers in Python. Sadly this is non-trivial, so for reference I suggest watching the video (starting from time 30:50)

Backward debugging

Using GDB normally we move forward with program execution. However resolving bugs usually centers around question "how did I get here?", not "what happens next?". We can tell GDB to record changes in state of program, so that after breakpoint is hit (or segmentation fault was triggered, or whatever), we can go back in time to see what caused this situation.
Basic workflow is:
  1. Start program under gdb
  2. Set up breakpoints
  3. record
  4. continue/run
  5. wait for breakpoint to stop execution
  6. reverse-step
  7. diagnose
Other commands for reverse flow are documented here.

Debugging stack overflow

Greg shown interesting and more advanced use case. You have a program that sometimes segfaults, but not on every run. In fact it is quite rare. Reading dump file (.core) does not help, because stack got overwritten and we don't know what place in the program caused the crash. Greg wanted to catch the crash "in action" with recording on, without much manual labor. He told GDB to start recording when program starts and to restart the program when it ends normally. Commands to achieve it are:
  1. break main   # creates breakpoint 2
  2. break _exit   # creates breakpoint 3
  3. command 2
    • record
    • continue
    • end
  4. commad 3
    • run
    • end
  5. run
  6. wait for crash to stop execution, diagnose (see video for details)

Cost of recording

Of course this feature costs, and GDB works much slower when recording. According to Greg, this slowdown is about 50'000 times slower. Therefore use recording with caution - set up breakpoint as close to crash as possible, run to it and only then start recording.
There is a feature in some new processors where you can use "record btrace". Slowdown is then limited to about 100 times slower, but all you can see is history of program counter (backtrace), and not program state (contents of variables).

.gdbinit

There is configuration file for gdb! Its location is ~/.gdbinit and possible contents is:
set history save on
set print pretty on
set pagination off
set confirm off

Other

Sadly Greg run out of time. Other topics that he wanted to mention were:
  • remote debugging - you can debug programs running on remote machines. For details google gdbserver
  • multiprocess debugging - when your program forks, you can tell gdb whether to follow child or parent
  • non-stop mode - by default when one thread hits breakpoint and you are at the prompt, all other threads pause. Non-stop mode allows them to continue ("set non-stop on" / "continue -a")
  • watchpoints - with "watch foo" you can set breakpoint that triggers when foo is modified. Other possibilities: when foo is read ("rwatch foo"), is modified by thread 3 ("watch foo thread 3"), when foo is changed and new value meets some criteria ("watch foo if foo > 10") 
  • hardware/software watchpoints - hardware ones work in silicon, are much faster, but are not always possible

Komentarze

Popularne posty z tego bloga

C++11 random - introduction

C++11 introduced robust random numbers library, <random>. Its author does great introduction in the video from Cppcon:
I will attempt to create short tutorial/HOWTO for this library.
Bad, old times Before C++11, when you wanted to get random numbers in range {1, 2, ... 10}, you wrote something like this:
#include <stdio.h>      /* printf, NULL */ #include <stdlib.h>     /* srand, rand */ #include <time.h>       /* time */ int main () { srand (time(NULL)); int num1 = rand()%10 + 1; int num2 = rand()%10 + 1; return 0; } Several things are wrong here:
It always gives uniform distribution (1 is as probable as 5). This is usually fine, but when not, you need a lot of additional work.Actually, it is not very uniform.If you want for some reason to reseed the generator (call srand(time(NULL)) again) shortly after first seeding, you may get repeated answersYou only get discreet numbers (integers)Random algorithm is not specified, so results are not portab…

<chrono> introduction

This article will summarize presentation by Howard Hinnant which he gave during CppCon 2016. Howard gives comprehensive walk through c++11 chrono library.



TL;DR (Summary) C++11 introduced <chrono> library for handling time. Use it instead of <time.h> / <ctime> because it gives you better type safety and precision.
Library provides those concepts:
time points - moments in time (as in "noon, December 1st 2000", "2016-01-31 14:15:32")time durations - "length" (amount) of time that passes between two time points ("two hours", "2563 milliseconds")clocks - providers of time points (like wall clock, stopper) Query clock to get time points; calculate difference between two time points to get time duration. You can use time points/duration with <thread> library for sleep, wait_for/wait_until functions.
Use auto, because otherwise type names are quite verbose.
Time Durationstd::time_duration is a measure of how much time…