Letter keys instead of function keys
h help, / search, s sort, t tree, z all, c command line, i swap in, o swap out, F flush, k signal, q quit.
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.1 — 2026-10-07
|
||||
|
||||
### Changed
|
||||
- Plain letter keys instead of function keys: `h` help, `/` search, `s` sort,
|
||||
`t` tree, `z` all, `c` command line, `i` swap in, `o` swap out, `F` flush,
|
||||
`k` signal, `q` quit.
|
||||
|
||||
## 1.0.0 — 2026-10-07
|
||||
|
||||
First release.
|
||||
|
||||
@@ -11,10 +11,10 @@ zram figures appear when present. Each process shows its SWAP, proportional
|
||||
SWPPSS, DELTA since the last refresh, RSS and how much of it is in swap;
|
||||
`Enter` breaks a process down by mapping (heap, stacks, libraries, files).
|
||||
|
||||
Actions, each confirmed first: swap a process back in (`F7`), page a process
|
||||
out (`F8`, `process_madvise`, Linux 5.10+), flush every swap device with
|
||||
swapoff+swapon (`F2`), send a signal (`F9`). Search (`/`), sort (`F6`), tree
|
||||
view (`F5`), zero-swap toggle (`z`), full command line (`c`). Settings
|
||||
Actions, each confirmed first: swap a process back in (`i`), page a process
|
||||
out (`o`, `process_madvise`, Linux 5.10+), flush every swap device with
|
||||
swapoff+swapon (`F`), send a signal (`k`). Search (`/`), sort (`s`), tree
|
||||
view (`t`), zero-swap toggle (`z`), full command line (`c`), help (`h`). Settings
|
||||
persist in `~/.config/swaptop/config`. See `man swaptop`.
|
||||
|
||||
Run it as root to see every process and use the actions:
|
||||
|
||||
+13
-13
@@ -1,5 +1,5 @@
|
||||
.\" Manual page for swaptop. View with: man -l docs/swaptop.1
|
||||
.TH SWAPTOP 1 "October 2026" "swaptop 1.0.0" "User Commands"
|
||||
.TH SWAPTOP 1 "October 2026" "swaptop 1.0.1" "User Commands"
|
||||
.SH NAME
|
||||
swaptop \- htop\-style view of which processes are using swap, and how
|
||||
.SH SYNOPSIS
|
||||
@@ -26,8 +26,8 @@ and
|
||||
.IR /proc/PID/smaps .
|
||||
Nothing is written anywhere except the configuration file (see
|
||||
.BR FILES ).
|
||||
Actions that change memory placement are available from the function keys
|
||||
and always ask for confirmation first.
|
||||
Actions that change memory placement are bound to single letter keys and
|
||||
always ask for confirmation first.
|
||||
.PP
|
||||
Running as root (\fBsudo swaptop\fR) is recommended: without it the kernel
|
||||
hides the detailed figures and memory of other users' processes (see
|
||||
@@ -147,19 +147,19 @@ Move the selection.
|
||||
.B Enter
|
||||
Per\-mapping breakdown of the selected process. \fBEsc\fR or \fBq\fR returns.
|
||||
.TP
|
||||
.B F1, h, ?
|
||||
.B h, ?
|
||||
Help.
|
||||
.TP
|
||||
.B F3, /
|
||||
.B /
|
||||
Incremental search over command line, name, user and PID. \fBEnter\fR keeps
|
||||
the filter, \fBEsc\fR clears it.
|
||||
.TP
|
||||
.B F5, t
|
||||
.B t
|
||||
Toggle tree view. In tree view a process is shown when it or any of its
|
||||
descendants matches, so parents stay for context; processes without swap are
|
||||
dimmed.
|
||||
.TP
|
||||
.B F6, s
|
||||
.B s
|
||||
Cycle the sort column.
|
||||
.TP
|
||||
.B z
|
||||
@@ -168,12 +168,12 @@ Toggle between processes with swap only (the default) and all processes.
|
||||
.B c
|
||||
Toggle between the command name and the full command line.
|
||||
.TP
|
||||
.B F10, q
|
||||
.B q
|
||||
Quit, saving the current settings.
|
||||
.SH ACTIONS
|
||||
Each action asks for confirmation before doing anything.
|
||||
.TP
|
||||
.B F7, i \(em swap in
|
||||
.B i \(em swap in
|
||||
Faults every swapped page of the selected process back into RAM by reading
|
||||
it through
|
||||
.IR /proc/PID/mem .
|
||||
@@ -183,7 +183,7 @@ so nothing else is touched. Afterwards the process runs at full speed again.
|
||||
Its swap slots usually remain allocated as swap cache (yellow in the bar)
|
||||
until the kernel needs them or the device is flushed.
|
||||
.TP
|
||||
.B F8, o \(em swap out
|
||||
.B o \(em swap out
|
||||
Asks the kernel to reclaim the selected process's resident memory now, with
|
||||
.BR process_madvise (2)
|
||||
.BR MADV_PAGEOUT
|
||||
@@ -191,7 +191,7 @@ Asks the kernel to reclaim the selected process's resident memory now, with
|
||||
pages are simply dropped. The process keeps running and faults its memory
|
||||
back as it touches it. Useful for parking an idle but large process.
|
||||
.TP
|
||||
.B F2, F \(em flush
|
||||
.B F \(em flush
|
||||
Empties every swap device completely by calling
|
||||
.BR swapoff (2)
|
||||
and then
|
||||
@@ -204,7 +204,7 @@ priority is kept; other
|
||||
.BR swapon (8)
|
||||
options such as discard are not reapplied. Needs root.
|
||||
.TP
|
||||
.B F9, k \(em signal
|
||||
.B k \(em signal
|
||||
Opens a picker of common signals (TERM, KILL, INT, HUP, QUIT, USR1, USR2,
|
||||
STOP, CONT) to send to the selected process.
|
||||
.SH PERMISSIONS
|
||||
@@ -258,7 +258,7 @@ option.
|
||||
.SH NOTES
|
||||
Swap cache explains most apparent mismatches: a process can show 0 in
|
||||
\fBSWAP\fR while the device's usage barely drops, because the slots are kept
|
||||
until needed. Flushing (\fBF2\fR) is the only way to release them on demand.
|
||||
until needed. Flushing (\fBF\fR) is the only way to release them on demand.
|
||||
.PP
|
||||
The \fBSWAP\fR column of processes sharing memory (database workers, for
|
||||
example) double counts that memory, by design, so that each process shows
|
||||
|
||||
@@ -97,7 +97,7 @@ func sortIndex(name string) int {
|
||||
func main() {
|
||||
cfg := loadConfig()
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: swaptop [-d SECONDS] [-s COLUMN] [-t] [-z] [--version]\n\nhtop-style view of swap usage per process. Press F1 inside for keys.\n\n")
|
||||
fmt.Fprintf(os.Stderr, "Usage: swaptop [-d SECONDS] [-s COLUMN] [-t] [-z] [--version]\n\nhtop-style view of swap usage per process. Press h inside for keys.\n\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
delay := flag.Float64("d", cfg.Refresh, "refresh interval in seconds")
|
||||
@@ -399,24 +399,6 @@ func (a *app) key(ev *tcell.EventKey) bool {
|
||||
}
|
||||
case tcell.KeyEscape:
|
||||
a.view = viewList
|
||||
case tcell.KeyF1:
|
||||
a.view = viewHelp
|
||||
case tcell.KeyF2:
|
||||
a.askFlush()
|
||||
case tcell.KeyF3:
|
||||
a.searching = true
|
||||
case tcell.KeyF5:
|
||||
a.toggleTree()
|
||||
case tcell.KeyF6:
|
||||
a.cycleSort()
|
||||
case tcell.KeyF7:
|
||||
a.askSwapIn()
|
||||
case tcell.KeyF8:
|
||||
a.askSwapOut()
|
||||
case tcell.KeyF9:
|
||||
a.askKill()
|
||||
case tcell.KeyF10:
|
||||
return true
|
||||
case tcell.KeyRune:
|
||||
switch ev.Rune() {
|
||||
case 'q':
|
||||
@@ -515,7 +497,7 @@ func (a *app) askSwapIn() {
|
||||
}
|
||||
})
|
||||
if err == nil {
|
||||
set(fmt.Sprintf("Swapped in %s (%d) in %s. Its slots stay 'cached' until the kernel drops them or you flush (F2).",
|
||||
set(fmt.Sprintf("Swapped in %s (%d) in %s. Its slots stay 'cached' until the kernel drops them or you flush (F).",
|
||||
p.Comm, p.PID, time.Since(start).Round(100*time.Millisecond)))
|
||||
}
|
||||
return err
|
||||
@@ -861,14 +843,14 @@ func (a *app) drawHelp() {
|
||||
" magenta not owned by any process: tmpfs/shm files, or slots whose owner we cannot read",
|
||||
"",
|
||||
"Actions (each asks for confirmation)",
|
||||
" F7 i swap in: fault every swapped page of the process back into RAM (reads /proc/PID/mem)",
|
||||
" F8 o swap out: ask the kernel to reclaim the process's pages now (process_madvise MADV_PAGEOUT, Linux 5.10+)",
|
||||
" F2 F flush: swapoff + swapon every device, emptying swap completely (root, needs enough free RAM)",
|
||||
" F9 k send a signal (picker)",
|
||||
" i swap in: fault every swapped page of the process back into RAM (reads /proc/PID/mem)",
|
||||
" o swap out: ask the kernel to reclaim the process's pages now (process_madvise MADV_PAGEOUT, Linux 5.10+)",
|
||||
" F flush: swapoff + swapon every device, emptying swap completely (root, needs enough free RAM)",
|
||||
" k send a signal (picker)",
|
||||
"",
|
||||
"Keys",
|
||||
" Up/Down PgUp/PgDn Home/End move Enter per-mapping breakdown of the selected process",
|
||||
" F3 / search F6 s sort F5 t tree view z show/hide processes with no swap c full command line F10 q quit",
|
||||
" / search s sort t tree view z show/hide processes with no swap c full command line h help q quit / back",
|
||||
"",
|
||||
"Settings (sort, tree, z, c, refresh) are saved to " + configPath() + " on exit.",
|
||||
"Non-root: SWPPSS and actions only work for your own processes (kernel.yama.ptrace_scope).",
|
||||
@@ -891,13 +873,13 @@ func (a *app) drawBottom(w, h int) {
|
||||
a.put(0, y, "Search: "+a.filter+"_", stWarn)
|
||||
return
|
||||
}
|
||||
keys := [][2]string{{"F1", "Help"}, {"F2", "Flush"}, {"F3", "Search"}, {"F5", "Tree"}, {"F6", "SortBy"}, {"F7", "SwapIn"}, {"F8", "SwapOut"}, {"F9", "Kill"}, {"F10", "Quit"}}
|
||||
keys := [][2]string{{"h", "Help"}, {"/", "Search"}, {"s", "Sort"}, {"t", "Tree"}, {"z", "All"}, {"c", "Cmd"}, {"i", "SwapIn"}, {"o", "SwapOut"}, {"F", "Flush"}, {"k", "Kill"}, {"q", "Quit"}}
|
||||
if a.view != viewList {
|
||||
keys = [][2]string{{"Esc", "Back"}, {"F7", "SwapIn"}, {"F8", "SwapOut"}, {"F9", "Kill"}, {"F10", "Quit"}}
|
||||
keys = [][2]string{{"q", "Back"}, {"i", "SwapIn"}, {"o", "SwapOut"}, {"k", "Kill"}}
|
||||
}
|
||||
x := 0
|
||||
for _, k := range keys {
|
||||
x = a.put(x, y, fmt.Sprintf("%3s", k[0]), stKey)
|
||||
x = a.put(x, y, " "+k[0], stKey)
|
||||
x = a.put(x, y, fmt.Sprintf("%-7s", k[1]), stFn)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user