∆ of what you have to what you want
It's an inefficient way to find and fix bugs — it can consume your project!
Before you debug:
Static checking
gcc -Wall foo.c # turn on all warning for foo.c that enable staic checkings
gcc -Wstrict-aliasing # just turn on one option
gcc -Wtype-limits # uit_t u = ... if (u < 0) return -1 | limits the type
gcc -fanalyzer # causes gcc to analyze the code across functin boundaries
Improve static checking quality by changing the program, annotating to improve checking
_Noreturn void abort(void);
int hash(char const*) __attribute__((pure));
/* attribute tells gcc that the return value depends only on storage
+ args of function and the function has no side effects */
int square(int)__attribute__((const));
/* pure, and also doesn't depend on storage */
int errprintf(char const *fmt, ...)__attribute__((format(printf,1,2)));
/* gcc can handle the errpintf like printf and use its error msg to this */
Dynamic checking
ps , top , time
strace #(trace system calls)
ltrace #(trace library calls)
# ^ they both use logs to debug
valgrind # every time you dereference a pointer, it checks if its an object. this does not require recompilation, runs on already-build executable
# checking is less precise
gcc -fsanitize=address # inserts code to check for subscripts errors, dangling pointers
# (make your buggy program crash more reliably)
# + more precise checking - more code generates (slower program)
gcc -fsanitize=undefined # watch for signed integer overflow, for example
gcc -fsanitize=leak # turns on the leak detection
gcc -fsanitize=thread # thread sanitization when writting multile thread program, catch race conditions
Dynamic analysis are not perfect because they check just one execution
Avoid debugging
Don't guess at random: too inefficient
Systematic
Aside: definition of
error - mistake in programmer's head
fault - latent bug in program
failure - program's behavior is not what the user want, an observable mismatch
GDB
gcc -g3 -O0 foo.c
# the -g3 flag tells the compiler to generate debugging metainfo
# try to use as little as optimization as possible
(gdb) r a b # run the program with given argument
(gdb) stepi # step in into one machine code
(gdb) step (s)# single step through the source code
(gdb) next (n) # go to the next sourcecode line within function
(gdb) fin # finishes execution of function
(gdb) b <label> # add a breaking point on the given label
(gdb) c # continue till next break point/return
(gdb) info break # gives out information of breakpoints
(gdb) ^C # to regain control of the program being debugged
(gdb) bt # backtrace the function
# once in backtrace
(gdb) up # going up in the backtrace
(gdb) down # going donw in the backtrace
# the backtrace process is basically exploring a program execution
(gdb) p <EXPR> # evalutae EXPR and print the result
# we are actually executing <EXPR> in the debugging program
# create machine code and put into memory and executes it
(gdb) set cwd /etc # change the working directory of the program
(gdb) set env PATH /usr/bin:/Bin # change the environment for the program
(gdb) set disable-randomization on/off # turn on/off the security feature that
# runs program in random location in stack
(gdb) attach PID # assuming curr is not running, stop PID and continue w/ curr
(gdb) detach # reverse the attach behavior
(gdb) watch <EXPR> # keep watching the expr until it chanegs
(gdb) rc # like 'c' but "runs" the program in reverse, operates by consulting gdb history
# only aviliable on several platforms upon request
(gdb) checkpoint # save the program state and gives out an id
(gdb) restart <ID> # restart on checkpoint ID
best strategy: try not to use a debugger when you can...
try use a print statement first
There might exist some pre-build steps that can build part of the programs that are portable for the builder using tools like automake, autoconfig etc.
Makefile.am : maintained by hand, generate Makefile.in
Automake: uses Makefile.in to generate Autocone
configure : generate Makefile and also config.h
Then, once the builder has those prebuilt files, they can make them to executables with a simpler set of tools