Tells you what happened.
Cannot modify arguments, override returns, or inject failures. Observe-only.
strace observes. Frida mutates. libFuzzer fuzzes. Each is excellent at its verb and silent on the other two. retrace is the first tool that does all three — observe, mutate, break — on any dynamically-linked binary, without source, without recompiling, on thirteen platforms.
Tells you what happened.
Cannot modify arguments, override returns, or inject failures. Observe-only.
Lets you script it.
Requires JavaScript. 40 MB runtime. Heavyweight for simple interception.
Finds bugs in your own code.
Requires source and recompilation. Cannot target binaries you didn't build.
One ~200 KB shared library. Twelve built-in actions. JSON config you can write in five minutes. Runs on the binary you already have.
retrace preloads a shared library into the target process. That's the same mechanism that malware uses to inject code. You should be skeptical. Here's what we have to say about it.
Works on Linux (glibc + musl + OHOS), macOS, FreeBSD/OpenBSD/NetBSD, Windows, Android.
PATH and libretrace in your loader path. About five seconds.# detect OS + arch, fetch the right binary$ curl -sSL .../install.sh | sh retrace 2.2.2 installed
--html for an interactive report. No source. No recompile. No config file.$ retrace trace malloc,free \ --html -- /bin/ls wrote /tmp/retrace-43892.html
$ open /tmp/retrace-43892.html 1,247 calls · 48.3 ms · 42 functions [ I/O ] 485 calls 32.1 ms (66%) [ MEM ] 412 calls 8.3 ms (17%)
Every output below mirrors what the real retrace binary produces. Use this to learn the command syntax and the output shape before you install. When you're ready to run it on your own binary, the install takes about five seconds.
retrace preloads a small (~200 KB) shared library into the target process. Every libc call routes through a single assembly trampoline per function. You decide — per call, per script — what happens at each of the three taps.
log_params emits args as JSON.call_real lets it proceed.modify_in_param_str, modify_in_param_int,modify_return_value_int.memory_fuzz, incomplete_io, delay,call_count_limit, sandbox.The honest answer: single-digit microseconds per call, plus JSON serialization proportional to argument size. Most programs see 5–15% wall-clock overhead under default logging. You can dial that down to near-zero if you only want the side effects (mock, sandbox, fault injection) and don't need the log.
Assembly trampoline → C engine → JSON serialization. No interpreter in the hot loop. Most of the cost is the log line itself, not the interception.
Under default logging (log_params + call_real for every function). Drops sharply when you restrict to a function allowlist.
Engine + parson + per-thread state is small. Log file grows linearly with intercepted calls; rotate or pipe to /dev/null for long runs.
Statically linked assembly trampolines + engine + parson. No third-party C libraries. No JavaScript. No Python.
RETRACE_LOGGER_ALLOWED_FUNCSComma-separated list of function names to log. The engine still intercepts every function (so call_real, modify_*, etc. still work), but the JSON log only emits lines for the allowlisted ones. Biggest single win.
RETRACE_LOGGER_DEF_STDOUT_ENA=0Pair with --log FILE. stdout mirroring has its own serialization cost; if you only need the file, disable mirroring.
(config)If you're using retrace to mutate (modify_in_param_*, modify_return_value_int) but don't care about observation, omit log_params from the action list. The engine still runs but emits no log line for that call.
RETRACE_LOGGER_DEF_ENA=0Interception still runs (so sandbox, mock, fuzz all still work), but no log output anywhere. Use when you only want the side effects.
Each function in your config carries an ordered list of actions. Add a new behavior by dropping one.c file into src/core/actions/ — no engine change, no recompile of the binary you're tracing.
retrace ships with prototypes for nearly three hundred libc functions out of the box. Below is a curated tour of the most-traced, grouped by what they're for. Click any function to see its signature and the one-liner to trace it. Use retrace list-functionsfor the full list.
The hot path for most programs. Trace these to see what a binary actually reads or writes.
Allocators and bulk byte operations. The first stop for memory-leak and OOM investigations.
Fork, exec, system. Audit these for command injection, sandbox escape, and unexpected subprocesses.
Read the clock or sleep. Mock these to freeze time, replay schedules, or test expiry.
Who am I? Mock these to make a binary think it's root, in a different login session, etc.
Paths the binary touches. Pair with the sandbox action to deny sensitive locations.
Locks, condition variables, rwlocks. Profile these to find contention and deadlocks.
Env var reads. Audit these to find a binary sniffing for secrets, debug flags, or backdoors.
Character classification and bulk byte ops. Often surprisingly hot.
Plugins, drivers, runtime-loaded libraries. Audit these to find supply-chain attack surface.
Looking for socket, connect, send, recv? Network functions are not yet in the default intercept set — tracked inissues. Until they ship, usestrace for raw syscall-level network observation, or write a custom action.
retrace is small enough to slip into a one-off debugging session, generic enough to anchor a CI fuzzing pipeline, principled enough to back a security audit. Below: every audience that reaches for it, and the command they reach for first.
Reverse-engineer a binary you don't have source for. Map every open, every connect, every system call — in seconds, without a disassembler.
$ retrace trace open,connect,system -- ./suspiciousFind error-path bugs without writing fixtures. Inject OOM, short I/O, and latency directly into your test runs in CI.
$ retrace fuzz malloc --rate 0.1 -- ./serverFind the hot path. See per-call timings on every libc call. Redirect a file open to a stub without rebuilding.
$ retrace trace --html -- ./api-serverTrace any containerized binary in one line. Drop the library into your image; no recompile, no restart, no APM agent.
$ RUN curl -sSL .../libretrace-linux.so -o /usr/lib/libretrace.soSandbox an untrusted binary at runtime. Block /etc/shadow, /root/.ssh/, network egress — without SELinux or AppArmor.
$ retrace run --config sandbox.json -- ./untrustedSee exactly what ls, cat, grep actually do. The trace is the lesson — every libc call, in order, with timings.
$ retrace trace --html -- /bin/lsSolve tracing challenges fast. Watch the binary leak the flag through write, find the comparison via strcmp, time the side channel.
$ retrace trace strcmp,write -- ./challengeUnderstand a closed-source binary's behavior without firing up IDA. Trace the calls; reconstruct the logic from observable I/O.
$ retrace trace -- ./closed-source-binaryPost-incident analysis: replay a captured trace to see exactly what a process did — files touched, sockets opened, env vars read.
$ retrace pp /tmp/trace.jsonFind vulns by exhausting resources. Make the 1000th malloc fail, the 50th open return ENOENT, see if the program survives.
$ retrace run --config limit-then-fail.json -- ./targetTrace any Android app's native libraries via wrap.sh or Magisk. Cross-compile once with the NDK; same JSON config as your desktop.
$ LD_PRELOAD=libretrace.so /data/local/tmp/wrap.shDiagnose why a service is misbehaving without restarting it. Trace a single command's libc activity to see what's really being read, written, connected to.
$ retrace trace --html -- $(which dig) example.comVerify a binary uses the right crypto primitives. Trace RAND_bytes, EVP_DigestInit, SSL_CTX_set_verify — see every algorithm and parameter.
$ retrace trace RAND_bytes,EVP_* -- ./crypto-appStress-test firmware error paths. Inject short reads on flash, ENOSPC on write, timeouts on i2c-sysfs — without desoldering a thing.
$ retrace run --config incomplete-io.json -- ./firmware-testConfirm your library's ABI surface in practice. Trace which functions downstream callers actually invoke, with what arguments, and how often.
$ retrace trace 'mylib_*' -- ./downstream-appTest defensive controls end-to-end. Sandbox your own dropper, fuzz its malloc paths to find crash points defenders could exploit, verify EDR detection logic triggers.
$ retrace run --config sandbox-and-fuzz.json -- ./dropperCapture a malicious binary's libc activity in a controlled sandbox. Re-run the captured trace to build an evidence timeline without giving the binary network access.
$ retrace run --config sandbox.json --log /tmp/evidence.json -- ./seized-binaryQuantify libc overhead before optimizing. See per-function totals across the whole run, then zoom into the top 1% of calls by duration.
$ retrace pp /tmp/trace.json | head -10Hunt for zero-days by exhausting every boundary. Make the 1000th call fail, the buffer exactly full, the path exactly too long. See what breaks.
$ retrace run --config edge-cases.json -- ./targetTrace native game libraries. Find the asset load that's slow, the audio callback that allocates, the render path that reads from disk mid-frame.
$ retrace trace fopen,read,mmap -- ./game-binaryValidate your libc intercept before shipping. Trace every call to a target function across all your test workloads; flag any unexpected arg shape.
$ retrace trace 'your_lib_*' --log ./baseline.json -- ./test-suiteWire fault-injection into CI. Run the test suite under memory_fuzz on every PR; catch OOM-as-bug before users do.
$ retrace fuzz malloc --rate 0.05 -- ./run-testsTrace a DB client's libc surface to find the slow query path. See which read/write syscalls each query triggers, and where time goes.
$ retrace trace read,write,fsync --log /tmp/db.json -- ./db-clientReproduce a flaky consensus vote by mocking time, fuzzing malloc, and forcing fsync to fail. The bug only happens under specific conditions — retrace makes them deterministic.
$ retrace run --config consensus-repro.json -- ./raft-nodeDebug a Python/C++ native binding. Trace the interpreter's libc calls to find which model-loading step is bottlenecked on disk I/O vs. computation.
$ retrace trace fopen,read,mmap -- python3 train.pyTwenty-two tutorials is a lot to navigate. Below are six pre-defined sequences through them, each chosen for a specific role or goal. Click a chip to see the sequence — every step links into the matching tutorial below.
Three tutorials that take you from zero to your first HTML trace report.
New to retrace? Start here.
sourceBuild retrace from source so you have the latest. (Or skip to step 2 if you curl-installed.)
slowRun your first trace, generate the HTML report, see the category breakdown.
oomNow break something. Inject 10% OOM into malloc and watch your binary's error paths.
Answer three questions and we'll generate the right config + command for your use case.
Every newcomer's question is the same: "I have this problem — how do I use retrace to solve it?"Twenty-two scenarios below cover the questions users actually ask. Click yours on the left; the steps appear on the right with copy-paste commands and expected output. No prior retrace knowledge assumed.
Trace every libc call with timings, then sort by total time.
Install retrace if you haven't already.
curl -sSL https://raw.githubusercontent.com/riboseinc/retrace/main/scripts/install.sh | sh
retrace 2.2.2 installed
Run your program under retrace with HTML output. The --html flag generates a self-contained interactive page.
retrace trace --html --log /tmp/trace.json -- ./your-program
wrote /tmp/retrace-43892.html
Open the report.
open /tmp/retrace-43892.html
(browser opens with summary cards + filterable table)
Look at the category breakdown. The biggest category by total time is your suspect. Click any function to filter the table.
[ I/O ] 485 calls 32.1 ms (66%) [ MEM ] 412 calls 8.3 ms (17%) ...
For a CLI-only view of per-function totals, pretty-print the JSON log:
retrace pp /tmp/trace.json | head -10
open 48 calls 12.4ms total read 124 calls 8.7ms total ...
Looking for something else? The cookbookhas 21 more recipe-style walkthroughs — each is a single JSON config plus the command to run it.
Per-platform assembly trampolines behind a uniform engine. The JSON config you write on your Mac runs unchanged on your Android device, your FreeBSD CI runner, and your Windows Server.
Same engine, different trampolines. ELF, Mach-O, and PE binaries each get a from-scratch assembly trampoline — no MinHook, no Detours, BSD-2 clean.
Binary releases for every platform are attached to eachGitHub release. The install script picks the right one.
Docker image atghcr.io/riboseinc/retrace:latest — drop into any container with a one-lineRUN.
The existing tools each do part of the job. strace observes but cannot mutate. Frida mutates but requires JavaScript. libFuzzer mutates but requires recompilation. retrace is the only one that does all three on a binary you didn't build.
| Capability | retrace | strace | Frida | libFuzzer |
|---|---|---|---|---|
| Observe libc calls | ✓ | ✓ | ✓ | — |
| Modify arguments | ✓ | — | ✓ | — |
| Override return value | ✓ | — | ✓ | — |
| Fault injection (OOM, short I/O) | built-in | — | partial | ✓ |
| No source code needed | ✓ | ✓ | — | — |
| No recompilation | ✓ | ✓ | ✓ | — |
| Cross-platform | 13 platforms | — | partial | partial |
| Binary size | ~200 KB | preinstalled | ~40 MB | linked in |
| Declarative config | JSON / CLI | — | — | — |
Each recipe is self-contained: copy the JSON, run the command, see the result. Browse thefull cookbookfor all twenty.
Inject OOM at 10% to find leak paths and unhandled NULL returns.
Block /etc/shadow, /root/.ssh/, and friends — without SELinux or AppArmor.
Drop-in GitHub Action that catches OOM and short-IO bugs on every PR.
Make a binary think it's root (or any uid) without actually being root.
Visually compose a config: pick a function, drag actions from the palette, tune params inline, copy the generated JSON into the validator.
Use * for wildcard. Common:
Drag actions here to add, or click:
{
"intercept_scripts": [
{
"func_name": "malloc",
"actions": [
{
"action_name": "log_params"
},
{
"action_name": "call_real"
}
]
}
]
} Paste into the Config Validator to verify, then save as /tmp/recipe.json.
The browser equivalent of retrace validate. JSON parses in your page; nothing is sent anywhere. Five pre-baked examples below the input let you see what valid (and invalid) configs look like.
config.json and launch with retrace run --config config.json -- ./your-binary. retrace has shipped continuously since 2017. The list below is what landed in the latest release and what's queued for the next two. Starthe repo or watchreleases to follow along.
The reap doctrine: a SIGCHLD self-pipe routes every spawned workload's death to the daemon's poll loop, which reaps and journals retrace.ctl.exit {pid, how, code}. A specimen that crashes, is killed, or leaves on its own is never a silent gap in the audit trail -- and never a zombie either.
The control plane's verbs were four hand-synced edits (dispatch chain, scope gate, CLI builder, usage text -- which had already drifted). Now one X-macro list is the SSOT: the daemon's dispatch table, every TLS claim scope, and the CLI's own usage derive from it. A verb is one line plus one handler. The extraction also caught a real defect: every events reply was truncating at 64 bytes.
One command forks a workload armed to join the daemon itself -- supervisor env, the nonce, an eager agent, the caller's preload. The launch is journaled before the child can act, and the child takes a full seat: never a spectator, because the nonce traveled with the fork. Launch, life, and kill now all ride the control plane.
The sessions walker goes recursive: worker trees and fork-bomb detonations chain past any fixed scan level, and depth stops lying at level 4. The tree the registry carries is the tree the CLI prints, at every depth.
The prototypes the very first upstream PR (#414) asked for, on today's rails: memory maps are now first-class intercepted calls -- arguments, returns, and the ability to fail or fuzz them like every other boundary crossing.
The journal's tail over the control plane, with the hash-chain verdict riding the reply -- evidence pulled over a network carries its own integrity statement, and a tampered line names its own line number. STATUS-scoped by design: an auditor reads evidence without touching even the registry. The fleet CLI is now control AND evidence.
retrace-ctl sessions prints the tree the registry always carried -- session tokens, fork parents, spectator seats -- nested instead of flat. Detonation-farm operators stop re-nesting forks by eye across JSON lines.
Fault injection could exhaust a resource but never recover from one. fail_first fails the first N calls with a chosen error (the real call never made), then lets every later call through -- a correct retry loop converges on call N+1, an untested one hangs where retrace can see it.
The Node runtime agent -- diagnostics_channel for the runtime's own boundary, UDS and named-pipe transports, the same supervise/emit surface as its Python and JVM siblings. Three independent implementations, three hook systems, one protocol: the conformance claim's strongest evidence.
CPack now rides the install surface: every tarball and zip carries bin/ -- retraced, the fleet CLI, enforcement, all the converters (they were missing from releases entirely before) -- and the Linux legs ship dpkg-validated .deb packages. RPM is configured for source builds.
One frame codec (the Windows arm adopted the shared encoder and its payload cap, closing a silent oversized-event loss), one policy validation ladder in policy_sig.c (the Windows copy had lost the expiry guard), and one pure event ring with drop accounting on both halves.
The fleet CLI's policy push now reaches Windows pipe agents for the first time -- the broadcast sends through an installed sink over transport-opaque handles, and the codebase's last extern-as-interface retired.
The named-pipe daemon, fleet CLI, and all four agents (libc, kernel-ETW, Python, JVM) now build, link, and pass their integration tests on Windows -- including the SCM service lane (sc create/start/stop with graceful journal flush) and a configure-time conformance guard over the everywhere-build set. Sixteen defects that had shipped dark were burned off in one activation arc.
pyretrace and jretrace attribute file opens, sockets, and execs to the module and line that issued them -- the layer a libc interposer sees only as an open from pid N. Both are third-party implementations pinned to the same conformance suite as the reference stub.
eBPF and ETW bridge agents join every session as kernel-lane spectators; the daemon grades kernel observations against libc claims per sweep and streams drift summaries -- the correlate oracle's signal, live.
One declared-set, four enforcement planes: Landlock, seccomp, Seatbelt, and AppContainer -- each exec bound to a hash-chained, Ed25519-signed audit trail that fails closed on tamper.
POLICY_SET ships as an Ed25519-signed wrapper over the exact policy bytes; the daemon pins multiple trust keys so rotation overlaps instead of breaking the fleet.
retraced: per-host registry, hash-chained journal, formally-versioned control protocol, and the nonce doctrine -- nonceless HELLOs seat as spectators: evidence always, policy never.
RETRACE_OTLP_ENDPOINT streams OpenTelemetry spans live from inside the traced process -- one env var, no batch pipeline. Spans, security events, and metrics land in Grafana/Jaeger/otelcol as calls happen.
Jail denials, fuzz crash clusters, and drift-oracle hits emit as OTLP log records with a documented schema -- policy violations watchable live during detonations.
/MT binaries carry their own CRT -- no ucrtbase to hook. Their file access is now observed and jailed through the ntdll layer, proven by a true static-CRT CI smoke.
Raw ETW events captured on CI runners and converted with the OS's own provider manifests -- the numeric Id-to-task table is transcribed from Get-WinEvent, not docs.
strace, dtrace, truss, ktrace, procmon, and ETW traces all convert to the retrace shape -- five platforms of kernel-layer ground truth for grading libc claims.
Crash clustering, minimized corpora, per-cluster reproducers, a drift oracle (behavior the baseline never saw), and dictionary grammar templates -- one command, one report.
retrace-profile jail emits a ready-to-run sandbox config; the sandbox action enforces it at runtime with deny/allow lists, decoy dirs, and env visibility.
Every backend's function inventory is now conformance-tested against the shared list -- the class of 'silently missing 28 functions on one arch' bug is a red check, not a coverage hole.
The eBPF agent runs --synthetic in the matrix; the real loader needs a BPF-capable (self-hosted) runner leg so kernel-lane captures are exercised on every PR, not only locally.
The recipe set covers the classic, security-research, and resilience flows (fail_first's retry-path recipe landed with v2.71); what's missing is user-contributed war stories.
The .deb ships from the releases (dpkg-validated in CI) and cpack builds rpm from source; the remaining channels are a Homebrew tap and an rpm repository so package managers see updates.
retrace is BSD-2-Clause open source. The three doors below cover the common reasons people come back after their first successful trace. Pick the one that matches your situation.
PRs welcome. Bug fixes, new actions, new backend ports, docs improvements — all of it counts.
Browse good first issues→Q&A, design ideas, use-case walkthroughs. GitHub Discussions is the canonical channel.
Open a discussion→Ribose offers support contracts, training, and custom development around retrace and its ecosystem.
Contact Ribose→Eight years of issues, PR reviews, and conference hallway-track conversations have distilled into these twelve. If yours isn't here, open anissue.
Yes, as long as the program compiles to a native binary. C, C++, Rust, Go (with cgo enabled or on Linux where Go's runtime uses libc for some syscalls), Swift, Zig, D, Pascal — all work. Pure managed runtimes (JIT-compiled Java, JavaScript, pure-Go without cgo) make fewer libc calls and are less useful to trace, but the libc calls they do make are still captured. Interpreters (Python, Ruby, Node) — retrace traces the interpreter's own libc activity, which often reveals what the script is doing.
Yes. retrace intercepts at the libc symbol level, not the binary's own symbols. A fully stripped binary still calls libc functions by name; those are the symbols retrace hooks. You don't need debug info, source, or symbols in the target.
Yes. Position-Independent Executables and ASLR don't affect retrace — interposition happens at the dynamic linker level, before the binary's load address matters. The same goes for stacked/relro/fortify hardening flags.
Single-digit microseconds per intercepted call, plus JSON serialization proportional to argument size. Most programs see wall-clock overhead of 5–15% under default logging. Crank RETRACE_LOGGER_ALLOWED_FUNCS down to the functions you care about and overhead drops sharply. The fork+engine path is assembly; there is no interpreter in the hot loop.
Yes, with care. Logs may contain argument values (file paths, buffer contents, environment variable names) — scrub or redact before persisting. For long-running processes, log to a file with rotation, not stdout. The sandbox and mock actions are stateless and safe; the fuzzing actions are not — they will crash your program by design.
retrace itself doesn't modify the target binary — it preloads a library and intercepts calls at runtime. The target runs with its normal privileges under your normal user. The untrusted-binary workflow (sandbox action, deny-list) is specifically designed for running code you don't trust; see cookbook recipe 20.
Different layers. eBPF runs in kernel context, observes syscalls, and can't (portably) modify arguments or skip calls. retrace runs in userspace, observes libc calls, and can mutate arguments, override return values, and inject failures. They're complementary — eBPF for system-wide observability, retrace for per-process control. eBPF is Linux-only; retrace works on 13 platforms.
strace observes syscalls (kernel boundary) and writes them as text. It cannot modify arguments, override returns, or inject failures. retrace observes libc calls (userspace boundary), can mutate them, runs on more platforms, and produces structured JSON plus interactive HTML. Use strace for 'what is this binary doing right now?' — use retrace for 'what does this binary do under failure conditions I control?'
Yes. Add one .c file under src/core/actions/ that registers itself via the RETRACE_ACTION_REGISTER macro. The action receives the thread context (parsed args, return value pointer) and the JSON action_params. No engine change needed. See src/core/actions/basic.c for the pattern.
Apple's System Integrity Protection blocks DYLD_INSERT_LIBRARIES for binaries in /usr/bin and similar protected paths. Either copy the target to /tmp/ first, or disable SIP via csrutil (not recommended for daily use). Third-party binaries in /Applications or your home directory are unaffected.
Within a major version, yes. New fields may be added (parse defensively); existing fields are not renamed or removed. See the configuration reference for the schema. The interactive HTML viewer handles whatever fields are present.
Cite the repository: riboseinc/retrace, version 2.2.2, BSD-2-Clause license, available at https://github.com/riboseinc/retrace. There is no formal paper yet; if you'd like one, open an issue.
retrace has its own vocabulary — trampolines, wrappers, frames, scripts, prototypes. The definitions below are the canonical reference. Cross-references point to related terms.
What retrace does to a libc call. The call routes through retrace's wrapper before reaching libc, so retrace can observe, modify, or fail it.
A small piece of assembly that 'bounces' a libc call into retrace's engine. One per intercepted function. Lives in src/backends/preload_*/<arch>/arch_spec_top.S.
See also: Wrapper, Frame
The retrace-defined symbol that replaces a libc function in the target's symbol table. Caller invokes the wrapper; wrapper runs the engine; engine decides whether to invoke the real libc function.
See also: Engine, Real impl
The C function (retrace_engine_wrapper) that receives every wrapped call. Looks up the matching intercept script, parses args, runs the script's actions in order, and synthesizes the return value.
See also: Action, Script
A C struct (WrapperSystemVFrame or WrapperAArch64Frame) that captures the call's register arguments. The engine reads args from the frame and writes the return value back into it.
The actual libc function (e.g., the real malloc). retrace resolves these once at init via dlsym(RTLD_NEXT, ...) and stores them in a function-pointer struct. call_real invokes the matching pointer.
See also: Real-impl indirection
The pattern where every libc call inside retrace itself goes through the retrace_real_impls struct, never direct. This is the reentrancy guard — bypass it and you recurse.
A JSON object binding a function name (or wildcard) to an ordered list of actions. The unit of configuration.
See also: Action
A named primitive that runs when its script matches. log_params, call_real, modify_in_param_*, modify_return_value_int, memory_fuzz, incomplete_io, delay, call_count_limit, sandbox, fuzzing_seed. New actions = one .c file, no engine change.
See also: Script
A C struct (struct FuncPrototype) describing a function's name, calling convention, return type, and parameter types. Lives in src/core/prototypes/. The engine uses prototypes to parse args from the frame.
A file containing one or more intercept scripts. Loaded at startup via RETRACE_JSON_CONFIG. Default config (no file) is log_params + call_real for every function.
The JSON-lines output stream. One object per intercepted call, with function name, arguments, return value, and call_duration_us. View with retrace pp (text) or retrace html (interactive).
Setting LD_PRELOAD (ELF), DYLD_INSERT_LIBRARIES (Darwin), or in-process inline hooking (Windows) so the dynamic linker resolves libc symbols to retrace's wrappers instead of the real ones.
A platform-specific implementation of the interposition mechanism. preload_elf, preload_macho, preload_bsd, preload_msvc, preload_mingw, ptrace. Each backend self-registers via a constructor-section scan.
See also: ptrace
The Linux kernel debugging primitive. retrace's ptrace backend uses it to intercept calls in statically-linked binaries that LD_PRELOAD can't reach. Slower than preload; reserved for that case.
Application Binary Interface — the register/stack convention for passing arguments. Sys V x86-64, AAPCS64, Microsoft x64. retrace's trampolines are per-ABI.
The AArch64 Procedure Call Standard. Integer args in x0–x7, FP args in v0–v7, return in x0 (or v0). Variadic args spill to the stack on Darwin but stay in registers on Linux.
Screenshot this. Print it. Tape it to your monitor. Everything below is also in theCLI referenceand configuration reference, but here it is at a glance.
trace [funcs]Log calls (optionally --html)mock <fn> <ret>Override a return valuefuzz [<fn>] --rate RInject OOM at rate Rslow <fn> --ms NInject N ms latencyrun --config FJSON config-drivenpp <log.json>Pretty-print a tracehtml <log.json>Convert log to HTMLlist-functionsEnumerate interceptablelist-actionsEnumerate actionsvalidate <cfg.json>Check a configlog_params(omit_params)call_real—fuzzing_seedseedmodify_in_param_strparam_name, new_str, match_str?modify_in_param_intparam_name, new_int, match_int?modify_in_param_arrparam_name, new_arr, match_arr?modify_return_value_intretval_intdelaymsmemory_fuzzfail_rateincomplete_ioratecall_count_limitlimitsandboxdeny_pathsRETRACE_JSON_CONFIGPath to JSON configRETRACE_LIBOverride library pathRETRACE_LOGGER_DEF_FNLog file pathRETRACE_LOGGER_DEF_ENA0 = disable loggingRETRACE_LOGGER_DEF_STDOUT_ENA0 = suppress stdoutRETRACE_LOGGER_ALLOWED_FUNCSComma allowlistRETRACE_LOGGER_EXCLUDED_FUNCSComma denylist{
"func_name": "*",
"actions": [
{ "action_name": "log_params" },
{ "action_name": "call_real" }
]
}{
"func_name": "open",
"actions": [
{ "action_name": "modify_in_param_str",
"action_params": {
"param_name": "path",
"new_str": "/tmp/fake"
} },
{ "action_name": "call_real" }
]
}{
"func_name": "malloc",
"actions": [
{ "action_name": "modify_return_value_int",
"action_params": { "retval_int": 0 } },
{ "action_name": "call_count_limit",
"action_params": { "limit": 5 } },
{ "action_name": "call_real" }
]
}