debugging
#define debugging: \
I-----------------------------------------------------\
I ______ _ _ \
I | _ \ | | (_) \
I | | | |___| |__ _ _ __ _ __ _ _ _ __ __ _ \
I | | | / _ \ '_ \| | | |/ _` |/ _` | | '_ \ / _` | \
I | |/ / __/ |_) | |_| | (_| | (_| | | | | | (_| | \
I |___/ \___|_.__/ \__,_|\__, |\__, |_|_| |_|\__, | \
I __/ | __/ | __/ | \
I |___/ |___/ |___/ \
I \
I-----------------------------------------------------I
# conventional bug-fixing celebration music
https://www.youtube.com/watch?v=h3_YnfTuocI
" \
Debugging is twice as hard as writing the code in the first place. \
Therefore, if you write the code as cleverly as possible, \
you are, by definition, not smart enough to debug it. \
" - Kernighan
"If it crashes, its as good as a break point, its another kind of breakpoint."
— Sam Bowme, CNIT 127
Debugger:
• a program used for interactively finding bugs in other programs
• forks/attaches to and oversees the execution of the program being debugged
— common responsibilities:
• suspending execution at breakpoints
• inspecting variables
• changing variables
• inspecting memory ranges
DWARF:
• embedded debugging metadata format
• used in most languages which compile to machine code
• parsed by debuggers
• describes how the source code corresponds to the machine code
{source files; function barriers; individual lines}
• has a reputation of being hard to work with (developer side)
Sections:
.debug_abbrev : abbreviations used in the .debug_info section
.debug_aranges : a mapping between memory address and compilation
.debug_frame : call Frame Information
.debug_info : the core DWARF data containing DWARF Information Entries (DIEs)
.debug_line : line Number Program
.debug_loc : location descriptions
.debug_macinfo : macro descriptions
.debug_pubnames : a lookup table for global objects and functions
.debug_pubtypes : a lookup table for global types
.debug_ranges : address ranges referenced by DIEs
.debug_str : string table used by .debug_info
.debug_types : type descriptions
-------------
gprof: gprof:
-------------
• execution profiling tool
• requires the "-pg" gcc flag
• executables compiled with "-pg" will generate a gmon.out during their execution
• it will create nice, informative graph data for you
○ for
C
Pascal
Fortran
gprof <options> <executable>
— a : silence private (static in C)
— b : do not add verbose legend comments
Programs:
gprof2dot : python helper program to convert the gprof
call graph format to graphviz's dot format
$ gprof2dot gprof.txt > mygraph; dot -Tpng -o callgraph.png mygraph
------------
perf: perf:
------------
• "PERFormance analisys tool"
perf <verb> <executable>
stat
----------------
valgrind: valgrind:
----------------
https://valgrind.org/
• standard utility to find any and all memory errors
• executes programs in a virtualized envirnment so that
their behaviour may be analized in detail
• often used interchangably in conversation to refer to its memcheck tool,
which is the default and most used, primarily by C/C++ devs
valgrind [options] <executable>
--tool=<tool>
gdb
#define gdb:: \
I-------------------\
I ___ ___ ___ \
I / __| \| _ ) \
I | (_ | |) | _ \ \
I \___|___/|___/ \
I-------------------I
• "Gnu DeBugger"
Supports: Ada, Assembly, C, C++, D, Fortran, Go, Objective-C, OpenCL, Modula-2, Pascal, Rust
Programs:
gdb ([executable])
--args [filename] [args...] : run program with arguments
— p [PID] : attaches gdb to already running process;
use this option when working with Ncurses ffs!
--tui : use pseudo graphical interface; its great, use it ffs
Execution_flow:
• a _breakpoint_ is an instruction at which, when executed, the program stops
• a _watchpoint_ or _data_watchpoint_ is an expression which, when changed, the program stops
Break_points:
• on creation every break point is assigned a number
b [location] ([condition]) : sets break-point
tb [location] ([condition]) : sets temporary break-point
rb [regex] ([condition]) : sets break-point on all functions matching [regex]
[location]:
([file])<int> : at <int>th line of source file [file]; defaults to the file that execution is currently at
[+ || -]<int> : at [+ || -]<int>th line from the current one
*[address] : at (assembly) instruction
[func] : at function named [func]
[condition]:
if [logic] : breaks only if [logic] evaluates to true { b 12 if my_var == 2 }
ignore [int1] [int2] : disables breaking at break-point [int1] for the first [int2] times encountering it
disable <int> : disables breaking at break-point <int>
enable [specifier] <int> : re-enables breaking at break-point <int>
once : disable break-point after hit
count <int> : disables break-point after it has been hit <int> times
delete : delete break-point after hit
del <int> : deletes breakpoint number <int>; dont supply an <int> to delete all breakpoints
clear <int> : deletes breakpoint at line <int>
save b [file] : saves breakpoints to file for future sourcing
Watch_points:
○ [watchpoint_set]
wa [var] : sets watchpoint to [var]
wa [cast][address] : sets watchpoint to [address] casted to [cast]
{ (gdb) wa *(long*)[0x0n123456]
}
wa [condition] : sets watchpoint to [condition]; where it can be any expression native to the current language
r[watchpoint_set] : sets a temporary watchpoint
Moving_around:
r [options] : run
< [file] :
< <([shell command]) :
start : starts program then breaks at main
c : continues (after reaching breakpoint)
s <int> : steps; run program until reaching <int> different source lines; (deffault is 1)
next : executes until function returns
Practical_example_to_stopping_anywhere:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━┳━━━┳━━━┳━━━┓
┃ ┃ ┃ S ┃ N ┃ U ┃ F ┃
┃ ┃ ┃ t ┃ e ┃ n ┃ i ┃
┃ Program Source ┃ Break target ┃ e ┃ x ┃ t ┃ n ┃
┃ ┃ ┃ p ┃ t ┃ i ┃ i ┃
┃ ┃ ┃ ┃ ┃ l ┃ s ┃
┃ ┃ ┃ ┃ ┃ ┃ h ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━┯━━━━━━━╋━━━╋━━━╋━━━╋━━━┫
┃┌─gdb_graph.c───────────────────────────────────────────┐┠──────────────┼───────╂───╂───╂───┨ ┃
┃│ 1 │┃ FALLTHROUGH │ ┃ ┃ ┃ ┃ C ┃
┃│ 2 #include "header.h" │┃ FALLTHROUGH │ ┃ ┃ ┃ ┃ a ┃
┃│ 3 │┃ FALLTHROUGH │ ┃ ┃ ┃ ┃ n ┃
┃│ 4 signed main(){ │┃ 4 │ main ┃ ┃ ┃ ┃ n ┃
┃│ > 5 int r = fun(); │┃ 5 │ ┃ ┃ ┃ ┃ o ┃
┃│ 6 return r; │┃ 6 │ ┃ ┃ < ┃ < ┃ t ┃
┃│ 7 } │┃ 7 │ ┃ ┃ ┃ ┃ ┃
┃└───────────────────────────────────────────────────────┘┠──────────────┼───────╂───╂───╂───┨ b ┃
┃┌─header.h──────────────────────────────────────────────┐┠──────────────┼───────╂───╂───╂───┨ e ┃
┃│ 1 #define FUNSIZE 32 │┃ FALLTHROUGH │ ┃ ┃ ┃ ┃ ┃
┃│ 2 int fun(){ │┃ header.h:2 │ fun ┃ < ┃ ┃ ┃ u ┃
┃│ 3 int a[FUNSIZE]; │┃ FALLTHROUGH │ ┃ ┃ ┃ ┃ s ┃
┃│ 4 int r = 0; │┃ header.h:4 │ ┃ ┃ ┃ ┃ e ┃
┃│ 5 for(int i = 0; i < FUNSIZE; i++){ │┃ header.h:5 │ ┃ ┃ ┃ ┃ d ┃
┃│ 6 r += a[i]; │┃ header.h:6 │ ┃ ┃ ┃ ┃ ┃
┃│ 7 } │┃ header.h:7 │ ┃ ┃ ┃ ┃ ┃
┃│ 8 return r; │┃ header.h:8 │ ┃ ┃ ┃ ┃ ┃
┃│ 9 } │┃ header.h:9 │ ┃ ┃ ┃ ┃ ┃
┃└───────────────────────────────────────────────────────┘┠──────────────┼───────╂───╂───╂───┨ ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┷━━━━━━━┻━━━┻━━━┻━━━┻━━━┫
┃ ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┯━━━━━━━┳━━━┳━━━┳━━━┳━━━┫
┃┌─gdb_graph.c───────────────────────────────────────────┐┠──────────────┼───────╂───╂───╂───╂───┨
┃│ 1 │┃ FALLTHROUGH │ ┃ ┃ ┃ ┃ ┃
┃│ 2 #include "header.h" │┃ FALLTHROUGH │ ┃ ┃ ┃ ┃ ┃
┃│ 3 │┃ FALLTHROUGH │ ┃ ┃ ┃ ┃ ┃
┃│ 4 signed main(){ │┃ FALLTHROUGH │ main ┃ ┃ ┃ ┃ ┃
┃│ 5 int r = fun(); │┃ main.c:5 │ ┃ ┃ ┃ ┃ < ┃
┃│ 6 return r; │┃ main.c:6 │ ┃ ┃ ┃ ┃ ┃
┃│ 7 } │┃ main.c:7 │ ┃ ┃ ┃ ┃ ┃
┃└───────────────────────────────────────────────────────┘┠──────────────┼───────╂───╂───╂───╂───┨
┃┌─header.h──────────────────────────────────────────────┐┠──────────────┼───────╂───╂───╂───╂───┨
┃│ 1 #define FUNSIZE 32 │┃ FALLTHROUGH │ ┃ ┃ ┃ ┃ ┃
┃│ 2 int fun(){ │┃ 2 │ fun ┃ < ┃ ┃ ┃ ┃
┃│ 3 int a[FUNSIZE]; │┃ FALLTHROUGH │ ┃ ┃ ┃ ┃ ┃
┃│ 4 int r = 0; │┃ 4 │ ┃ ┃ ┃ ┃ ┃
┃│ 5 for(int i = 0; i < FUNSIZE; i++){ │┃ 5 │ ┃ ┃ < ┃ < ┃ ┃
┃│ > 6 r += a[i]; │┃ 6 │ ┃ ┃ ┃ ┃ ┃
┃│ 7 } │┃ 7 │ ┃ ┃ ┃ ┃ ┃
┃│ 8 return r; │┃ 8 │ ┃ ┃ ┃ ┃ ┃
┃│ 9 } │┃ 9 │ ┃ ┃ ┃ ┃ ┃
┃└───────────────────────────────────────────────────────┘┠──────────────┼───────╂───╂───╂───╂───┨
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┷━━━━━━━┻━━━┻━━━┻━━━┻━━━┛
Signals:
• signal naming follows the C std (see AT "C++/C std/signal.h");
numbers also work
signal <signal> : sends <signal> to the debugged program
handle <signal> <action> : defines what to do when <signal> is received by the program
(no)stop
(no)print
(no)pass
(no)ignore
COMMANDS:
Runtime_management:
kill : kills the program being debugged
file [name] : loads [name] as the executable to debug
l : lists; display source code; only works if debugging symbols were supplied at compilation;
displays 10 lines by default
<int> : displays with <int>th line as the center
[function] : displays from [function]
[int1],[int2] : displays from line [int1] till line [int2]
Information:
layout : changes the layout of windows
src : displays source and command windows; you must list it first
asm : displays disassembly and command windows
split : displays source, disassembly and command windows
regs : displays register window
show [...] : show [...] (?!)
args : show arguments
print (/[format]) [exp] : prints the value of a given expression in a given format
[exp]:
[var] : print [var]
x (/<int>[format][u]) [addr] : examine <int>*[u] memory at [addr]
u : unit size
b - bytes
h - two bytes (half word)
w - four bytes (word)
g - eight bytes (giant word)
format:
o - octal
x - hexadecimal
u - unsigned decimal
t - binary
f - floating point
a - address
c - char
s - string
i <subject> : info; prints info on [subject]; it has nothing to do with help
args : arguments of current function
func : list of functions
locals : list of local variables
threads : current thread
wat : list of watchpoints
b : list of breakpoints
f : frame information
r : registers
disas <func> : disassemble; displays assembly for <func>
Call_stack:
where : prints call stack
up : goes up a frame to display
down : goes down a frame to display
frame <uint> : jumps to frame number <uint> to display
Misc:
shell [...] : run (non gdb) commands from inside gdb
python [...] : run python commands inside gdb; the executed code has access to the gdb API
help : surprisingly it DOES help a lot
signal [signal] : sends [signal] to the debugged program; signal naming follows the C std (see AT "C++/C std/signal.h")
q : quit
Configuration:
set print element <int> : sets how many elements of an array should be outputed at once;
200 by default; 0 signals limitless output
set follow-fork-mode [mode]
child
parent
Api:
pretty_printing:
gdb.pretty_printers.append(<fun>) : adds a new call back to execute when printing
<fun>(gdb.Value) -> <obj> : must either return an object with a to_string() or None
gdb.Value : python API representation of a variable
Members:
type : string; name of the type;
keywords such as const might be stored within its value
Member_functions:
cast(<type>)
gdb.type_lookup(string <name>) -> <type>
<type>
Member_functions:
pointer()
dereference()
### Simple example of a gdb pretty printer ###
1. Assume the following type in C
{
typedef struct {
int i;
} hello_world_t;
hello_world_t my_hw;
}
• compiling with debugging information is required (-ggdb under gcc)
2. We create a pretty printer in python
# myPrettyPrinter.py
class mybsPrinter:
def to_string:
return "Hello World"
def mylookup(val):
if str(val.type) == "hello_world_t":
return mybsPrinter(val)
return None
gdb.pretty_printers.append(mylookup)
3. Source the pretty printer code from inside gdb like so:
$ python execfile('myPrettyPrinter.py') # python 2
$ python exec(open(('myPrettyPrinter.py').read()) # python 3
or copy paste the whole thing into your .gdbinit after the keyword "python"
python
<...>
4. Invoke our pretty printer by printing an appropriate instance
$ p my_hw
#
SHORTCUTS:
[ctrl] + [x] & [a] : switch between windowed and normal layout mode
[ctrl] + [x] & [1] : switch to one windowed layout mode \____(switching between these two will cycle the contents of the windows)
[ctrl] + [x] & [2] : switch to two windowed layout mode /
— XXX things which are terrible about GDB and should be fixed:
• in tui mode, theres no way to buffer the command window
• https://stackoverflow.com/questions/16031100/how-can-i-make-gdb-print-unprintable-characters-of-a-string-in-hex-instead-of-oc
linters
#define linters:: \
I _ _ _ \
I | | (_)_ _| |_ ___ _ _ ___ \
I | |__| | ' \ _/ -_) '_(_-< \
I |____|_|_||_\__\___|_| /__/ I
• a program that provides static code analysis
• the goal is to direct programmers best practices and catch potential bugs early
• in my experience they tend to be too strict and poorly configurable
--------
splint
--------
https://splint.org/
• super strict C linter
• only supports older standards
• has some good ideas about code quality
• used by The Only Serbian Developer