Library / SDK
inspector-repl/inspector avatar
inspector-repl/inspector

Inspector injects a real C++ REPL into a program that is already running

A drop-anywhere C++ REPL

853 stars29 forksC++MIT

At a glance

What is it?
Inspector is a C++ REPL that you drop into a program with a single include, so you can stop inside a running binary, look at the local variables, evaluate expressions against them and then continue. It works by parsing your source with Clang at build time to capture variable addresses, and by starting a clang interpreter inside the process at run time, which is why the REPL speaks the exact dialect your program was compiled with.
Who is it for?
Inspector fits a C++ developer debugging a program that only misbehaves at run time, or exploring a code path where a print statement is inconvenient, and who is willing to rebuild with a Clang based toolchain. Skip it if you need C support, if you are working on a header-only codebase where the include would have to live in a header, or if you want debugger integration rather than an interpreter.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly C++, 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

One include, and you have a prompt inside your program

The pitch in the README is that Inspector injects a fully functional C++ REPL into running, compiled programs, that it can access your program state, and that it offers features like code completion and syntax highlighting. The important qualifier is in the next sentence: the REPL uses the language standard and include paths the program was compiled with.

That last point is what separates this from a generic interpreter bolted on the side. When the REPL evaluates an expression, it evaluates it as your compiler would have, with your standard, your flags and your headers in scope.

The API is deliberately familiar, and the README compares it to Pry in Ruby and Pdb in Python. The mechanism is an include that you drop anywhere inside a function.

c++
// save as main.cpp
#include <iostream>
#include <string>

int main(int argc, char** argv) {
    int a = 1;
    std::string b = "hello world";
#include INSPECTOR
    std::cout << "second break." << std::endl;
#include INSPECTOR
}

The session then looks like this, with the REPL in one terminal and the program in another.

console
$ inspector-c++ -std=c++17 main.cpp -o main
$ inspector repl      # terminal 1: waits for programs to connect
$ ./main              # terminal 2: stops at each INSPECTOR and hands over

The two-terminal arrangement is the point of the design. The program stops where you told it to stop, and you talk to it in a separate session, so the debugging session is not buried in the program's own output.

Clang at build time captures the addresses of your locals

The README explains the mechanism in three steps, and the first one is where the cleverness sits.

At build time, `inspector` parses each source file with Clang. For every `#include INSPECTOR` it writes a small header that captures the addresses of all local variables in scope.

That is the whole trick. Clang already knows the types, the names and the storage of every local in that scope, and it can emit code that takes the address of each one. The generated header is what makes the later evaluation possible, because at run time the REPL needs a pointer to each variable in order to bind a reference to it. Without that address, an expression like `name + "!"` would have no way to refer to the local `name` rather than to a copy of it.

Because the addresses are captured rather than reconstructed, the values the REPL shows are the live ones, and mutating a container through the REPL changes the state of the running program. That is what makes this a REPL inside the process rather than a debugger stepping over a program.

The second step is that at run time the program calls into `libinspector`, which connects to `inspector repl`, the `inspector-repl` binary, over a Unix socket. It then starts clang-repl, using `clang::Interpreter`, with references to those variables, and input and completion requests are evaluated inside the program.

The third step is the exit from the break. Typing `.continue`, or pressing Ctrl-D, resumes the program.

So the architecture is a build-time code generator plus a run-time interpreter plus a socket, and the interesting constraint is that the interpreter is the same Clang that generated the header, which is why the language dialect matches.

The REPL prints values by content and has a small command set

The REPL session in the README shows the behaviour that matters.

code
[0] .locals
  v     std::vector<int>
  name  std::string
[1] v.push_back(4)
[2] v
(std::vector<int> &) {1, 2, 3, 4}
[3] name + "!"
(std::string &) "inspector!"
[4] undefined + 1
error: use of undeclared identifier 'undefined'

The `.locals` command lists the variables in scope with their types, which is the fastest way to find out what you are actually looking at. The type shown for a container is the reference type, and the value is printed by content, so a vector prints its elements rather than a pointer.

