pgr
A Linux utility to graph resource usage of a process and its subprocesses over
time. It uses procfs to record memory, CPU, I/O, thread, and file descriptor
metrics for a process tree, outputting timestamped snapshots in JSON Lines
format. Recordings can be visualized as terminal line charts or summary tables.
Requirements
- Linux kernel >= 3.5 (uses
/proc/[pid]/task/[tid]/children for subprocess
discovery)
- Go 1.26+
Installation
go install github.com/supakeen/pgr/cmd/pgr@latest
Or build from source:
git clone https://github.com/supakeen/pgr.git
cd pgr
go build ./cmd/pgr
Usage
Recording
Record resource usage of a process and all its descendants:
pgr record --pid <PID>
Options:
| Flag |
Short |
Default |
Description |
--pid |
-p |
|
Root process ID to monitor (required) |
--interval |
-i |
1s |
Sampling interval (Go duration) |
--output |
-o |
stdout |
Output file path |
--long-names |
-l |
false |
Use full command name from /proc/[pid]/cmdline instead of the kernel 15-char comm name |
Examples:
# Record PID 1234 every second to stdout
pgr record -p 1234
# Record every 500ms to a file
pgr record -p 1234 -i 500ms -o recording.jsonl
# Record with full command names
pgr record -p 1234 -l
# Record every 5 seconds
pgr record -p 1234 -i 5s
Stop recording with Ctrl-C (SIGINT) or SIGTERM. Recording also stops
automatically when the root process exits.
Output is JSON Lines — one JSON object per line,
one line per sample. Each sample contains a timestamp and resource stats for
every process in the tree at that point in time:
{
"timestamp": "2026-05-15T10:30:00.123Z",
"processes": [
{
"pid": 1234,
"ppid": 1233,
"name": "myapp",
"vm_size_kb": 67890,
"vm_rss_kb": 12345,
"shared_kb": 1000,
"swap_kb": 0,
"utime_ticks": 500,
"stime_ticks": 200,
"read_bytes": 4096000,
"write_bytes": 2048000,
"threads": 4,
"fd_count": 12
}
]
}
| Field |
Source |
Description |
vm_size_kb |
/proc/[pid]/status |
Virtual memory size (kB) |
vm_rss_kb |
/proc/[pid]/status |
Resident set size — physical memory (kB) |
shared_kb |
/proc/[pid]/status |
Shared memory / RssShmem (kB) |
swap_kb |
/proc/[pid]/status |
Swapped memory (kB) |
utime_ticks |
/proc/[pid]/stat |
CPU time spent in user mode (clock ticks) |
stime_ticks |
/proc/[pid]/stat |
CPU time spent in kernel mode (clock ticks) |
read_bytes |
/proc/[pid]/io |
Bytes read from storage |
write_bytes |
/proc/[pid]/io |
Bytes written to storage |
threads |
/proc/[pid]/status |
Number of threads |
fd_count |
/proc/[pid]/fd/ |
Number of open file descriptors |
Note: /proc/[pid]/io requires the reader to be the same user as the
process (or have CAP_SYS_PTRACE). If unreadable, read_bytes and
write_bytes will be 0.
Drawing charts
Render a recording as a terminal line chart:
pgr draw <file>
pgr draw - # read from stdin (live mode)
Options:
| Flag |
Short |
Default |
Description |
--metric |
-m |
rss |
Metric to plot (see below) |
--no-total |
|
false |
Hide the aggregate total line |
--no-per-process |
|
false |
Hide individual process lines |
--height |
-H |
auto |
Chart height in terminal rows |
--human-readable |
-r |
false |
Show y-axis values in human-readable format |
--top |
-n |
0 (all) |
Show only the top N processes by peak usage |
--group |
-g |
false |
Group processes by name (sum values for same-name PIDs) |
--timespan |
-t |
0 (all) |
Only show the last duration of data (e.g. 30s, 5m) |
Examples:
# Draw RSS chart from a file
pgr draw recording.jsonl
# Live chart piped from record
pgr record -p 1234 | pgr draw -
# CPU ticks chart with human-readable axis, top 5 processes
pgr draw recording.jsonl -m cpu -r -n 5
# I/O bytes chart, grouped by process name
pgr draw recording.jsonl -m io -r -g
# Thread count over time
pgr draw recording.jsonl -m threads
# Only the last 2 minutes
pgr draw recording.jsonl -t 2m
Tables
Render a summary table of process tree resource usage:
pgr table <file>
pgr table - # read from stdin (live mode)
Options:
| Flag |
Short |
Default |
Description |
--metric |
-m |
rss |
Metric to display (see below) |
--human-readable |
-r |
false |
Show values in human-readable format |
--top |
-n |
0 (all) |
Show only the top N processes by peak usage |
--group |
-g |
false |
Group processes by name (sum values for same-name PIDs) |
The table shows each process with its runtime, average, and peak value for the
selected metric. In the default (non-grouped) mode, processes are displayed as
a tree reflecting the parent-child hierarchy.
Examples:
# RSS table from a file
pgr table recording.jsonl
# Human-readable memory table, top 10
pgr table recording.jsonl -r -n 10
# Thread count table grouped by name
pgr table recording.jsonl -m threads -g
# FD count table
pgr table recording.jsonl -m fds -r
# Live table piped from record
pgr record -p 1234 | pgr table -
Available metrics
| Name |
Description |
Unit |
rss |
Resident set size (default) |
kB |
vmsize |
Virtual memory size |
kB |
shared |
Shared memory |
kB |
swap |
Swap usage |
kB |
cpu |
Total CPU time (user + system) |
ticks |
utime |
User-mode CPU time |
ticks |
stime |
System-mode CPU time |
ticks |
io |
Total I/O (read + write) |
bytes |
read_bytes |
Storage bytes read |
bytes |
write_bytes |
Storage bytes written |
bytes |
threads |
Thread count |
count |
fds |
Open file descriptor count |
count |
An unknown --metric value will produce an error.
In live mode (draw -), when --timespan is set, old samples outside the
window are discarded to keep memory usage bounded.
Version
pgr version
Building with version info
Pass version and commit at build time via ldflags:
go build -ldflags "-X main.version=1.0.0 -X main.commit=$(git rev-parse --short HEAD)" ./cmd/pgr
License
See LICENSE.