openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

227 lines
6.1 KiB

#!/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()