[+] update oom_firefox

1. fix rss being in KiB;
  2. get rid of --slice, use --unit-name;
  3. fix columns strings being cut,
    use two calls to ps,
    to get full cmd and cgroup;
  4. tested manually that it works;
This commit is contained in:
Siarhei Siniak 2025-12-01 15:59:36 +03:00
parent 7d5868345b
commit a8d0f64419

@ -19,6 +19,7 @@ from prompt_toolkit.widgets import TextArea, Frame, Dialog, Button, Label
from prompt_toolkit.styles import Style
from typing import (TypedDict,)
from collections import OrderedDict
logger = logging.getLogger(__name__)
@ -64,34 +65,59 @@ class get_firefox_procs_ps_t:
cmd: str
def get_firefox_procs_ps(slice_name=None) -> list[get_firefox_procs_ps_t.res_t.entry_t]:
lines = subprocess.check_output([
'ps', '-ax', '-o', 'rss,pid,ppid,cgroup,cmd'
]).decode('utf-8').splitlines()[1:]
entries : dict[int, dict[str, Any]] = dict()
entries : list[dict[str, str]] = []
for regex, columns in [
(
re.compile(r'^\s*(\d+)\s+(\d+)\s+(\d+)\s+(.*)$'),
OrderedDict(
pid=lambda x: int(x[1]),
rss=lambda x: int(x[2]) * 1024,
ppid=lambda x: int(x[3]),
cmd=lambda x: x[4],
),
),
(
re.compile(r'^\s*(\d+)\s+(.*)$'),
OrderedDict(
pid=lambda x: int(x[1]),
cgroup=lambda x: x[2],
),
),
]:
lines = subprocess.check_output([
'ps', '-ax', '-o', ','.join(columns.keys()),
]).decode('utf-8').splitlines()[1:]
for line in lines:
r = re.compile(r'^\s*(\d+)\s+(\d+)\s+(\d+)\s+([^\s]+)\s+(.*)$')
# print([r, line])
match = r.match(line)
for line in lines:
r = re.compile(regex)
# print([r, line])
match = r.match(line)
assert match
assert match
entry = dict(
rss=int(match[1]),
pid=int(match[2]),
ppid=int(match[3]),
cgroup=match[4],
cmd=match[5],
)
entry = {
k : v(match)
for k, v in columns.items()
}
if not entry['pid'] in entries:
entries[entry['pid']] = dict()
entries[entry['pid']].update(entry)
filtered_entries : list[dict[str, Any]] = []
for entry in entries.values():
if not 'cgroup' in entry or not 'rss' in entry:
continue
if not slice_name is None:
if not slice_name in entry['cgroup']:
continue
filtered_entries.append(entry)
entries.append(entry)
return entries
return filtered_entries
def get_firefox_procs(slice_name=None):
procs = []
@ -126,9 +152,11 @@ def total_rss_mb(procs: list['get_firefox_procs_ps_t.res_t.entry_t']):
def is_main_firefox(p):
try:
# for arg in p.cmdline():
for arg in p['cmd'].split():
if "contentproc" in arg:
return False
#for arg in p['cmd'].split():
# if "contentproc" in arg:
# return False
if 'contentproc' in p['cmd']:
return False
return True
except Exception:
logger.exception('')
@ -203,8 +231,6 @@ def main():
help="MemorySwapMax (MB) for the systemd scope")
parser.add_argument("--interval", type=float, default=1.0,
help="Monitoring interval in seconds")
parser.add_argument("--slice", type=str, default=None,
help="Only monitor Firefox processes in this systemd slice")
parser.add_argument("--unit-name", type=str, default="firefox-limited",
help="Name for systemd transient unit")
parser.add_argument("--firefox-extra", action="append", default=[],
@ -225,6 +251,7 @@ def main():
def terminate():
terminate_flag.set()
app.exit()
def stop():
with lock:
@ -248,13 +275,13 @@ def main():
nonlocal firefox_proc
with lock:
procs = get_firefox_procs_ps(slice_name=args.slice)
procs = get_firefox_procs_ps(slice_name=args.unit_name)
total = total_rss_mb(procs)
limit = args.max_mb
kill_to = args.kill_percent / 100.0 * limit
lines = [
f"Firefox RSS (slice={args.slice}): {total:.1f} MB",
f"Firefox RSS (slice={args.unit_name}): {total:.1f} MB",
f"Threshold (max): {limit:.1f} MB",
f"Killto target: {kill_to:.1f} MB ({args.kill_percent}%)",
f"Lowpriority PIDs: {sorted(low_priority_pids)}"
@ -350,7 +377,7 @@ def main():
open_message("Settings",
f"max_mb = {args.max_mb}\n"
f"kill_percent = {args.kill_percent}\n"
f"slice = {args.slice}\n"
f"slice = {args.unit_name}\n"
f"swap_max_mb = {args.swap_max_mb}\n"
f"extra firefox args = {args.firefox_extra}")