{"id":24375934,"url":"https://github.com/thass0/spray","last_synced_at":"2025-07-22T18:33:48.577Z","repository":{"id":187843019,"uuid":"655387612","full_name":"thass0/spray","owner":"thass0","description":"A x86_64 Linux debugger 🐛🐛🐛","archived":false,"fork":false,"pushed_at":"2024-02-05T16:19:01.000Z","size":526,"stargazers_count":234,"open_issues_count":8,"forks_count":8,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-10T02:54:37.218Z","etag":null,"topics":["c","cli","debugger","scheme"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thass0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-18T18:33:15.000Z","updated_at":"2025-04-03T23:10:22.000Z","dependencies_parsed_at":"2023-08-12T10:29:40.051Z","dependency_job_id":"54c3d80b-26a2-4625-8bd2-704ba5a97e1d","html_url":"https://github.com/thass0/spray","commit_stats":null,"previous_names":["d4ckard/spray","thass0/spray"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/thass0/spray","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thass0%2Fspray","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thass0%2Fspray/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thass0%2Fspray/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thass0%2Fspray/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thass0","download_url":"https://codeload.github.com/thass0/spray/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thass0%2Fspray/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266552590,"owners_count":23947179,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","cli","debugger","scheme"],"created_at":"2025-01-19T05:58:35.127Z","updated_at":"2025-07-22T18:33:48.542Z","avatar_url":"https://github.com/thass0.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\t\u003ch2 align=\"center\"\u003e🐛🐛🐛 Spray 🐛🐛🐛\u003c/h3\u003e\n \u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/thass0/spray/#%EF%B8%8F-installation\"\u003eGet started\u003c/a\u003e -\n  \u003ca href=\"https://github.com/thass0/spray/issues\"\u003eIssues\u003c/a\u003e -\n  \u003ca href=\"https://github.com/thass0/spray/issues/new\"\u003eBug report\u003c/a\u003e\n \u003c/p\u003e\n\u003c/p\u003e\n\n[![Spray Demo](https://asciinema.org/a/620413.svg)](https://asciinema.org/a/620413)\n\n\u003e You can watch a tiny demo of using Spray to interact with a running program here: https://youtu.be/mjwIrfQkURc\n\nSpray is a small debugger for C code that comes with minimal mental overhead. All functionality aims to be simple and easy to grasp.\n\nIn Spray you can easily control the execution of running programs, and inspect and modify their state.\n\nI 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.\n\n## 🦾 Features\n\n- [x] Breakpoints on functions, on lines in files and on addresses\n- [x] Printing and setting variables, memory at addresses and registers\n- [x] C syntax highlighting\n- [x] Backtraces\n- [x] Instruction, function and line level stepping\n- [x] Filters to format command output\n\n## 🚀 Roadmap\n\n- [ ] Printing and modifying complex structures\n- [ ] Syntax highlighting for complex structures\n- [ ] Backtraces based on DWARF instead of frame pointers\n- [ ] Inlined functions\n- [ ] Loading external libraries\n- [ ] Catching signals sent to the debugged program\n\n## 💿️ Installation\n\nParts of the Spray frontend are written in Scheme and embedded into the application using [CHICKEN Scheme](https://www.call-cc.org/) which compiles Scheme to C. Currently, you need to have [CHICKEN installed](https://code.call-cc.org/#download) 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.\n\nSpray depends on [libdwarf](https://github.com/davea42/libdwarf-code/releases)\nso 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\nhave to [clone all the submodules](https://stackoverflow.com/a/4438292) too.\n\n```sh\ngit clone --recurse-submodules https://github.com/thass0/spray.git\ncd spray\nmake\n```\n\nThe compiled binary is named `spray` and can be found in the `build` directory.\n\nTo use `spray` as a regular command you need to [add it to your `$PATH`](https://askubuntu.com/a/322773).\n\n## 🏃‍♀️ Running Spray\n\n\u003e 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.\n\n\u003e 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.\n\nThe 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.\n\nFor example\n\n```sh\nclang -g examples/free_uninit.c\nspray a.out\n```\n\nstarts a debugging session with the executable `a.out`.\n\n## ⌨️ Commands\n\nSpray's REPL offers the following commands to interact with a running program.\n\n### Reading and writing values\n\n\u003ctable\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eCommand\u003c/td\u003e\n        \u003ctd\u003eArgument(s)\u003c/td\u003e\n        \u003ctd\u003eDescription\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd rowspan=\"3\"\u003e\u003ccode\u003eprint\u003c/code\u003e, \u003ccode\u003ep\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;variable\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003ePrint the value of the runtime variable.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;register\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003ePrint the value of the register.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;address\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003ePrint the value of the program\u0026#39;s memory at the address.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd rowspan=\"3\"\u003e\u003ccode\u003eset\u003c/code\u003e, \u003ccode\u003et\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;variable\u0026gt; \u0026lt;value\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003eSet the value of the runtime variable.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;register\u0026gt; \u0026lt;value\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003eSet the value of the register.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n\t\u003ctd\u003e\u003ccode\u003e\u0026lt;address\u0026gt; \u0026lt;value\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003eSet the value of the program\u0026#39;s memory at the address.\u003c/td\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n\nRegister names are prefixed with a `%`, akin to the AT\u0026T 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`.\n\nA `\u003cvalue\u003e` 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.\n\nAn `\u003caddress\u003e` is always a hexadecimal number. The prefix `0x` is again optional.\n\n### Breakpoints\n\n\u003ctable\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eCommand\u003c/td\u003e\n        \u003ctd\u003eArgument(s)\u003c/td\u003e\n        \u003ctd\u003eDescription\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd rowspan=\"3\"\u003e\u003ccode\u003ebreak\u003c/code\u003e, \u003ccode\u003eb\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;function\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003eSet a breakpoint on the function.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;file\u0026gt;:\u0026lt;line\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003eSet a breakpoint on the line in the file.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;address\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003eSet a breakpoint on the address.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd rowspan=\"3\"\u003e\u003ccode\u003edelete\u003c/code\u003e, \u003ccode\u003ed\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;function\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003eDelete a breakpoint on the function.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;file\u0026gt;:\u0026lt;line\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003eDelete a breakpoint on the line in the file.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003ccode\u003e\u0026lt;address\u0026gt;\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003eDelete a breakpoint on the address.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e\u003ccode\u003econtinue\u003c/code\u003e, \u003ccode\u003ec\u003c/code\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003c/td\u003e\n        \u003ctd\u003eContinue execution until the next breakpoint.\u003c/td\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n\nIt'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.\n\n### Stepping\n\n| Command          | Description                                         |\n|------------------|-----------------------------------------------------|\n| `next`, `n`      | Go to the next line. Don't step into functions.     |\n| `step`, `s`      | Go to the next line. Step into functions.           |\n| `leave`, `l`     | Step out of the current function.                   |\n| `inst`, `i`      | Step to the next instruction.                       |\n| `backtrace`, `a` | Print a backtrace starting at the current position. |\n\n### Filters\n\nThe `print` and `set` commands can be followed by a filter, to change how output is displayed. For example, if you want to inspect the binary data in the rdx register, you can enter `print %rdx | bin`.\n\nFilters are separated from the command by a pipe symbol: `\u003ccommand\u003e '|' \u003cfilter\u003e`. Currently, only one filter can be used at a time.\n\nThe following table shows how different filters format the same 64-bit word with the value 103.\n\n\n| Filter                | Output                                                                    |\n|-----------------------|---------------------------------------------------------------------------|\n| `dec` (*decimal*)     | `103`                                                                     |\n| `hex` (*hexadecimal*) | `0x67`                                                                    |\n| `addr` (*address*)    | `0x0000000000000067`                                                      |\n| `bits`                | `00000000 00000000 00000000 00000000 00000000 00000000 00000000 01100111` |\n| `bytes`               | `00 00 00 00 00 00 00 67`                                                 |\n| `deref` `*`           | *Prints the value found at memory address `0x67`*                         |\n\nExcept for `deref`, all the above simply change the way the output is formatted. `deref`, abbreviated as `*`, interprets the value that would be printed as a memory address, and prints whatever is found it memory that address. Using `deref`, you can inspect that value that a pointer points to.\n\n## 🛠️Contributing\n\nAll contributions are welcome. Before opening a pull request, please run\nthe test suite locally to verify that your changes don't break any other\nfeatures.\n\nIt's possible that some of the tests fail due to off-by-one errors when\nmaking assertions about specific values found in the example binaries that\nare used in the tests. Refer to [this issue](https://github.com/thass0/spray/issues/2)\nfor more details. You can ignore tests that fail for this reason only.\n\n## 📖 References\n\n- Sy Brand's blog series [Writing a Linux Debugger](https://blog.tartanllama.xyz/writing-a-linux-debugger-setup/) on writing a debugger in C++\n\n- [The DWARF 5 standard](https://dwarfstd.org/dwarf5std.html)\n\n- [libdwarf's documentation](https://www.prevanders.net/libdwarfdoc/index.html)\n\n- Eli Bendersky's posts [How debuggers work](https://eli.thegreenplace.net/2011/01/23/how-debuggers-work-part-1)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthass0%2Fspray","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthass0%2Fspray","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthass0%2Fspray/lists"}