# reli: sampling and memory profiling of a running PHP process from the outside

> Reli is a standalone PHP CLI that reads the state of a running PHP process from outside it, using the ELF binary, /proc and ptrace through FFI, so call traces, heap graphs and variable inspection work without touching the target script. It fits production debugging where you cannot instrument the code, and it does not fit environments where ptrace is not permitted.

**reliforp/reli-prof** — A sampling profiler or a memory usage profiler for PHP written in PHP, which reads information about running PHP VM from outside the process. You can find performance bottlenecks or memory leaks of your scripts without changing the target script or loading extensions.

- Repository: https://github.com/reliforp/reli-prof
- Stars: 550 · Forks: 17
- Language: PHP
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/reliforp-reli-prof

## Profiling a PHP process without changing it

Reli is a sampling profiler, or a VM state inspector, written in PHP, and the README is explicit about the constraint that shapes it: the tool reads information about a running PHP script from outside the process. That means the target program needs no modification and no extension has to be loaded into it. The described use is for call-trace sampling, which shows where time is spent, memory-graph analysis, which shows where memory is used, runtime variable inspection, and condition-triggered captures. It is a standalone CLI tool, so the operational model is that you point it at a process identifier and it works on a running production instance. The people it is for are PHP developers investigating a bottleneck or a leak in code they either cannot change or do not want to instrument, for example a vendor script, a long-running worker, or a request handler that only misbehaves under load.

## ELF parsing, /proc maps and ptrace through FFI

The mechanism is documented in the README under a how it works section, and it is four steps. Reli parses the ELF binary of the PHP interpreter, reads the target's memory map from /proc/<pid>/maps, reads the memory of the target process through ptrace(2) and process_vm_readv(2) via FFI, and analyses the internal data structures of the PHP VM, also known as the Zend Engine. Parsing the interpreter binary is what makes the tool work across PHP builds without a recompiled extension: the profiler reads the layout rather than assuming it. The consequence is that the profiler needs privileges the target does not have, since ptrace and process_vm_readv are the mechanisms that make the target's memory readable. The README states that this keeps target-side overhead low, which is the reason a sampling profiler in this style can be pointed at a live process at all.

## Capturing a trace to .rbt and analysing it later

The trace workflow is a capture step and an analysis step, and the trace is written to a file with the .rbt extension. The capture command names the target process and the output file:

```bash
$ reli inspector:trace -p <pid> -o trace.rbt
```

The analysis is a separate command, which is what lets a capture happen on one machine and the study happen later. Because it is a sampling profiler, reli takes a snapshot of the target's PHP call stack every ten milliseconds or so, and the analyser ranks frames by how many accumulated snapshots they appear in. The distinction the README draws is between self time, which is time directly in the frame, and total time, which is the frame plus anything it called. At the default sampling rate the README says an hour of trace is rarely more than a few megabytes, so a capture is cheap to leave running. The live view pipes the analysis through watch, refreshing it every 0.2 seconds while samples stream in:

```bash
$ watch -n0.2 'reli rbt:analyze --last --last-depth=10 --top=10 --sections="tail,self+total" --crop-anchor=right --path=short --crop=auto < trace.rbt'
```

The trace file is not a closed format. The documentation lists converters for speedscope, pprof and callgrind, so a captured trace can be opened in other profilers once it is on disk.

## Sandwich, flamegraph and tree views in the terminal

Interactive exploration is a separate command, and it opens a terminal interface with several views of the same capture:

```bash
$ reli rbt:explore trace.rbt
```

The views are described as a sandwich, a flamegraph and a tree, all in a TUI. The linked documentation for that command covers the keymap, filters, an option to show opcodes, mouse support and a live tail, which are the features that matter when a capture is long and you want to filter it rather than scroll it. There is a separate advanced capture document covering opcodes, native frames and JIT, so the tool can see below the level of PHP function calls when needed. That distinction between the default view and the advanced capture is worth knowing before you start, because a bottleneck inside a VM operation rather than inside a userland function will not appear in a plain PHP-level stack.

## Walking the PHP heap as a graph

The memory side of the tool takes a different shape. Reli walks the target's PHP heap into a graph in which every value, including objects, arrays, strings and call frames, is a node, and every reference is an edge. The production-recommended flow is a short stop followed by offline analysis: dump the heap from the target, analyze the dump into the graph format, then work on the result:

```bash
$ reli inspector:memory:dump -p <pid> -o snapshot.rdump
$ reli inspector:memory:analyze snapshot.rdump -f rmem -o snapshot.rmem
```

