forked from IQ.Lvbs/IQ.Pilot
IQ.Pilot Prebuilt Release @ ab07000
This commit is contained in:
227
system/proclogd.py
Executable file
227
system/proclogd.py
Executable file
@@ -0,0 +1,227 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
from typing import NoReturn, TypedDict
|
||||
|
||||
from cereal import messaging
|
||||
from openpilot.common.realtime import Ratekeeper
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
JIFFY = os.sysconf(os.sysconf_names['SC_CLK_TCK'])
|
||||
PAGE_SIZE = os.sysconf(os.sysconf_names['SC_PAGE_SIZE'])
|
||||
|
||||
|
||||
def _cpu_times() -> list[dict[str, float]]:
|
||||
cpu_times: list[dict[str, float]] = []
|
||||
try:
|
||||
with open('/proc/stat') as f:
|
||||
lines = f.readlines()[1:]
|
||||
for line in lines:
|
||||
if not line.startswith('cpu') or len(line) < 4 or not line[3].isdigit():
|
||||
break
|
||||
parts = line.split()
|
||||
cpu_times.append({
|
||||
'cpuNum': int(parts[0][3:]),
|
||||
'user': float(parts[1]) / JIFFY,
|
||||
'nice': float(parts[2]) / JIFFY,
|
||||
'system': float(parts[3]) / JIFFY,
|
||||
'idle': float(parts[4]) / JIFFY,
|
||||
'iowait': float(parts[5]) / JIFFY,
|
||||
'irq': float(parts[6]) / JIFFY,
|
||||
'softirq': float(parts[7]) / JIFFY,
|
||||
})
|
||||
except Exception:
|
||||
cloudlog.exception("failed to read /proc/stat")
|
||||
return cpu_times
|
||||
|
||||
|
||||
def _mem_info() -> dict[str, int]:
|
||||
keys = ["MemTotal:", "MemFree:", "MemAvailable:", "Buffers:", "Cached:", "Active:", "Inactive:", "Shmem:"]
|
||||
info: dict[str, int] = dict.fromkeys(keys, 0)
|
||||
try:
|
||||
with open('/proc/meminfo') as f:
|
||||
for line in f:
|
||||
parts = line.split()
|
||||
if parts and parts[0] in info:
|
||||
info[parts[0]] = int(parts[1]) * 1024
|
||||
except Exception:
|
||||
cloudlog.exception("failed to read /proc/meminfo")
|
||||
return info
|
||||
|
||||
|
||||
_STAT_POS = {
|
||||
'pid': 1,
|
||||
'state': 3,
|
||||
'ppid': 4,
|
||||
'utime': 14,
|
||||
'stime': 15,
|
||||
'cutime': 16,
|
||||
'cstime': 17,
|
||||
'priority': 18,
|
||||
'nice': 19,
|
||||
'num_threads': 20,
|
||||
'starttime': 22,
|
||||
'vsize': 23,
|
||||
'rss': 24,
|
||||
'processor': 39,
|
||||
}
|
||||
|
||||
class ProcStat(TypedDict):
|
||||
name: str
|
||||
pid: int
|
||||
state: str
|
||||
ppid: int
|
||||
utime: int
|
||||
stime: int
|
||||
cutime: int
|
||||
cstime: int
|
||||
priority: int
|
||||
nice: int
|
||||
num_threads: int
|
||||
starttime: int
|
||||
vms: int
|
||||
rss: int
|
||||
processor: int
|
||||
|
||||
|
||||
def _parse_proc_stat(stat: str) -> ProcStat | None:
|
||||
open_paren = stat.find('(')
|
||||
close_paren = stat.rfind(')')
|
||||
if open_paren == -1 or close_paren == -1 or open_paren > close_paren:
|
||||
return None
|
||||
name = stat[open_paren + 1:close_paren]
|
||||
stat = stat[:open_paren] + stat[open_paren:close_paren].replace(' ', '_') + stat[close_paren:]
|
||||
parts = stat.split()
|
||||
if len(parts) < 52:
|
||||
return None
|
||||
try:
|
||||
return {
|
||||
'name': name,
|
||||
'pid': int(parts[_STAT_POS['pid'] - 1]),
|
||||
'state': parts[_STAT_POS['state'] - 1][0],
|
||||
'ppid': int(parts[_STAT_POS['ppid'] - 1]),
|
||||
'utime': int(parts[_STAT_POS['utime'] - 1]),
|
||||
'stime': int(parts[_STAT_POS['stime'] - 1]),
|
||||
'cutime': int(parts[_STAT_POS['cutime'] - 1]),
|
||||
'cstime': int(parts[_STAT_POS['cstime'] - 1]),
|
||||
'priority': int(parts[_STAT_POS['priority'] - 1]),
|
||||
'nice': int(parts[_STAT_POS['nice'] - 1]),
|
||||
'num_threads': int(parts[_STAT_POS['num_threads'] - 1]),
|
||||
'starttime': int(parts[_STAT_POS['starttime'] - 1]),
|
||||
'vms': int(parts[_STAT_POS['vsize'] - 1]),
|
||||
'rss': int(parts[_STAT_POS['rss'] - 1]),
|
||||
'processor': int(parts[_STAT_POS['processor'] - 1]),
|
||||
}
|
||||
except Exception:
|
||||
cloudlog.exception("failed to parse /proc/<pid>/stat")
|
||||
return None
|
||||
|
||||
class ProcExtra(TypedDict):
|
||||
pid: int
|
||||
name: str
|
||||
exe: str
|
||||
cmdline: list[str]
|
||||
|
||||
|
||||
_proc_cache: dict[int, ProcExtra] = {}
|
||||
|
||||
|
||||
def _get_proc_extra(pid: int, name: str) -> ProcExtra:
|
||||
cache: ProcExtra | None = _proc_cache.get(pid)
|
||||
if cache is None or cache.get('name') != name:
|
||||
exe = ''
|
||||
cmdline: list[str] = []
|
||||
try:
|
||||
exe = os.readlink(f'/proc/{pid}/exe')
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
with open(f'/proc/{pid}/cmdline', 'rb') as f:
|
||||
cmdline = [c.decode('utf-8', errors='replace') for c in f.read().split(b'\0') if c]
|
||||
except OSError:
|
||||
pass
|
||||
cache = {'pid': pid, 'name': name, 'exe': exe, 'cmdline': cmdline}
|
||||
_proc_cache[pid] = cache
|
||||
return cache
|
||||
|
||||
|
||||
def _procs() -> list[ProcStat]:
|
||||
stats: list[ProcStat] = []
|
||||
for pid_str in os.listdir('/proc'):
|
||||
if not pid_str.isdigit():
|
||||
continue
|
||||
try:
|
||||
with open(f'/proc/{pid_str}/stat') as f:
|
||||
stat = f.read()
|
||||
parsed = _parse_proc_stat(stat)
|
||||
if parsed is not None:
|
||||
stats.append(parsed)
|
||||
except OSError:
|
||||
continue
|
||||
return stats
|
||||
|
||||
|
||||
def build_proc_log_message(msg) -> None:
|
||||
pl = msg.procLog
|
||||
|
||||
procs = _procs()
|
||||
l = pl.init('procs', len(procs))
|
||||
for i, r in enumerate(procs):
|
||||
proc = l[i]
|
||||
proc.pid = r['pid']
|
||||
proc.state = ord(r['state'][0])
|
||||
proc.ppid = r['ppid']
|
||||
proc.cpuUser = r['utime'] / JIFFY
|
||||
proc.cpuSystem = r['stime'] / JIFFY
|
||||
proc.cpuChildrenUser = r['cutime'] / JIFFY
|
||||
proc.cpuChildrenSystem = r['cstime'] / JIFFY
|
||||
proc.priority = r['priority']
|
||||
proc.nice = r['nice']
|
||||
proc.numThreads = r['num_threads']
|
||||
proc.startTime = r['starttime'] / JIFFY
|
||||
proc.memVms = r['vms']
|
||||
proc.memRss = r['rss'] * PAGE_SIZE
|
||||
proc.processor = r['processor']
|
||||
proc.name = r['name']
|
||||
|
||||
extra = _get_proc_extra(r['pid'], r['name'])
|
||||
proc.exe = extra['exe']
|
||||
cmdline = proc.init('cmdline', len(extra['cmdline']))
|
||||
for j, arg in enumerate(extra['cmdline']):
|
||||
cmdline[j] = arg
|
||||
|
||||
cpu_times = _cpu_times()
|
||||
cpu_list = pl.init('cpuTimes', len(cpu_times))
|
||||
for i, ct in enumerate(cpu_times):
|
||||
cpu = cpu_list[i]
|
||||
cpu.cpuNum = ct['cpuNum']
|
||||
cpu.user = ct['user']
|
||||
cpu.nice = ct['nice']
|
||||
cpu.system = ct['system']
|
||||
cpu.idle = ct['idle']
|
||||
cpu.iowait = ct['iowait']
|
||||
cpu.irq = ct['irq']
|
||||
cpu.softirq = ct['softirq']
|
||||
|
||||
mem_info = _mem_info()
|
||||
pl.mem.total = mem_info["MemTotal:"]
|
||||
pl.mem.free = mem_info["MemFree:"]
|
||||
pl.mem.available = mem_info["MemAvailable:"]
|
||||
pl.mem.buffers = mem_info["Buffers:"]
|
||||
pl.mem.cached = mem_info["Cached:"]
|
||||
pl.mem.active = mem_info["Active:"]
|
||||
pl.mem.inactive = mem_info["Inactive:"]
|
||||
pl.mem.shared = mem_info["Shmem:"]
|
||||
|
||||
|
||||
def main() -> NoReturn:
|
||||
pm = messaging.PubMaster(['procLog'])
|
||||
rk = Ratekeeper(0.5)
|
||||
while True:
|
||||
msg = messaging.new_message('procLog', valid=True)
|
||||
build_proc_log_message(msg)
|
||||
pm.send('procLog', msg)
|
||||
rk.keep_time()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user