The rules for output are described in the README. Expressions print their value, and strings, containers, pairs and any type with `operator<<` are shown by content. A trailing semicolon suppresses the output, which is the conventional way to run a statement for its effect. Multi-line input continues while brackets are open, so a call spanning several lines does not need to be pasted in one piece.

The command set is short: `.locals`, `.where`, `.help` and `.abort`, with `.continue` or Ctrl-D resuming the program and Ctrl-C clearing the current line. The REPL keeps running between breakpoints and between programs, so you do not have to restart the server for each session, and Ctrl-C while it is waiting exits.

History is saved to `$XDG_STATE_HOME/inspector/history`, which means the session survives a restart of the REPL binary.

Four ways to fit it into a build

The README's usage section is organised around build system integration, which is the part of a REPL injection tool that usually goes wrong.

The general answer is a compiler wrapper. `inspector-c++` wraps the real compiler, which is `$INSPECTOR_CXX` with a default of `c++`, and it generates the headers for the include on the fly and links `libinspector`.

console
$ make CXX=inspector-c++                         # make / autotools
$ CXX=inspector-c++ meson setup build            # meson
$ inspector wrap g++ -std=c++17 main.cpp -o main # explicit

For CMake there is a package, and the integration is three lines.

cmake
find_package(inspector REQUIRED)
add_executable(app main.cpp)
inspector_enable(app)   # wraps compiles of `app`, links inspector::inspector

For pkg-config users, `pkg-config --cflags --libs inspector` provides the include and link flags, with the caveat that generating the headers still needs `inspector-c++` or `inspector prebuild`.

And for a manual or compile_commands setup there is a prebuild step, which can also run in parallel over a compilation database.

console
$ inspector prebuild main.cpp --cflags "-std=c++17 -Iinclude"
$ inspector prebuild -p build/compile_commands.json -j8   # parallel
$ c++ $(inspector print-cflags) main.cpp -o main

The `examples/` directory holds a working setup for each of these, with `cmake/`, `make/` and `meson/` subdirectories, and the README says `nix flake check` builds each one and runs `inspector-e2e` against it.

The build needs a recent Clang, and a vendored line editor

The build requirements are the part of this project that will decide whether you can try it in an afternoon.

The first is LLVM/Clang version 22 or later, including its CMake package, `ClangConfig.cmake`. The README is reassuring about the source of that dependency, saying that released distribution packages work, listing apt.llvm.org, Homebrew, Arch and nixpkgs, and that no LLVM source tree or fork is needed. The second is CMake 3.20 or newer plus ninja, and the third is GoogleTest, needed only when `BUILD_TESTING` is on, which is the default.

The build itself is three commands.

console
$ cmake -B build -GNinja
$ ninja -C build
$ ctest --test-dir build      # gtest suites, including a pty-driven REPL test

The binaries land in `build/inspector`, `build/inspector-c++` and `build/inspector-repl`, and there is an end-to-end binary at `build/test/inspector-e2e` that can be pointed at another instrumented program. There is a detail about the test suite worth noting, since it explains a design constraint: one of the gtest suites is driven through a pty, because a REPL is a terminal program and testing it without a terminal would test the wrong thing.

One build hint is there for a common failure. If CMake cannot find the clang executable, it cannot detect the clang resource directory where the builtin headers live, and the README says to pass it explicitly with `-DCLANG_RESOURCE_DIR=$(clang -print-resource-dir)`.

There is a Nix path as well, with `nix build` using `llvmPackages_latest`, `nix run . -- repl`, `nix develop` for a dev shell, and separate checks for the example CMake build and for clang-tidy.

And there is a dependency decision that explains a directory in the tree. The line editor, replxx, is vendored in `third_party/` on purpose, because distributions ship the 2021 release, which lacks the fork's paste, redraw and crash fixes. That is a small but telling detail: the project would rather carry a patched dependency than depend on what the distro ships.

The limits are stated, and the TODO says what is next

The README has a limitations section with three entries, and they are worth reading before you restructure a project around the tool.

