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: ------------- • 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: ------------ • "PERFormance analisys tool" perf <verb> <executable> stat ---------------- valgrind: ---------------- https://valgrind.org/ // the documentation is pretty good • 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 //move; ?! • a _watchpoint_ or _data_watchpoint_ is an expression which, when changed, the program stops //move; ?! 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 //probably move; ?! Watch_points: ○ [watchpoint_set] wa [var] : sets watchpoint to [var] wa [cast][address] : sets watchpoint to [address] casted to [cast] { (gdb) wa *(long*)[0x0n123456] // will watch an 8 byte region at [0x0n123456] as if it were an actual variable } 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 /* @BAKE gcc $@ -o graph.out -ggdb */ │┃ 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 │ ┃ ┃ ┃ ┃ ┃ ┃└───────────────────────────────────────────────────────┘┠──────────────┼───────╂───╂───╂───┨ ┃ ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┷━━━━━━━┻━━━┻━━━┻━━━┻━━━┫ ┃ /* Execution moved */ ┃ ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┯━━━━━━━┳━━━┳━━━┳━━━┳━━━┫ ┃┌─gdb_graph.c───────────────────────────────────────────┐┠──────────────┼───────╂───╂───╂───╂───┨ ┃│ 1 /* @BAKE gcc $@ -o graph.out -ggdb */ │┃ 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: // ?!; deprecate 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; // and an exemplary instance 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