From there the graph can be rendered as a standalone HTML file, in a Circle Pack, Treemap, Sunburst or 3D Force view:

```bash
$ reli rmem:viz snapshot.rmem
```

There is also a live mode that serves the graph over HTTP and SSE, with a shared focus bus that the terminal interface, browser views and an MCP client all follow in sync, so pressing the focus key in the TUI moves the browser view at the same moment. The README frames this as the way to chase memory leaks and to find which classes consume the most memory. The offline path is the one to prefer on a production host, since it keeps the stop on the target as short as the dump itself.

## Automated memory findings and snapshot comparison

Reli can also do the analysis instead of you. The memory report command scans the captured heap graph for known waste patterns, which the README lists as dominant classes, reference cycles, choke points and deduplication candidates, and prints each finding with a severity, a hypothesis and a next investigation step. The input is the same .rmem file produced by the analyze step:

```bash
$ reli inspector:memory:report snapshot.rmem
```

Two snapshots can be compared to track a regression or confirm a fix, which is the mode that turns the tool into part of a routine rather than a one-off investigation:

```bash
$ reli inspector:memory:compare before.rmem after.rmem
```

The memory documentation also covers a JSON mode, capture options such as excluding the heap and producing portable dumps, and analysis of core files for crashed or post-mortem cases. The reporting is a heuristic list rather than a proof: a severity and a hypothesis tell you where to look next, and the actual confirmation is still the comparison of two captures.

## Container permissions, PHP version floors, and the release record

The operational requirement is the ptrace capability, and the test setup in the repository shows exactly what that means in a container. The compose file adds SYS_PTRACE, sets the container to share the host pid namespace and relaxes the seccomp profile:

```yaml
    pid: "host"
    cap_add:
      - SYS_PTRACE
    security_opt:
      - seccomp:unconfined
```

The same file mounts the Docker socket and a directory for test output, which points at a test harness that starts sibling containers to profile. The production image also tells a story about the host requirements: it is based on php:8.5-cli, installs the FFI extension, and raises the memory limit because the profiler reads the target binary fully into a PHP string, which the Dockerfile explains can exceed the default 128 MB for a phpenv-style or debug-info-bearing build. The README badges state the version floors, PHP 8.4.0 or later for the runner and PHP 7.0 or later for the target. The licence is MIT, the default branch is 0.13.x, and the last push was on 2026-10-17, with tags at 0.12.0 on 2026-04-30, 0.11.4 on 2024-02-14 and 0.11.3 on 2024-01-18.

## Conclusion

Reli fits a team debugging a PHP process it cannot modify, on a host where ptrace and /proc access are available, and it earns its place in production through a short capture followed by offline analysis. It does not fit a locked-down environment that blocks SYS_PTRACE, and it does not fit Windows targets, since the mechanism depends on the ELF binary and Linux process interfaces. Verify first that the target PHP build is 7.0 or later and the runner is 8.4.0 or later, then confirm your container can pass SYS_PTRACE, since that single capability is what makes the whole tool work.

## FAQ

### Do I have to change my PHP script to profile it with reli?

No. The README states that reli is a standalone CLI tool that reads information about a running PHP script from outside the process, so target programs need no modifications and no extension has to be loaded. You point it at a process identifier.

### How do I capture and analyse a call trace with reli?

Capture with reli inspector:trace -p <pid> -o trace.rbt, then open the result with reli rbt:explore for the terminal views or reli rbt:analyze for the ranked hot frames report. The .rbt file can also be converted to speedscope, pprof and callgrind formats.

### How do I find a memory leak in a running PHP process?

The README's recommended flow for production is a short stop: dump the heap with reli inspector:memory:dump, analyze it into the .rmem graph with reli inspector:memory:analyze, then either render it with reli rmem:viz or run reli inspector:memory:report for prioritised findings. Two snapshots can be compared with reli inspector:memory:compare.

### What permissions does reli need on the host?

Reli reads the target process through ptrace and process_vm_readv via FFI, so the environment has to permit that. The test compose setup in the repository adds the SYS_PTRACE capability, uses the host pid namespace and sets seccomp to unconfined, which is the shape of the requirement.

## Sources

- [Issues](https://github.com/reliforp/reli-prof/issues)
- [License: MIT](https://github.com/reliforp/reli-prof/blob/0.13.x/LICENSE)
- [README](https://github.com/reliforp/reli-prof/blob/0.13.x/README.md)
- [Releases](https://github.com/reliforp/reli-prof/releases)
- [reliforp/reli-prof on GitHub](https://github.com/reliforp/reli-prof)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/reliforp-reli-prof
