From a8d0f64419379ef73b3803b484671c1035671865 Mon Sep 17 00:00:00 2001 From: Siarhei Siniak Date: Mon, 1 Dec 2025 15:59:36 +0300 Subject: [PATCH] [+] 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; --- .../home/nartes/.local/bin/oom_firefox | 81 ++++++++++++------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/platform_dotfiles/ideapad_slim_3_15arp10/home/nartes/.local/bin/oom_firefox b/platform_dotfiles/ideapad_slim_3_15arp10/home/nartes/.local/bin/oom_firefox index ae5400d..864538e 100755 --- a/platform_dotfiles/ideapad_slim_3_15arp10/home/nartes/.local/bin/oom_firefox +++ b/platform_dotfiles/ideapad_slim_3_15arp10/home/nartes/.local/bin/oom_firefox @@ -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"Kill‑to target: {kill_to:.1f} MB ({args.kill_percent}%)", f"Low‑priority 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}")