ššš Spray ššš
Get started –
Issues –
Bug report
You can watch a tiny demo of using Spray to interact with a running program here: https://youtu.be/mjwIrfQkURc
Spray is a small debugger for C code that comes with minimal mental overhead. All functionality aims to be simple and easy to grasp.
In Spray you can easily control the execution of running programs, and inspect and modify their state.
I started work on Spray out of curiosity about the mysterious inner workings of debuggers. In addition, I want to explore ways in which debugging can be made more approachable.
š¦¾ Features
- Breakpoints on functions, on lines in files and on addresses
- Printing and setting variables, memory at addresses and registers
- C syntax highlighting
- Backtraces
- Instruction, function and line level stepping
- Filters to format command output
š Roadmap
- Printing and modifying complex structures
- Syntax highlighting for complex structures
- Backtraces based on DWARF instead of frame pointers
- Inlined functions
- Loading external libraries
- Catching signals sent to the debugged program
šæļø Installation
Parts of the Spray frontend are written in Scheme and embedded into the application using CHICKEN Scheme which compiles Scheme to C. Currently, you need to have CHICKEN installed to build Spray. In the future it’s possible that the generated C files are provided instead so that you only need a C compiler.
Spray depends on libdwarf
so if you want to build Spray, you need to install libdwarf first. Then, to install Spray you clone this repository and run make
. Note the you
have to clone all the submodules too.
git clone --recurse-submodules https://github.com/d4ckard/spray.git
cd spray
make
The compiled binary is named spray
and can be found in the build
directory.
To use spray
as a regular command you need to add it to your $PATH
.
šāāļø Running Spray
Ensure that the binary you want to debug has debug information enabled, i.e. it was compiled with the
-g
flag. Also, you should disable all compile-time optimizations to ensure the best output.
Spray is only tested using Clang. The debug information generated by different compilers for the same piece of code varies. Thus,
clang
should be used to compile the programs you want to debug using Spray.
The first argument you pass to spray
is the name of the binary that should be debugged (the debugee). All subsequent arguments are the arguments passed to the debugee.
For example
clang -g examples/free_uninit.c spray a.out
starts a debugging session with the executable a.out
.
āØļø Commands
Spray’s REPL offers the following commands to interact with a running program.
Reading and writing values
Command | Argument(s) | Description |
print , p |
|
Print the value of the runtime variable. |
|
Print the value of the register. | |
|
Print the value of the program’s memory at the address. | |
set , t |
|
Set the value of the runtime variable. |
|
Set the value of the register. | |
|
Set the value of the program’s memory at the address. |
Register names are prefixed with a %
, akin to the AT&T assembly syntax. This avoids name conflicts between register names and variable names. For example, to read the value of rax
, use print %rax
. You can find a table of all available register names in src/registers.h
.
A
can be a hexadecimal or a decimal number. The default is base 10 and hexadecimal will only be chosen if the literal contains a character that’s exclusive to base 16 (i.e. one of a – f). You can prefix the literal with 0x
to explicitly use hexadecimal in cases where decimal would work as well.
An
is always a hexadecimal number. The prefix 0x
is again optional.
Breakpoints
Command | Argument(s) | Description |
break , b |
|
Set a breakpoint on the function. |
|
Set a breakpoint on the line in the file. | |
|
Set a breakpoint on the address. | |
delete , d |
|
Delete a breakpoint on the function. |
|
Delete a breakpoint on the line in the file. | |
|
Delete a breakpoint on the address. | |
continue , c |
Continue execution until the next breakpoint. |
It’s possible that the location passed to break
, delete
, print
, or set
is both a valid function name and a valid hexadecimal address. For example, add
could refer to a function called add
and the number 0xadd
. In such a case, the default is to interpret the location as a function name. Use the prefix 0x
to explicitly specify an address.
Stepping
Command | Description |
---|---|
next , n |
Go to the next line. Don’t step into functions. |
step , s |
Go to the next line. Step into functions. |
leave , l |
Step out of the current function. |
inst , i |
Step to the next instruction. |
backtrace , a |
Print a backtrace starting at the current position. |
Filters
The print
and set
commands can be followed