It is C++ only. The quick pre-scan only looks at the source file itself, so if the include sits in a header you have to set `INSPECTOR_SCAN_ALL=1`. And if no REPL is listening when the program reaches the include, it prints a warning and continues.

That last one is a deliberate design choice rather than a bug, and it is the right one for a debugging tool. A program that aborts because nobody attached a REPL is a program that breaks in production, and a warning in the log is a much better failure mode. The `INSPECTOR_REQUIRED` environment variable flips that behaviour for the case where you want a hard failure instead, which is the setting to use in a test rather than in a deployment.

The environment variable table lists five knobs in total. `INSPECTOR_CXX` sets the real compiler, `INSPECTOR_INCLUDE_DIR` sets where the generated headers go, defaulting to `./.inspector-includes`, `INSPECTOR_SCAN_ALL` parses every file rather than only those containing the include, `INSPECTOR_NO_LINK` stops the wrapper adding the link flag, and `INSPECTOR_REQUIRED` makes a missing REPL an error.

The TODO section lists three items. Including type declarations into the REPL is not done, so the interpreter works with the variables it has references to rather than with the full set of declarations in scope. A GDB and LLDB plugin is planned, which would preload `libinspector.so` and invoke Inspector with the debug information from the debugger, and that is the obvious next step for a tool that already speaks to a debugger's users. C support is the third item.

So the honest summary is that Inspector today is a C++ tool, that it knows about the variables in the frame it stopped in, and that the debugger integration, which is what many people actually want, is still ahead of it.

Editorial conclusion

Inspector fits a C++ developer debugging a program that only misbehaves at run time, or exploring a code path where a print statement is inconvenient, and who is willing to rebuild with a Clang based toolchain. Skip it if you need C support, if you are working on a header-only codebase where the include would have to live in a header, or if you want debugger integration rather than an interpreter. Verify first that your compiler can be wrapped and that the REPL server is reachable in your environment, and remember that with no server listening the program prints a warning and keeps running.

Frequently asked questions

How do I use the Inspector C++ REPL?

Compile with the wrapper so the include is instrumented, for example `inspector-c++ -std=c++17 main.cpp -o main`, then run `inspector repl` in one terminal and the program in another. Each `#include INSPECTOR` inside a function stops the program and hands over to the REPL, and `.continue` or Ctrl-D resumes it.

How does Inspector capture the local variables?

At build time `inspector` parses each source file with Clang and, for every `#include INSPECTOR`, writes a small header that captures the addresses of all local variables in scope. At run time the program calls into `libinspector`, connects to `inspector repl` over a Unix socket, and starts clang-repl with references to those variables, so expressions are evaluated inside the running program.

What are the build requirements for Inspector?

LLVM/Clang 22 or later including its CMake package, CMake 3.20 or newer with ninja, and GoogleTest when `BUILD_TESTING` is enabled, which is the default. The README notes that released distribution packages of LLVM work, so no LLVM source tree is needed, and that the replxx line editor is vendored in `third_party/` because distributions ship a 2021 release lacking the fork's fixes.

What can I do inside the Inspector REPL?

Expressions print their value, with strings, containers, pairs and any type with `operator<<` shown by content, and a trailing semicolon suppresses the output. The commands are `.locals`, `.where`, `.help` and `.abort`, multi-line input continues while brackets are open, history is saved under `$XDG_STATE_HOME/inspector/history`, and the REPL keeps running between breakpoints and between programs.

What are the limitations of Inspector?

The README lists three: it is C++ only, the quick pre-scan only looks at the source file itself so an include in a header needs `INSPECTOR_SCAN_ALL=1`, and if no REPL is listening when the program reaches the include it prints a warning and continues. The TODO section adds type declarations in the REPL, a GDB and LLDB plugin, and C support as still to come.

Official sources

  1. inspector-repl/inspector on GitHub
  2. Issues
  3. License: MIT
  4. README
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/inspector-repl-inspector.svg)](https://hysenlabs.com/projects/inspector-repl-inspector)

Was this useful?