Reverie intercepts Linux syscalls before the kernel sees them
An ergonomic and safe syscall interception framework for Linux.
At a glance
- What is it?
- Reverie is a user space syscall interception framework for Linux, written in Rust, where a tool subscribes to selected events and handles each one with callbacks while a backend does the trapping and process supervision. It suits people building observability, fault injection or determinism tooling rather than application code.
- Who is it for?
- Adopt Reverie when you need to observe or rewrite system calls in a process tree rather than only in your own code, since a tool can subscribe to a minimal set of events, modify arguments and return values, inject or suppress calls, and act as a GDB server. Do not adopt it for a syscall heavy production workload, because the README is explicit that ptrace adds significant overhead and Reverie adds similarly significant overhead on top.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Interception happens at the user/kernel boundary
Reverie is described as a user space system call interception framework for Linux, and the sentence that defines it is about position rather than about features. It can intercept, modify or elide a syscall before the kernel executes it, and in essence it sits at the boundary between user space and kernel space. That position is what makes the use cases possible. The README lists three: observability tools like `strace`, failure injection to test error handling logic, and manipulating scheduling decisions to expose concurrency bugs. The third one is the interesting one, because scheduling bugs are the class of defect that unit tests reproduce least reliably, and a framework that can rewrite the order of events can turn a rare interleaving into a repeatable one. The repository is hosted under `facebookexperimental`, and the primary consumer named in the README is Hermit.
Tools are callback sets, and they subscribe to what they need
The terminology section is worth reading because the vocabulary is precise. Clients of the library write tools. A tool runs a shell command, creating a guest process tree made of multiple guest threads and processes, in an instrumented manner. Each tool is written as a set of callbacks, which the README calls handlers, and those are invoked each time a guest thread encounters a trappable event such as a system call or an inbound signal. The tool stipulates exactly which event streams it subscribes to, and the tool itself is stateful, maintaining state between consecutive invocations. The feature list spells out the ergonomics that follow from this design: it is easy to modify syscall arguments or return values, inject multiple syscalls, or suppress the syscall entirely, and async and await usage allows blocking syscalls to be handled without blocking other guest threads. It can also intercept CPUID and RDTSC instructions, and typed syscalls give every call a wrapper, which makes pointer values easier to access and gives strace-like pretty printing for free.
The backend contract is what makes the framework swappable
A backend is the other half of the picture, and this is the most distinctive part of the design. Where a tool decides what to do on each event, a backend decides how those events are trapped and how the tool is actually run against a live guest. The backend spawns and supervises the guest process tree, intercepts syscalls and other trappable events, routes each event to the tool's handlers, hosts the tool's global state, and tears everything down at exit. A backend is a swappable implementation, anything playing the same role as `reverie-ptrace`, and it must be able to host an arbitrary tool rather than a hard-coded one. The README lists the implementations: the production ptrace backend, the in progress KVM and DBT backends, and the experimental SaBRe, e9patch and LiteInst rewriting backends. The reference backend is `reverie-ptrace`, and the README is careful that `reverie-e9patch` and `reverie-liteinst` implement the generic Backend contract while KVM, DBT and SaBRe currently expose specialized runners or adapters instead of that generic launch contract.
State is split into process, thread and global layers
The overall architecture section names three components of a tool to consider. There is the process-level state, the thread-level state, and the global state, which is shared among all processes and threads in the traced process tree. The README says the separation is meant to provide an abstraction that allows future Reverie backends to be used without requiring the tool to be rewritten, which is the same argument as the backend contract seen from the other side. The lifecycle rules are simple. Whenever a new process is spawned, that is, when the guest calls `fork` or `clone`, a new instance of the process state struct is created and managed by the Reverie backend, and thread state is created as the guest's threads make syscalls. The repository is a Cargo workspace with many crates, and the split is visible in the directory names, with `reverie-syscalls`, `reverie-process`, `reverie-memory`, `reverie-util`, `reverie-rpc-transport` and `safeptrace` sitting alongside the backend crates.
Building it and running the strace example
Reverie needs two system level dependencies, and the README says they are required to get backtraces from the guest process:
sudo apt install pkg-config libunwind-develThe test suite is run with the tests serialised, which is what you would expect for a framework that spawns and traces process trees:
cargo test -- --test-threads=1The strace example lives in the `reverie-examples` directory and is run as a binary against a command:
cd reverie-examples
cargo run --bin strace -- lsThere is one more step if you want the larger backends. Large native sources for DynamoRIO, SaBRe and e9patch are pinned as shallow submodules, and the README says `git submodule update --init --recursive` checks out every pinned source while `scripts/backend-submodule.sh` remains available for focused activation. The Makefile in the repository has a `checkout-all` target for that, along with `checkout-e9patch` and `checkout-sabre`, and it carries a long comment about copying the update policy from `.gitmodules` into the local git config so that older checkouts do not silently skip those submodules.
Two ways to run a tool, one concrete and one abstract
Copying an example tool into a new Rust project is the documented starting point, and you will find that it depends both on the general `reverie` crate for the API and on the specific backend implementation crate, `reverie_ptrace`. Running a tool always follows the same shape: pick a backend, hand it a command and the tool's config, and receive the guest's exit status together with the tool's final global state. With the ptrace backend's builder that looks like this:
// `MyTool: reverie::Tool`
let tracer = reverie_ptrace::TracerBuilder::<MyTool>::new(command)
.spawn()
.await?;
let (exit_status, global_state) = tracer.wait().await?;The same run, expressed through the abstract trait, is:
use reverie::Backend;
let (exit_status, global_state) =
reverie_ptrace::PtraceBackend::run::<MyTool>(command, config).await?;The second form is the one that matters if you intend to move between backends, since any backend implements the `reverie::Backend` trait and `reverie_ptrace::PtraceBackend` is the reference implementation of it.
ptrace overhead is the price of the approach
The performance section is honest in a way that many framework READMEs are not. Since ptrace adds significant overhead when the guest has a syscall heavy workload, Reverie will add similarly significant overhead, and the slowdown depends on how many syscalls are performed and are intercepted by the tool. The mitigation is architectural rather than a tuning knob. The primary way to improve performance with the current implementation is to implement the `subscriptions` callback, specifying a minimal set of syscalls that are actually required by the tool, and the feature list says the same thing from the other side, that you can avoid paying the cost of intercepting syscalls you do not care about, for example intercepting only `sys_open`. There is also a separate, non-CI counter2 performance shootout that measures native normalized instrumentation overhead across the available backends on calibrated multi second workloads, and it correctness-probes the known green workload intersection before collecting any timing samples.
Hermit is the reason the framework exists
The README explains the lineage plainly. Reverie is the instrumentation layer beneath Hermit, a reproducible Linux container that makes a program's execution deterministic by replacing sources of nondeterminism such as time, randomness and thread scheduling, and Hermit is Reverie's primary consumer. Its determinism engine is itself written as a Reverie tool, which is where the tool concept comes from. Two more features complete the picture of what a tool can do. Reverie can act as a GDB server, which allows a connection through the GDB client so you can step through the process being traced, and it can act as a tool in its own right for the observability and fault injection cases. The licence is one GitHub cannot classify, a custom licence in the LICENSE file, and the repository also carries a NOTICE file, a `CODE_OF_CONDUCT.md`, a `CHANGELOG.md` and a `BACKENDS.md` with the backend architecture. There are no GitHub releases, and the last push was on 2026-10-16.
Editorial conclusion
Adopt Reverie when you need to observe or rewrite system calls in a process tree rather than only in your own code, since a tool can subscribe to a minimal set of events, modify arguments and return values, inject or suppress calls, and act as a GDB server. Do not adopt it for a syscall heavy production workload, because the README is explicit that ptrace adds significant overhead and Reverie adds similarly significant overhead on top. Verify first which backend you will use, since only `reverie-ptrace` is described as the production one and the KVM, DBT, SaBRe, e9patch and LiteInst backends are in progress or experimental.
Frequently asked questions
What is Reverie used for?
It is a user space syscall interception framework for Linux that can intercept, modify or elide a syscall before the kernel executes it. The README names observability tools like strace, failure injection for testing error handling, and manipulating scheduling decisions to expose concurrency bugs.
What system dependencies does Reverie need?
The README lists `sudo apt install pkg-config libunwind-devel`, and says these are required to get backtraces from the guest process. The test suite is then run with `cargo test -- --test-threads=1`.
Which Reverie backends are production ready?
The ptrace backend is described as the production one and the reference implementation of the generic Backend contract, along with `reverie-e9patch` and `reverie-liteinst`. KVM, DBT and SaBRe are in progress or experimental and currently expose specialized runners rather than that generic launch contract.
How do I reduce the performance cost of Reverie?
Implement the `subscriptions` callback so that only the syscalls your tool actually needs are intercepted. The README states that ptrace already adds significant overhead for syscall heavy workloads and that Reverie adds similarly significant overhead on top of that.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/facebookexperimental-reverie)Was this useful?