# RSH 016 internal: debugging
## Introduction
- This is a really challenging topic to talk about, since there are
- So many ways to go wrong
- So many ways to fix it
- No one right answer, it's more of an art than a science
- https://en.wikipedia.org/wiki/List_of_software_bugs
## Types of bugs
- syntax errors
- runtime errors
- Hard errors that are clearly reported as errors
- results are wrong (like the kelvin example)
- Heisenbug: trying to study the bug changes it, e.g. adding print statements changes timings. Memory locations change. etc
- Compiled with or without optimizations. Stepped through with debugger to eliminate race conditions.
- you add a print statement and bug goes away: memory bugs
- schrödinbug: it never worked, but you never noticed it.
- Local (clearly identify to one line) vs systematic (a property of the whole system)
## Approach to debugging
- Reality not as fancy as you might thing
- Take a break
- Reducing size of the problem
- Make it run faster, but still produce the bug (smaller input data, less iterations, etc)
- Removing degrees of freedom: Disable optional features until you get straight to it
- i thought efficient debugging is like efficient tree-search of possibilities "inside our head": eliminating as big branches as possible as early as possible.
- Finding the point of the problem
- bisection
- git bisect: when you have good version control, when was it introduced?
- Turn off optional features and see if the problem still occurs
- "Deactivating code"/skipping code to locate memory problems: making the result wrong but making the code not crash
- git grep
- it can be useful to make the code produce "not scientifically meaningful" results for the sake of debugging
- Now, you roughly know the point. What now?
- Reading error messages
- How to approach stack traces and finding the problem
- How to pick the interesting part out of error message: read from both the top and the bottom
- internet-searching solutions with the right error message
- Turn it into a unit test or example script, that is self-contained
- Can you make the example portable to someone else to try (git, conda, containers, etc?)
- This is basically a prerequisite to asking someone for help
- Asking for help
- When to ask for help (after you have narrowed it down)
- What to include when asking (not "it doesn't work")
- what to do if it crashes/fails in somebody else's code (library or package)
## Preparing code for debugging
- various points from Zen of Python, for example "Errors should never pass silently / unless explicitly silenced", but there is more relevant in it too
- print debugging
- writing good error messages, catching errors the right way
- don't trap all errors and ignore them
- logging and verbosity
- shell: set -x
- -v, -q
- stdout vs stderr for printing stuff
- logging module **show example (rkdarst)**
- Everything defined in levels: debug, info, warning, error, critical
- https://github.com/NordicHPC/envkernel/blob/master/envkernel.py
- assertions, programming for safety
- shell script strict modes
- set -e ; set -u ; set -o pipefail . often done as set -euo pipefail
- debug compile flags
## Debuggers and debugging tools
- "debuggers do not remove bugs, they run your code in slow motion"
- **gdb/pdb type interface (AF)**, let it run until error happens
- demo-debugging/fortran/bugs
- explicit error to make it start
- print statements
- Useful as starting point
- Maybe use logging module instead?
- jupyter: **%%debug (RD)**
- import IPython ; IPython.embed() or or from code import interact; interact()
- **Valgrind and memory bugs (Radovan)**
- C/C++ example with 3 memory bugs (use after free, memory leak, out of bounds access)
- **debugging through IDE**; remote debugging
- **variable inspector (RD)**
## Bonus if we have time
- git bisect example