# Debugging guide *Last updated November 5th, 2019* This page details various tools and techniques we use to debug the Materialize software stack / Rust programs more generally. ## Overview There's a couple of different options for debugging problems with Rust programs 1. Printing more data / printing backtraces 2. Using a debugger ## Printing more data ## Debuggers Rust provides wrappers over `gdb` and `lldb` called `rust-gdb` and `rust-lldb`. These wrappers mainly add better pretty printing for Rust objects and limited support for parsing Rust expressions in the debugger's REPL. On macOS, `rust-lldb` should be ready to go pretty much out of the box but `rust-gdb` will require some setup. Conversely, on Linux, `rust-gdb` should be available out of the box and `rust-lldb` is currently unavailable. At this time, we don't have a clear recommendation between gdb and lldb. Sometimes, gdb provides more useful introspection than lldb. Other times, gdb hangs and is unable to debug but lldb works. The best recommendation we have at the moment is to start with the tool thats easiest on each platform (lldb for macOS, gdb for Linux) and only bother setting up gdb on macOS if lldb fails. Either way, it's good to have the option to use either tool, and any technique used on one debugger will translate to the other. ### rust-gdb setup on macOS First, install `gdb` with ```shell $ brew install gdb ``` Verify that you have `gdb` version 8.3 or higher by checking ```shell $ gdb -v ``` If you don't then you may have to update Homebrew [TODO source] or build `gdb` from source [TODO source] Once you have a recent `gdb` version create a file called `.gdbinit` in your home directory and add the following line to it ``` set startup-with-shell disable ``` We're almost done setting up `gdb`. Now we just need to code sign the binary. First, we need to create a new certificate. 1. Open up Keychain Access 2. Select Certificate Assistant -> Create a Certificate 3. Give it a name like `gdb-cert` (You can call it whatever you like just substitute that name in the rest of the instructions) 4. Set "type" to "Self-signed root" 5. Set "Certificate Type" to "Code Signing" 6. Select "Let me override defaults" 7. Scroll through and select default options for everything (e.g. key size, encryption algorithm) 8. For the "Location" option choose "System" 9. Restart the machine (it may work without the restart but the Internet recommends it) After we've made the certificate we need to make an entitlements file (these are formatted as XML). ```shell cat gdb.xml com.apple.security.cs.allow-jit com.apple.security.cs.allow-unsigned-executable-memory com.apple.security.cs.allow-dyld-environment-variables com.apple.security.cs.disable-library-validation com.apple.security.cs.disable-executable-page-protection com.apple.security.cs.debugger com.apple.security.get-task-allow ``` Once you have an entitlements file (example name is `gdb.xml`) all you have to do is ```shell codesign --entitlements gdb.xml -fs gdb-cert /usr/local/bin/gdb ``` We are finally ready to start debugging! ### Using rust-{gdb,lldb} First, we need to see what binaries need to be debugged. Adding `-v` to Cargo commands should give the necessary info. After running for example `cargo test -v` ```shell Running `/Users/Test/github/sqlparser/target/debug/deps/sqlparser-726083e10aabeebf` ``` The path after "Running" is the binary - to validate that just run ```shell $ /path/to/binary ``` We can now use `rust-gdb` (or `rust-lldb`) by simply doing ```shell $ rust-gdb /path/to/binary ``` There's a few more wrinkles / tips and tricks for running unit tests. The Rust test runner (what we get when we call `cargo test`) defaults to spawning multiple unit tests in parallel. We can use the `RUST_TEST_THREADS` environment variable to limit it to one thread which is easier to debug. ```shell $ RUST_TEST_THREADS=1 rust-gdb /path/to/binary ``` Finally, adding breakpoints can make the debugger execution really slow it (chalk it up to Rust unoptimized builds + debugger overhead maybe?) We can pass the name of the test we want to run as a command line arg ```shell $ RUST_TEST_THREADS=1 rust-gdb --args /path/to/binary ``` The corresponding command for `lldb` is: ```shell $ RUST_TEST_THREADS=1 rust-lldb -- /path/to/binary ``` In general, GDB and LLDB commands have the annoying property of being very similar, but not identical. This [page](https://lldb.llvm.org/use/map.html) maps commands from one debugger to another. At this point, we are free to do all sorts of things with our debugger - set breakpoints, inspect memory, and step through code execution to name just a few examples. ### Debugging forked child processes with gdb GDB does not automatically attach to forked child processes, which can make debugging Materialize more difficult. In order to attach to child processes you first need to tell GDB to attach to both processes on fork: ```gdb (gdb) set detach-on-fork off ``` Then you need to tell GDB to follow the child process on fork: ```gdb (gdb) set follow-fork-mode child ``` These commands can be saved in a [`.gdbinit`](https://sourceware.org/gdb/onlinedocs/gdb.html#Initialization-Files) file for convenience. When a child process exits, you will need to manually switch back to the parent process to resume execution. You should see an output that looks like this: ```gdb [Inferior 3 (process 615551) exited with code 01] ``` Inferior number and process number will likely be different. GDB calls any program it executes an inferior. To see all the current inferiors, run: ```gdb (gdb) info inferiors Num Description Executable 1 process 615534 /home/user/materialize/target/debug/materialized 2 process 615550 /usr/bin/python3.8 * 3 /usr/bin/dpkg-query ``` Your output will likely vary, but the current dead process will be prefixed with `*` and have a description of ``. To continue debugging you will need to switch to the parent process and continue execution: ```gdb (gdb) inferior 2 [Switching to inferior 2 [process 615550] (/usr/bin/python3.8)] [Switching to thread 2.1 (Thread 0x7ffff7bf2740 (LWP 615550))] #0 arch_fork (ctid=0x7ffff7bf2a10) at ../sysdeps/unix/sysv/linux/arch-fork.h:49 49 ../sysdeps/unix/sysv/linux/arch-fork.h: No such file or directory. (gdb) continue Continuing. ``` Again, your inferior number and output will likely be different. ### Further information The rustc [guide](https://rustc-dev-guide.rust-lang.org/debugging-support-in-rustc.html) has a page detailing the state of debugger support in Rust. There's plenty of guides for how to use GDB/LLDB online but most of them focus on C/C++ programs. [Beej's Quick Guide to GDB](https://beej.us/guide/bggdb/) is a good starting point.