# Monitoring and profiling

> Record JFR files, dump the heap, watch a running Elide process with jcmd, jps and jstat, and profile with Linux perf using the instrument flags.

The instrument flags record what an Elide process does while it runs.

| Question | Capture | Inspect with |
| --- | --- | --- |
| What happened during the run? | `--jfr=run.jfr` | JDK `jfr` |
| What is in the heap? | `--heap-dump=heap.hprof` | VisualVM |
| How does the heap behave over time? | `--vmstat` | `jps`, `jstat` |
| What is a running process doing? | `jcmd PID` | JFR, heap dumps, thread stacks |
| Where does compiled code spend CPU time? | `--jitdump=DIR` | Linux `perf` |

List the flags with `elide help instruments`. They apply to `run` and to scripts, snippets and modules. Put them before
any `--` or `-m MODULE`. A JDK 25 install provides `jfr`, `jcmd`, `jps` and `jstat`;
[VisualVM](https://visualvm.github.io/download.html) reads heap dumps. For coverage, CPU tracing and debuggers, see
[Debugging](https://elide.help/docs/debugging). To export traces, metrics, and logs to an observability backend, see
[OpenTelemetry](https://elide.help/docs/opentelemetry).

## Record with JFR

```bash
elide --jfr=run.jfr run app.js
jfr summary run.jfr
jfr print --events 'dev.elide.*' run.jfr
```

`--jfr` uses the `profile` settings and saves on normal exit. `--jfr-settings=default` or a `.jfc` path picks other
settings. File names and settings cannot contain commas or double quotes.

| Event | Records |
| --- | --- |
| `dev.elide.Boot` | Startup finished; `elapsedNanos` is the startup time. |
| `dev.elide.GuestStart` / `dev.elide.GuestStop` | Entry program run, with language, duration, outcome, exit code. |
| `dev.elide.HttpRequest` / `dev.elide.HttpResponse` | One served request, with method, protocol, status, duration. |
| `dev.elide.HttpClientExchange` | One outgoing request, with queue and connect time, protocol, status and body bytes. |
| `dev.elide.TransportTlsHandshake` | One TLS handshake, with duration, role, outcome and ALPN. |
| `dev.elide.Execution` | The command finished, with its exit code. |

URLs, headers and payloads are not recorded.

## Dump the heap

```bash
elide --heap-dump=heap.hprof run app.js
elide --heap-dump-on-oom=oom.hprof run app.js
visualvm --openfile heap.hprof
```

`--heap-dump` writes on normal exit. `--heap-dump-on-oom` writes on an out-of-memory error, then exits without running
shutdown hooks.

## Watch a running process

```bash
elide --vmstat run app.js &
jps -l
jstat -gc PID 1s
```

`--vmstat` lists the process in `jps` (by PID, with no name) and lets `jstat` sample it. `jcmd` attaches by PID with or
without `--vmstat`, on Linux and macOS, as the same OS user:

```bash
jcmd PID help
jcmd PID JFR.start name=profile settings=profile
jcmd PID JFR.dump name=profile filename=live.jfr
jcmd PID GC.heap_dump live.hprof
jcmd PID Thread.print
```

A killed process does not write its captures. Dump a live recording with `jcmd` before you stop it.

## Profile with perf on Linux

```bash
perf record -k 1 --call-graph fp -o perf.data -- elide --jitdump=./jitdump run app.js
perf inject -j -i perf.data -o perf.jit.data
perf report -i perf.jit.data
```

`--jitdump` works in Linux release builds. Keep the `jitdump` directory until `perf inject` finishes. Frequently run
functions appear under `jitted-` objects. A zero exit does not prove a valid capture; check that the report shows your
functions. On WSL2, `perf` must match the running kernel.

## VM options

Each flag has a VM option spelling:

```text
--jfr=run.jfr                 -XX:StartFlightRecording=filename=run.jfr,settings=profile,dumponexit=true
--heap-dump-on-oom=oom.hprof  -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=oom.hprof -XX:+ExitOnOutOfMemoryError
--vmstat                      -XX:+UsePerfData
--jitdump=DIR                 -XX:+RuntimeJitdump -XX:RuntimeJitdumpDir=DIR
```

`-XX:+DumpHeapAndExit -XX:HeapDumpPath=initial.hprof` dumps the initial heap, then exits. `-Xmx` and
`-XX:MaxHeapSize` are the same setting as `--max-mem`; passing both is an error.
