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