pmemstat 4.0.0__tar.gz → 4.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pmemstat might be problematic. Click here for more details.
- pmemstat-4.0.2/PKG-INFO +375 -0
- pmemstat-4.0.2/README.md +349 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/pmemstat/main.py +349 -85
- {pmemstat-4.0.0 → pmemstat-4.0.2}/pyproject.toml +12 -6
- pmemstat-4.0.2/runner +123 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/tests/__init__.py +11 -1
- {pmemstat-4.0.0 → pmemstat-4.0.2}/tests/test_main.py +285 -19
- pmemstat-4.0.0/PKG-INFO +0 -452
- pmemstat-4.0.0/README.md +0 -429
- pmemstat-4.0.0/runner +0 -55
- {pmemstat-4.0.0 → pmemstat-4.0.2}/.github/workflows/ci.yml +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/.gitignore +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/LICENSE.txt +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/images/pmemstat.png +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/images/scroll-pos.png +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/memtest.c +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/pmemstat/CGroup.py +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/pmemstat/CpuSmooth.py +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/pmemstat/History.py +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/pmemstat/KillThem.py +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/pmemstat/Pressure.py +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/pmemstat/__init__.py +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/pylintrc +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/tests/fixtures/smaps_rollup_sample.txt +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/tests/fixtures/smaps_sample.txt +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/tests/test_cgroup.py +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/tests/test_cpusmooth.py +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/tests/test_history.py +0 -0
- {pmemstat-4.0.0 → pmemstat-4.0.2}/tests/test_pressure.py +0 -0
pmemstat-4.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pmemstat
|
|
3
|
+
Version: 4.0.2
|
|
4
|
+
Summary: Report Proportional Memory on Linux
|
|
5
|
+
Keywords: proportional memory,RAM,htop,top,monitor
|
|
6
|
+
Author-email: Joe Defen <joedef@duck.com>
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
License-File: LICENSE.txt
|
|
20
|
+
Requires-Dist: console-window == 2.1.3
|
|
21
|
+
Requires-Dist: pylint == 3.3.4 ; extra == "dev"
|
|
22
|
+
Requires-Dist: build ; extra == "dev"
|
|
23
|
+
Project-URL: Bug Tracker, https://github.com/joedefen/pmemstat/issues
|
|
24
|
+
Project-URL: Homepage, https://github.com/joedefen/pmemstat
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
|
|
27
|
+
> **Quick Start**: from the CLI (requires **Python 3.10 or later**)
|
|
28
|
+
> * **Install (preferred, per-user):** `pipx install pmemstat`
|
|
29
|
+
> * **Run:** `pmemstat` — shows only *your* processes
|
|
30
|
+
> * **Full view (all processes):** `pmemstat --sudo`. To avoid retyping it, add `export PMEMSTAT_ARGS=--sudo` to your shell init, then just run `pmemstat`.
|
|
31
|
+
> * **Upgrade:** `pipx upgrade pmemstat || pipx install pmemstat`
|
|
32
|
+
> * Type `?` within `pmemstat` for the help screen.
|
|
33
|
+
|
|
34
|
+
> **Note (v4.0.0 — breaking change):** `pmemstat` no longer elevates itself automatically (it did in 3.x); use `--sudo` for the full view.
|
|
35
|
+
|
|
36
|
+
> **Run `pmemstat` as root if you can. `sudo pmemstat` (or `--sudo`) is strongly recommended; an unprivileged (`-U`) run is a degraded, partial view, not a smaller-but-equivalent one:**
|
|
37
|
+
> 1. **You see only your own processes.** Every other user's (and most of root's) memory and CPU is missing, so totals understate real usage; the `PIDs=shown/total` count makes the gap explicit.
|
|
38
|
+
> 2. **System-wide header numbers degrade.** The unaccounted residual `Oth` cannot be split into its kernel (`OthK`) and user (`OthU`) parts — it silently absorbs every process you cannot read, so it is inflated and misleading.
|
|
39
|
+
> 3. **Growth / leak monitoring is crippled.** The cross-run, per-boot history is root-only, so an unprivileged run measures growth only from *this run's start* (no "since boot" baseline), and the `z` zap reset is unavailable.
|
|
40
|
+
> 4. **Kill mode reaches only your own processes.**
|
|
41
|
+
>
|
|
42
|
+
> Use `-U` only when you deliberately want a user-only view (or cannot elevate); otherwise elevate — the tool then works as designed.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# pmemstat - Proportional Memory Status
|
|
46
|
+
|
|
47
|
+
`pmemstat` shows detailed **proportional** memory use of **Linux** processes by digesting `/proc/{PID}/smaps` and `/proc/{PID}/smaps_rollup`.
|
|
48
|
+
|
|
49
|
+
Proportional memory splits the cost of shared memory among the processes sharing it (instead of counting it repeatedly, as `top` does) and excludes uninstantiated virtual memory.
|
|
50
|
+
|
|
51
|
+
Without `-o`, `pmemstat` is much faster but less detailed (it omits classes such as SysV shared memory, usually absent anyway). With `-o` it may take a few seconds to start, though loop-mode refreshes stay fast because unchanged numbers are not recomputed. In window mode, `o` toggles the detail.
|
|
52
|
+
|
|
53
|
+
Grouping rolls up the resources of multiple processes of one feature (e.g. a browser) so its total memory/CPU impact is apparent. Looping reveals growth that may be a leak, and segregating memory by type makes leaks easier to identify.
|
|
54
|
+
|
|
55
|
+
## How `pmemstat` Compares to Other Tools
|
|
56
|
+
Reach for `top`/`ps` when you need a tool that is always installed, and `htop` for a friendly full-screen overview. `pmemstat` differs in its focus on **accurate, proportional memory**: rather than RES/RSS — which double-counts shared pages and ignores uninstantiated virtual memory — it digests `smaps`/`smaps_rollup` to attribute shared memory fairly and break memory down by type. That makes it slower to start (especially with `-o`) but far more truthful about who is really using RAM. The table compares it with its closest peers: `smem` (the other tool that derives **PSS** from `smaps`) and `systemd-cgtop` (whose per-cgroup view `pmemstat` mirrors, with proportional numbers).
|
|
57
|
+
|
|
58
|
+
Three capabilities are **new or substantially overhauled** recently and marked **🆕** below: **cgroup v2 grouping**, **memory-leak / growth monitoring**, and **system pressure (PSI)**. All three are ✗ for every peer, and each has its own section.
|
|
59
|
+
|
|
60
|
+
| Feature | `pmemstat` | `smem` | `systemd-cgtop` | `htop` |
|
|
61
|
+
|---|---|---|---|---|
|
|
62
|
+
| **Proportional memory (PSS)** accounting | ✓ core design | ✓ core design | ✗ (`memory.current`) | ✗ (RES, double-counts) |
|
|
63
|
+
| **Memory-type breakdown** (shared/swap/stack/text/data) | ✓ | ✗ (USS/PSS/RSS/Swap only) | ✗ | ✗ |
|
|
64
|
+
| **Proportional swap** per grouping | ✓ | ✗ (flat Swap column) | ✗ | ✗ (total swap only) |
|
|
65
|
+
| **Group processes** by executable/command | ✓ aggregates PIDs into one row | ✓ by command by default | ✗ | ✗ (tree view only) |
|
|
66
|
+
| **Group by cgroup v2** 🆕 (services, scopes, containers) | ✓ (`-g cgroup`) | ✗ | ✓ (its core design) | ✗ |
|
|
67
|
+
| **Memory-leak / growth monitoring** 🆕 (annotates top-N growers; only re-shows significant growth) | ✓ (`G`, `--growth-style`, growth sort) | ✗ | ✗ | ✗ |
|
|
68
|
+
| **zRAM-aware effective RAM** (eTot/eUsed/eAvail) | ✓ | ✗ | ✗ | ✗ |
|
|
69
|
+
| **System pressure (PSI)** 🆕 in header (memory/cpu/io, some/full) | ✓ (`-P`) | ✗ | ✗ | ✗ |
|
|
70
|
+
| **Data source** | `/proc/{PID}/smaps*` | `/proc/{PID}/smaps` | `/sys/fs/cgroup` | `/proc` + libs |
|
|
71
|
+
| **Interactive full-screen window** | ✓ (`console-window`/curses) | ✗ (one-shot) | ✓ | ✓ |
|
|
72
|
+
| **CPU usage reporting** | ✓ | ✗ (memory only) | ✓ | ✓ |
|
|
73
|
+
| **Kill processes** | ✓ (inline confirm) | ✗ | ✗ | ✓ |
|
|
74
|
+
| **Inline search-as-you-type** | ✓ (`/`) | ✗ | ✗ | ✓ (F3) |
|
|
75
|
+
| **Requires root for all-process detail** | ✓ (else only your own) | ✓ (else only your own) | ✗ | ✗ |
|
|
76
|
+
|
|
77
|
+
For **per-mapped-file attribution** (which shared library or mmap'd file costs the most RAM), use `smem -m` or `pmap`; `pmemstat` deliberately stays focused on per-process and per-cgroup responsibility rather than rolling PSS up by mapped file.
|
|
78
|
+
|
|
79
|
+
## Installation Options
|
|
80
|
+
* `pmemstat` requires **Python 3.10 or later** and depends on `console-window` (the curses UI), pinned to an **exact version** on purpose; installing `pmemstat` pulls it automatically, so do not "upgrade" it independently.
|
|
81
|
+
* Root is needed to read `smaps`/`smaps_rollup` for **all** processes; without it only your own are reported. Persistent default flags go in `PMEMSTAT_ARGS` (e.g. `export PMEMSTAT_ARGS='--sudo --psi --loop 3 -s name'`), which is tokenized like a command line and prepended to the real arguments, so anything typed still takes precedence.
|
|
82
|
+
* `pmemstat` runs as the invoking user by default and never silently escalates. If `--sudo` cannot elevate (no terminal for a password prompt and sudo not pre-authorized, or the isolated interpreter cannot import the install), it fails with a clear message and non-zero exit rather than downgrading or hanging.
|
|
83
|
+
* To force user-only operation, use `--user`/`-U`; the header marks such a run with `--user` so the degraded (partial) view is obvious.
|
|
84
|
+
|
|
85
|
+
See the **Quick Start** above for the basic install; **Alternative Installation Options** below covers system-wide and non-`pipx` installs.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## Usage
|
|
89
|
+
```
|
|
90
|
+
usage: pmemstat [-h] [-D] [-C] [-a CPU_AVG_SECS] [-P]
|
|
91
|
+
[-g {exe,cmd,pid,cgroup,cgroupCharge}] [-f] [-k MIN_DELTA_KB]
|
|
92
|
+
[-l LOOP_SECS] [-L CMDLEN] [-t TOP_PCT] [-U] [--sudo]
|
|
93
|
+
[--save-history-now] [--reset-history-now] [-o]
|
|
94
|
+
[-u {MB,KB,human}] [-s {mem,cpu,name,growth}]
|
|
95
|
+
[--growth-style {off,both,growth,rate}] [--growth-top {all,10}]
|
|
96
|
+
[--no-growth-history] [--dont-save-growth-history] [-/ SEARCH]
|
|
97
|
+
[-W] [pids ...]
|
|
98
|
+
|
|
99
|
+
positional arguments:
|
|
100
|
+
pids list of pids/groups (none means every accessible pid)
|
|
101
|
+
|
|
102
|
+
options:
|
|
103
|
+
-h, --help show this help message and exit
|
|
104
|
+
-D, --debug debug mode (the more Ds, the higher the debug level)
|
|
105
|
+
-C, --no-cpu do NOT report percent CPU (only in window mode)
|
|
106
|
+
-a, --cpu-avg-secs CPU_AVG_SECS
|
|
107
|
+
CPU moving-average smoothing window in seconds [5-90,
|
|
108
|
+
dflt=20]
|
|
109
|
+
-P, --psi show PSI (system pressure in header + memPSI% column)
|
|
110
|
+
[dflt=off]
|
|
111
|
+
-g, --groupby {exe,cmd,pid,cgroup,cgroupCharge}
|
|
112
|
+
grouping method for presenting rows; the cgroup
|
|
113
|
+
variants differ only in the memory view: "cgroup"
|
|
114
|
+
(footprt) or "cgroupCharge" (memory.current)
|
|
115
|
+
-f, --fit-to-window do not overflow window [if -w]
|
|
116
|
+
-k, --min-delta-kb MIN_DELTA_KB
|
|
117
|
+
minimum delta KB to show again [dflt=100 if DB else
|
|
118
|
+
1000]
|
|
119
|
+
-l, --loop LOOP_SECS loop interval in secs [dflt=5 if -w else 0]
|
|
120
|
+
-L, --cmdlen CMDLEN max shown command length [dflt=36 if not -w]
|
|
121
|
+
-t, --top-pct TOP_PCT
|
|
122
|
+
report group contributing to top pct of ptotal
|
|
123
|
+
[dflt=100]
|
|
124
|
+
-U, --user run as user (NOT as root); marks the header with a
|
|
125
|
+
dimmed "--user" so the degraded view is obvious
|
|
126
|
+
--sudo re-run self as root via sudo (or set
|
|
127
|
+
PMEMSTAT_ARGS=--sudo)
|
|
128
|
+
--save-history-now save the current stats to the history ledger and exit
|
|
129
|
+
(implies --sudo; ignores other options and the
|
|
130
|
+
environment)
|
|
131
|
+
--reset-history-now reset the history ledger so all growth is measured
|
|
132
|
+
from the current values, then exit (implies --sudo;
|
|
133
|
+
ignores other options and the environment)
|
|
134
|
+
-o, --others expand "other" into shSYSV, shOth, stack, text
|
|
135
|
+
-u, --units {MB,KB,human}
|
|
136
|
+
units of memory [dflt=MB]
|
|
137
|
+
-s, --sortby {mem,cpu,name,growth}
|
|
138
|
+
sort method for presenting rows
|
|
139
|
+
--growth-style {off,both,growth,rate}
|
|
140
|
+
leak/growth style: off|both|growth|rate [dflt=off]
|
|
141
|
+
--growth-top {all,10}
|
|
142
|
+
annotate only the top-N growers: all|10 [dflt=all]
|
|
143
|
+
--no-growth-history do NOT seed growth from the cross-run ledger (fresh
|
|
144
|
+
baseline)
|
|
145
|
+
--dont-save-growth-history
|
|
146
|
+
do NOT write the cross-run growth ledger
|
|
147
|
+
-/, --search SEARCH show items with search string in name
|
|
148
|
+
-W, --no-window show in "curses" window [disables: -D,-t,-L]
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
Explanation of some options and arguments:
|
|
152
|
+
* `-g {exe,cmd,pid,cgroup,cgroupCharge}, --groupby {...}` - select the grouping of memory stats for reporting: the executable basename (the default `exe`), the truncated command line (`cmd`, length via `-L`), one process (`pid`), or the cgroup v2 path (`cgroup`, showing the derived **footprint** total, or `cgroupCharge`, showing the kernel's `memory.current`); see "Grouping by cgroup v2" below.
|
|
153
|
+
* `-a CPU_AVG_SECS, --cpu-avg-secs CPU_AVG_SECS` - the CPU smoothing window in seconds, an integer from 5 to 90 (default 20), which damps brief spikes without hiding sustained load.
|
|
154
|
+
* `-P, --psi` - show PSI (off by default): adds a **system pressure (PSI)** table to the header *and* the per-cgroup `memPSI%` column; see "System pressure (PSI)" and "Grouping by cgroup v2" below.
|
|
155
|
+
* `-k MIN_DELTA_KB, --min-delta-kb MIN_DELTA_KB` - when looping, how much change in memory use is required to show the grouping in subsequent loops: a positive value means the group must **grow** by that amount (KB), a non-positive value means it need only **change** by that amount. It also gates the growth annotation (a row is annotated only if its absolute growth is at least `-k`).
|
|
156
|
+
* `--growth-style {off,both,growth,rate}` - the inline growth ("leak") annotation for the groups that are growing (default `off`); the `G` key cycles it in window mode. See "Memory growth (leak) detection" below.
|
|
157
|
+
* `--growth-top {all,10}` - annotate only the top-N growers by the displayed metric (default `all` = no cap).
|
|
158
|
+
* `--no-growth-history` / `--dont-save-growth-history` - opt out of (respectively) seeding growth from the cross-run ledger and writing it; `PMEMSTAT_NO_HISTORY` disables both. A non-root run (`-U`) asserts both regardless. See "Memory growth (leak) detection" below.
|
|
159
|
+
* `-W, --no-window` - report once to the terminal instead of the interactive curses window (with `-l` it loops there too). Window mode (the default) forces `-t 100`, `-L 100` and off `-D`; `-W` forces `-C` (no CPU).
|
|
160
|
+
* `pids` - the positional arguments may be pids (i.e., numbers) or the names of executables (as shown by `-gexe`)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
## Grouping by cgroup v2
|
|
164
|
+
With `-g cgroup` (or cycling `g` in window mode) `pmemstat` groups processes by their **cgroup v2** path (read from `/proc/<pid>/cgroup`), which corresponds to systemd services/scopes and to container sandboxes. Each row is labelled with the cgroup's leaf unit name (e.g. `foo.service`), and a trailing `+` marks a cgroup that also contains descendant cgroups listed on their own rows. If the leaf name matches none of the row's member executables — common with launcher-created app scopes (e.g. niri's `app-niri-fuzzel-*.scope`, which actually host the launched app) — the row is labelled with the dominant member executable instead; the full cgroup path stays searchable with `/`.
|
|
165
|
+
|
|
166
|
+
There are two cgroup memory views:
|
|
167
|
+
* `kcharge` - what the kernel charges, and what `memory.max` / the OOM killer act on; the number `systemd-cgtop`/`docker stats` show.
|
|
168
|
+
* `footprt` - what the cgroup really needs to keep resident: a stable working-set proxy that does not balloon with page cache and still accounts for swapped-out pages.
|
|
169
|
+
|
|
170
|
+
Because `pmemstat` computes **proportional** memory (PSS), `ptotal` is *not* the number `systemd-cgtop`, `docker stats` or `podman stats` report (they use the non-proportional `memory.current`, which over-counts shared pages). The cgroup groupings therefore keep `ptotal` as a left-hand **reference** column but use the kernel charge as the row total: `-g cgroup` shows `footprt`, `-g cgroupCharge` shows `memory.current` (`kcharge`).
|
|
171
|
+
|
|
172
|
+
The kernel columns, read from `/sys/fs/cgroup/<path>/` in the `-u` units, decompose that charge, and the slices to the left of the total sum to it:
|
|
173
|
+
* `anon` - `memory.stat`'s `anon`: anonymous memory charged to the cgroup
|
|
174
|
+
* `cache` - page cache: `file` (in the `kcharge` view) or `file - inactive_file` (in the `footprt` view, where reclaimable inactive cache is removed)
|
|
175
|
+
* `kmem` - kernel memory: `kernel` (`kcharge`) or `kernel - slab_reclaimable` (`footprt`)
|
|
176
|
+
* `swap` - the kernel swap charge (`footprt` view only; swap is not part of `memory.current`). Distinct from `pswap` (the smaps-proportional swap shown in other modes)
|
|
177
|
+
* `oK` - the remainder, `total - (anon + cache + kmem [+ swap])`; normally just `sock`, it guarantees the row adds up
|
|
178
|
+
* `footprt` / `kcharge` - the row **total** for the active grouping (`cgroup` -> `footprt`; `cgroupCharge` -> `kcharge`):
|
|
179
|
+
* `footprt` (default) - the derived **footprint**: `memory.current - inactive_file - slab_reclaimable + swap`; also the growth metric
|
|
180
|
+
* `kcharge` - `memory.current`, the kernel's own charge
|
|
181
|
+
* `memPSI%` - `memory.pressure`'s `some avg10`: memory-pressure stall time as a percentage; shown only with `-P` (PSI), beside `cpu%`
|
|
182
|
+
|
|
183
|
+
These columns appear only in the cgroup groupings (`-g cgroup` / `-g cgroupCharge`), and are omitted entirely when the kernel exposes no cgroup v2 data (e.g. a cgroup v1 host).
|
|
184
|
+
|
|
185
|
+
cgroup v2 accounting is **hierarchical**: each cgroup's figures include its descendants, so a row with separately listed descendants (marked `+`) has their figures **subtracted** and shows only its own share; rows therefore add up to `TOTALS`. The unified root (`/`, labelled `(root)`) is everyone's ancestor, so a process living directly in it — as inside a container, where it may be the only cgroup visible — makes the `(root)+` row count every other row as a descendant. `memPSI%` is not totalled (`n/a` in `TOTALS` and `---- OTHERS ----`).
|
|
186
|
+
|
|
187
|
+
Reading `/proc/<pid>/cgroup` and `/sys/fs/cgroup` needs no root, so the grouping works unprivileged too (the PSS columns still reflect only the processes you can read).
|
|
188
|
+
|
|
189
|
+
## System pressure (PSI)
|
|
190
|
+
With `-P` (or the `p` key in window mode) pmemstat adds a small header table reporting Linux **Pressure Stall Information**, read from `/proc/pressure/{memory,cpu,io}`: the fraction of time work stalled waiting for a resource, so higher means more of a bottleneck.
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
SOME 10s 60s 300s FULL 10s 60s 300s
|
|
194
|
+
memPSI% 0.42 0.10 0.02 0.00 0.00 0.00
|
|
195
|
+
cpuPSI% 0.00 0.03 0.00 - - -
|
|
196
|
+
ioPSI% 0.15 0.60 1.04 0.10 0.49 0.96
|
|
197
|
+
```
|
|
198
|
+
Each cell is the kernel's `avg10`/`avg60`/`avg300` stall-time percentage for its window. `SOME` means at least one task was stalled; `FULL` means **all** non-idle tasks were stalled, and is normally available only for `memory` and `io` (cells a resource does not provide show `-`). A resource whose file is absent (a kernel built with `CONFIG_PSI=n`) is omitted; if no resource is readable, the table is not shown.
|
|
199
|
+
|
|
200
|
+
This is the **system-wide** view, distinct from the per-cgroup `memPSI%` column described under "Grouping by cgroup v2".
|
|
201
|
+
|
|
202
|
+
## Example Output
|
|
203
|
+
```
|
|
204
|
+
20:49:12 Tot=7.6G Used=6.2G Avail=1.4G OthK=512M OthU=3.6G Sh+Tmp=477M PIDs=174
|
|
205
|
+
2.4%/ker MajF/s=2 zRAM=813M CR=4.3 eTot:16.8G eUsed:8.8G eAvail:8.0G
|
|
206
|
+
cpu% pswap other data ptotal key/info (exe by mem)
|
|
207
|
+
60.8 2,535 593 3,988 7,116 T 174x --TOTALS in MB --
|
|
208
|
+
───────────────────────────────────────────────────────────────────────────────
|
|
209
|
+
5.9 1,366 90 2,110 3,567 24x browser
|
|
210
|
+
16.6 89 117 822 1,028 9x code
|
|
211
|
+
5.9 270 32 291 593 3.5G 2h3m 1x firefox
|
|
212
|
+
1.6 95 68 335 497 6x exe
|
|
213
|
+
2.4 150 66 94 310 6x brave
|
|
214
|
+
16.0 52 73 152 277 3x vivaldi
|
|
215
|
+
0.1 39 19 13 70 2x konsole
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
This is a **root** (`--sudo`) run; a non-root run shows a single `Oth` (see below).
|
|
219
|
+
|
|
220
|
+
In the default window loop we see
|
|
221
|
+
* a **leader line** with the current time, and `Tot`, `Avail`, `Used` (= Tot-Avail) and `Sh+Tmp` from `/proc/meminfo`, plus:
|
|
222
|
+
* the unaccounted-for memory `Oth = Tot - Avail - Tmp - ptotal` — whatever is in neither `ptotal` (the sum of proportional process totals) nor `Sh+Tmp`: kernel, reserve, drivers, imprecision, etc., and when **not** root every process whose `smaps` you cannot read.
|
|
223
|
+
* As **root** every process is accounted for, so `Oth` splits into **OthK** = `SUnreclaim + KernelStack + PageTables` (kernel-owned) and **OthU** = `Oth - OthK` (the userland remainder).
|
|
224
|
+
* When **not root** other users' memory is folded into the residual, so a single **Oth** is shown; `PIDs=shown/total` on the same line is the clue that `ptotal` is incomplete.
|
|
225
|
+
* BTRFS, ZFS, zRAM or unattached SysV shared memory can make `Oth` significant (and MemAvailable understated); start with `sudo slabtop -sc` and feature tools (e.g. `zpool list`).
|
|
226
|
+
* contributing PIDs vs the total excluding kernel threads
|
|
227
|
+
* an optional **PSI block** (with `-P`), as described in "System pressure (PSI)" above
|
|
228
|
+
* a **second leader line for zRAM** (only if zRAM is active):
|
|
229
|
+
* percent CPU consumed by kernel processes (not normalized: 4 CPUs can total 400%) — with zRAM mostly the swap process
|
|
230
|
+
* **MajF/s** - major page faults per second; 100+/s means heavy swapping and memory pressure, 1000+/s is extreme
|
|
231
|
+
* **zRAM** - "Used" RAM storing zRAM data
|
|
232
|
+
* **eTot** / **eUsed** / **eAvail** - effective Total / Used / Available RAM (more accurate as **zRAM** grows)
|
|
233
|
+
* a **header line with the reported fields**:
|
|
234
|
+
* **cpu** - percent CPU (again not normalized to 100%)
|
|
235
|
+
* **pswap** - proportional swap use (per smaps_rollup)
|
|
236
|
+
* **other** - partly sums these categories (shown by the key, `o`):
|
|
237
|
+
* **shSYSV** - proportional System V shared memory use (per smaps)
|
|
238
|
+
* **shOth** - proportional use of other shared memory (per smaps)
|
|
239
|
+
* **stack** - exclusive stack use per smaps
|
|
240
|
+
* **text** - proportional use of text memory (the read-only binary code, per smaps)
|
|
241
|
+
* **data** - exclusive data ("heap") use per smaps
|
|
242
|
+
* **ptotal** - proportional memory of all categories (sum of the columns to the left); with zRAM it includes **pswap**
|
|
243
|
+
* *empty* - entry type: **T** (grand total), **A** (newly added grouping) or **O** (overflow groupings below `--top-pct`)
|
|
244
|
+
* **growth** - inline growth annotation for top-N growers (e.g. `3.5G 2h3m`); only when growth mode is not `off`
|
|
245
|
+
* **key/info** - **{num}x**, the number of processes in the grouping (e.g. `24x browser`), plus the grouping key; `pid` grouping rows show `1x`, the pid and the command line.
|
|
246
|
+
|
|
247
|
+
## Memory growth (leak) detection
|
|
248
|
+
|
|
249
|
+
With `--growth-style` (or the `G` key in window mode) `pmemstat` annotates the
|
|
250
|
+
groups that are growing, making slow leaks visible without scrolling history:
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
5.9 270 32 291 593 3.5G 2h3m 1x firefox
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
* The annotation is `{human growth} {interval}` (e.g. `3.5G 2h3m`); `rate` adds
|
|
257
|
+
a per-day projection (`3.5G/d`) once the baseline spans at least a minute, and
|
|
258
|
+
`growth` shows just the size.
|
|
259
|
+
* The baseline comes from a **per-boot history ledger** (see "Updating History
|
|
260
|
+
from Cron / Startup") that root runs write as they run and on exit, so growth
|
|
261
|
+
is measured from the group's **first observation this boot** (possibly an
|
|
262
|
+
earlier run), not merely since this invocation; a reboot resets it. The ledger
|
|
263
|
+
is system-wide and hence **root-only**: a non-root run (`-U`) neither reads nor
|
|
264
|
+
writes it and measures from this run's start, since a root baseline compared
|
|
265
|
+
against a user-only view is meaningless and a user-only write would corrupt it.
|
|
266
|
+
Opt out with `--no-growth-history` (seed), `--dont-save-growth-history`
|
|
267
|
+
(write), or `PMEMSTAT_NO_HISTORY` (both).
|
|
268
|
+
* Within a run the baseline is **geometric and self-forgetting** (a group's
|
|
269
|
+
first 16 seconds never count), so startup bursts age out; growth is clamped at
|
|
270
|
+
zero, so shrinking is not reported.
|
|
271
|
+
* A row is annotated only if its growth (KB) is at least `-k` **and** it is
|
|
272
|
+
among the top-N growers (`--growth-top`, default `all`; `t` cycles `all`/`10`).
|
|
273
|
+
`-s growth` sorts by the displayed metric.
|
|
274
|
+
* With a cgroup grouping the metric is the grouping's total (`footprt` for
|
|
275
|
+
`-g cgroup`, `kcharge` for `-g cgroupCharge`) rather than proportional PSS.
|
|
276
|
+
* A system-wide line beneath the leader closes the identity
|
|
277
|
+
`ΔUsed = ΔTOTALS(ptotal) + Δ(Sh+Tmp) + ΔOth`; as **root** the residual splits
|
|
278
|
+
into `ΔOthK`/`ΔOthU`, so kernel/driver growth is separable from userspace and
|
|
279
|
+
tmpfs, while a non-root run shows a single `ΔOth`. The shared baseline interval
|
|
280
|
+
trails as `[1h58m]` in every growth mode.
|
|
281
|
+
|
|
282
|
+
## Updating History from Cron / Startup
|
|
283
|
+
`pmemstat --save-history-now` performs one **silent** scan, writes the per-boot
|
|
284
|
+
history ledger and exits (`0` on success, `1` on failure with a message on
|
|
285
|
+
stderr). It implies `--sudo` and ignores all other options and `PMEMSTAT_ARGS`,
|
|
286
|
+
so it is safe from cron or a startup unit. The ledger is per invoking user
|
|
287
|
+
(`/tmp/pmemstat-<uid>`, or `PMEMSTAT_STATE_DIR`), so run it as the user whose
|
|
288
|
+
history you want; a root cron job without `SUDO_UID` updates the root ledger.
|
|
289
|
+
Only root runs use the ledger (see "Memory growth (leak) detection" above).
|
|
290
|
+
|
|
291
|
+
`pmemstat --reset-history-now` scans the same way but **replaces** the ledger
|
|
292
|
+
rather than merging: current values become the new baseline, as if the tool had
|
|
293
|
+
first run just after boot. It likewise implies `--sudo` and ignores other
|
|
294
|
+
options. Inside the window, `z` performs the same reset (only as root while the
|
|
295
|
+
growth annotation is on), asking for `y`/`Y` confirmation first; `ESC` or 10
|
|
296
|
+
seconds declines.
|
|
297
|
+
|
|
298
|
+
## Key Legend (Window Mode)
|
|
299
|
+
The top line of the header is an always-visible key legend with the live search
|
|
300
|
+
field appended, so the available keys are in evidence without opening the help
|
|
301
|
+
screen:
|
|
302
|
+
|
|
303
|
+
?:help group units sort cpu Kill pSI Growth /{regex}
|
|
304
|
+
|
|
305
|
+
`?` opens the complete list of keys plus the navigation keys. `zap` is appended
|
|
306
|
+
for a root run while the growth annotation is showing, since resetting the
|
|
307
|
+
baseline has no visible effect otherwise.
|
|
308
|
+
|
|
309
|
+
## Help Screen (in Window Mode, Press '?')
|
|
310
|
+
In window mode, `?` opens the help screen: navigation keys at the top and, below
|
|
311
|
+
the line, the option keys with their possible values (the current choice is
|
|
312
|
+
highlighted). Typing an option key (e.g. `c`) advances to the next value, applied
|
|
313
|
+
on the next loop; the option keys also work directly on the main screen without
|
|
314
|
+
opening the help screen.
|
|
315
|
+
|
|
316
|
+
## Inline Search (Window Mode)
|
|
317
|
+
Press `/` to activate inline search mode; the live search field is the trailing
|
|
318
|
+
`/{regex}` on the first header line. The display filters in real-time,
|
|
319
|
+
case-insensitively, matching any part of the process info. **Enter** commits the
|
|
320
|
+
search; **ESC** cancels and clears it.
|
|
321
|
+
|
|
322
|
+
## Kill Mode (Window Mode)
|
|
323
|
+
Press `K` to enter "Kill Mode" where you can select and kill processes:
|
|
324
|
+
1. Use the navigation keys to highlight a row and press **Enter**
|
|
325
|
+
2. An inline confirmation prompt names the process and its PID count; press **`y`** to kill the process(es) or **ESC** to cancel
|
|
326
|
+
3. A brief flash message reports the result
|
|
327
|
+
4. Press `K` again to exit Kill Mode
|
|
328
|
+
|
|
329
|
+
## Scroll Position (Window Mode)
|
|
330
|
+
|
|
331
|
+
Sometimes, the horizontal line between the header and scrollable region has a reverse video block (under the "351" in this case):
|
|
332
|
+

|
|
333
|
+
|
|
334
|
+
Its position and length indicate how far through the document you are and how
|
|
335
|
+
much of it is visible; with no block, the document does not overflow. Press `f`
|
|
336
|
+
to fit the document to the screen with a "rollup" line summarizing the lines
|
|
337
|
+
that would not fit.
|
|
338
|
+
|
|
339
|
+
## Quirks and Details
|
|
340
|
+
* **pswap** comes only from `smaps_rollup`, so it may be slightly out of sync with the `smaps` data.
|
|
341
|
+
* **ptotal** (from `smaps`) and **pss** (from `smaps_rollup`, usually hidden) differ more than expected, though still closely.
|
|
342
|
+
* After the first loop only **pss** is read, and groups with sufficient aggregate change are probed for detail; later loops are therefore faster, at some cost in accuracy.
|
|
343
|
+
* the "exe" value comes from `/proc/{PID}/cmdline`: the leading path is stripped, and if the result is a script interpreter (python, perl, bash, ...) whose first argument is a full path, it is shown as `{interpreter}->{basename(script)}` (e.g. `python3->pmemstat.py`).
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
## Test Program and Test Suggestions
|
|
347
|
+
The included C program, `memtest.c`, builds with `cc memtest.c -o memtest`. It allocates about **3 MiB per loop** by default across all memory types (SysV shared memory, a memory-mapped file, the heap and a little stack), can run several times concurrently (sharing the SysV and mapped memory), and caps itself near `min(256 MiB, RAM/16)` so it will not trigger the OOM killer.
|
|
348
|
+
|
|
349
|
+
Because ~3 MiB per loop exceeds `pmemstat`'s default `-k` gate (1000 KB), the program stays visible loop to loop and — with `--growth-style` and `--growth-top all` — is annotated as a grower without relaxing `-k`. Tuning options:
|
|
350
|
+
* `-q` - quick: sleep 1s between loops instead of 10s
|
|
351
|
+
* `-s` - short: only 16 loops
|
|
352
|
+
* `-n LOOPS` - number of loops
|
|
353
|
+
* `-c KiB` - bytes added to each pool per loop (default 1024 KiB)
|
|
354
|
+
* `-b MiB` - total memory budget (default `min(256 MiB, RAM/16)`)
|
|
355
|
+
|
|
356
|
+
For a robustness test with processes coming and going, run several background `sleep`s of varying durations in a loop plus one in the foreground; this exercises races such as a PID vanishing from `/proc` while its `smaps` is read.
|
|
357
|
+
|
|
358
|
+
## Alternative Installation Options
|
|
359
|
+
The Quick Start's per-user `pipx` install is preferred. Variants:
|
|
360
|
+
* **System-wide with `pipx` (root)** — makes `pmemstat` available to every account and to plain `sudo pmemstat` (the launcher lands in `/usr/local/bin`):
|
|
361
|
+
```
|
|
362
|
+
sudo pipx install --global pmemstat
|
|
363
|
+
# to upgrade: sudo pipx upgrade --global pmemstat || sudo pipx install --global pmemstat
|
|
364
|
+
# to uninstall: sudo pipx uninstall --global pmemstat
|
|
365
|
+
```
|
|
366
|
+
* **From PyPi as non-root (`pip`)** — you need `~/.local/bin` on your `$PATH`:
|
|
367
|
+
```
|
|
368
|
+
python -m pip install --user pmemstat
|
|
369
|
+
# to uninstall: python -m pip uninstall pmemstat
|
|
370
|
+
```
|
|
371
|
+
* **From PyPi as root (`pip`)** — installs into `/usr/local/bin` for all users; `PIP_BREAK_SYSTEM_PACKAGES=1` may be required on some distros:
|
|
372
|
+
```
|
|
373
|
+
sudo PIP_BREAK_SYSTEM_PACKAGES=1 python3 -m pip install pmemstat
|
|
374
|
+
# to uninstall: sudo python -m pip uninstall pmemstat
|
|
375
|
+
```
|