Inspector injects a fully-functional C++ REPL into running, compiled programs that can access your program state and offers features like code-completion and syntax highlighting. The REPL uses the language standard and include paths the program was compiled with.
The API is similar to tools like Pry in Ruby or Pdb in Python:
Drop #include INSPECTOR anywhere inside a function:
// 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
}$ 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 overPresentation slides on Inspector
10 min video presentation on Inspector
- At build time,
inspectorparses each source file with Clang. For every#include INSPECTORit writes a small header that captures the addresses of all local variables in scope. - At runtime the program calls into
libinspector, which connects toinspector repl(theinspector-replbinary) over a Unix socket. It then starts clang-repl (clang::Interpreter) with references to those variables. Input and completion requests are evaluated inside the program. - Typing
.continue(or Ctrl-D) resumes the program.
Requirements:
- LLVM/Clang >= 22 including its CMake package (
ClangConfig.cmake). Released distro packages work (apt.llvm.org, Homebrew, Arch, nixpkgs); no LLVM source tree or fork is needed. - CMake >= 3.20, ninja
- GoogleTest, only with
BUILD_TESTING(the default)
The line editor, replxx, is vendored
in third_party/ on purpose: distributions ship the 2021 release, which
lacks the fork's paste, redraw and crash fixes.
$ cmake -B build -GNinja
$ ninja -C build
$ ctest --test-dir build # gtest suites, including a pty-driven REPL testThe binaries end up in build/inspector, build/inspector-c++ and
build/inspector-repl. To run the end-to-end tests against another
instrumented program: build/test/inspector-e2e ./program.
If CMake can't find the clang executable, it can't detect the clang resource
dir (builtin headers). Pass it explicitly:
-DCLANG_RESOURCE_DIR=$(clang -print-resource-dir).
$ nix build # package, uses llvmPackages_latest
$ nix run . -- repl
$ nix develop # dev shell with all dependencies
$ nix build .#checks.x86_64-linux.example-cmake # make / meson / package-llvm22
$ nix build .#checks.x86_64-linux.clang-tidy # or: run-clang-tidy -p buildRun inspector repl in one terminal, then start your instrumented program
in another. Pick whichever integration fits your build:
[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'
- Expressions print their value. Strings, containers, pairs and any type with
operator<<are shown by content. A trailing;suppresses the output. - Multi-line input continues while brackets are open.
.locals,.where,.help,.abort;.continueor Ctrl-D resumes the program. Ctrl-C clears the current line.- History is saved in
$XDG_STATE_HOME/inspector/history. - The REPL keeps running between breakpoints and programs; Ctrl-C while waiting exits.
For scripts, each command runs one step and exits:
$ INSPECTOR_WAIT=1 ./main &
$ inspector repl eval 'v.size()'
stopped at main.cpp:12
locals:
v: std::vector<int>
(size_t) 3
$ inspector repl continue
continuedThe first command starts a background server that holds stopped programs
between commands. inspector repl stop ends it. eval waits up to -t
seconds (default 30) for a program, reads code from stdin with - and
prints JSON with --json. Exit codes: 0 ok, 1 error in the code, 2 no
program stopped, 3 program disconnected, 124 timed out.
inspector-c++ wraps the real compiler ($INSPECTOR_CXX, default c++).
It generates the headers for #include INSPECTOR on the fly and links
libinspector:
$ make CXX=inspector-c++ # make / autotools
$ CXX=inspector-c++ meson setup build # meson
$ inspector wrap g++ -std=c++17 main.cpp -o main # explicitEnvironment variables:
| Variable | Meaning |
|---|---|
INSPECTOR_CXX |
real compiler used by inspector-c++ (default c++) |
INSPECTOR_INCLUDE_DIR |
where generated headers go (default ./.inspector-includes) |
INSPECTOR_SCAN_ALL |
parse every file, not only those containing INSPECTOR |
INSPECTOR_NO_LINK |
don't add -linspector when linking |
INSPECTOR_REQUIRED |
exit instead of continuing when no REPL is listening |
INSPECTOR_WAIT |
wait until a REPL listens instead of continuing |
find_package(inspector REQUIRED)
add_executable(app main.cpp)
inspector_enable(app) # wraps compiles of `app`, links inspector::inspectorpkg-config --cflags --libs inspector provides the include and link flags.
Generating the headers still needs inspector-c++ or inspector prebuild.
$ 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 mainSee examples/ for working setups; nix flake check builds each one and runs
inspector-e2e against it.
- C++ only.
- The quick pre-scan only looks at the source file itself. If
#include INSPECTORsits in a header, setINSPECTOR_SCAN_ALL=1. - If no REPL is listening when the program reaches
INSPECTOR, it prints a warning and continues.
- include type declarations into the repl
- GDB/LLDB plugin: preload libinspector.so and invoke inspector with debug information from gdb/lldb
- C support
