#!/usr/bin/env python3
# type: ignore
'''
System tools like top / htop can only show current cpu usage values , so I write this script to do statistics jobs .
Features :
Use psutil library to sample cpu usage ( avergage for all cores ) of openpilot processes , at a rate of 5 samples / sec .
Do cpu usage statistics periodically , 5 seconds as a cycle .
Caculate the average cpu usage within this cycle .
Caculate minumium / maximium / accumulated_average cpu usage as long term inspections .
Monitor multiple processes simuteneously .
Sample usage :
root @localhost : / data / openpilot $ python selfdrive / debug / cpu_usage_stat . py boardd , ubloxd
( ' Add monitored proc: ' , ' ./boardd ' )
( ' Add monitored proc: ' , ' python locationd/ubloxd.py ' )
boardd : 1.96 % , min : 1.96 % , max : 1.96 % , acc : 1.96 %
ubloxd . py : 0.39 % , min : 0.39 % , max : 0.39 % , acc : 0.39 %
'''
import psutil
import time
import os
import sys
import numpy as np
import argparse
import re
from collections import defaultdict
from selfdrive . manager . process_config import managed_processes
# Do statistics every 5 seconds
PRINT_INTERVAL = 5
SLEEP_INTERVAL = 0.2
monitored_proc_names = [
# android procs
' SurfaceFlinger ' , ' sensors.qcom '
] + list ( managed_processes . keys ( ) )
cpu_time_names = [ ' user ' , ' system ' , ' children_user ' , ' children_system ' ]
timer = getattr ( time , ' monotonic ' , time . time )
def get_arg_parser ( ) :
parser = argparse . ArgumentParser ( formatter_class = argparse . ArgumentDefaultsHelpFormatter )
parser . add_argument ( " proc_names " , nargs = " ? " , default = ' ' ,
help = " Process names to be monitored, comma separated " )
parser . add_argument ( " --list_all " , action = ' store_true ' ,
help = " Show all running processes ' cmdline " )
parser . add_argument ( " --detailed_times " , action = ' store_true ' ,
help = " show cpu time details (split by user, system, child user, child system) " )
return parser
if __name__ == " __main__ " :
args = get_arg_parser ( ) . parse_args ( sys . argv [ 1 : ] )
if args . list_all :
for p in psutil . process_iter ( ) :
print ( ' cmdline ' , p . cmdline ( ) , ' name ' , p . name ( ) )
sys . exit ( 0 )
if len ( args . proc_names ) > 0 :
monitored_proc_names = args . proc_names . split ( ' , ' )
monitored_procs = [ ]
stats = { }
for p in psutil . process_iter ( ) :
if p == psutil . Process ( ) :
continue
matched = any ( l for l in p . cmdline ( ) if any ( pn for pn in monitored_proc_names if re . match ( r ' .* {} .* ' . format ( pn ) , l , re . M | re . I ) ) )
if matched :
k = ' ' . join ( p . cmdline ( ) )
print ( ' Add monitored proc: ' , k )
stats [ k ] = { ' cpu_samples ' : defaultdict ( list ) , ' min ' : defaultdict ( lambda : None ) , ' max ' : defaultdict ( lambda : None ) ,
' avg ' : defaultdict ( lambda : 0.0 ) , ' last_cpu_times ' : None , ' last_sys_time ' : None }
stats [ k ] [ ' last_sys_time ' ] = timer ( )
stats [ k ] [ ' last_cpu_times ' ] = p . cpu_times ( )
monitored_procs . append ( p )
i = 0
interval_int = int ( PRINT_INTERVAL / SLEEP_INTERVAL )
while True :
for p in monitored_procs :
k = ' ' . join ( p . cmdline ( ) )
cur_sys_time = timer ( )
cur_cpu_times = p . cpu_times ( )
cpu_times = np . subtract ( cur_cpu_times , stats [ k ] [ ' last_cpu_times ' ] ) / ( cur_sys_time - stats [ k ] [ ' last_sys_time ' ] )
stats [ k ] [ ' last_sys_time ' ] = cur_sys_time
stats [ k ] [ ' last_cpu_times ' ] = cur_cpu_times
cpu_percent = 0
for num , name in enumerate ( cpu_time_names ) :
stats [ k ] [ ' cpu_samples ' ] [ name ] . append ( cpu_times [ num ] )
cpu_percent + = cpu_times [ num ]
stats [ k ] [ ' cpu_samples ' ] [ ' total ' ] . append ( cpu_percent )
time . sleep ( SLEEP_INTERVAL )
i + = 1
if i % interval_int == 0 :
l = [ ]
for k , stat in stats . items ( ) :
if len ( stat [ ' cpu_samples ' ] ) < = 0 :
continue
for name , samples in stat [ ' cpu_samples ' ] . items ( ) :
samples = np . array ( samples )
avg = samples . mean ( )
c = samples . size
min_cpu = np . amin ( samples )
max_cpu = np . amax ( samples )
if stat [ ' min ' ] [ name ] is None or min_cpu < stat [ ' min ' ] [ name ] :
stat [ ' min ' ] [ name ] = min_cpu
if stat [ ' max ' ] [ name ] is None or max_cpu > stat [ ' max ' ] [ name ] :
stat [ ' max ' ] [ name ] = max_cpu
stat [ ' avg ' ] [ name ] = ( stat [ ' avg ' ] [ name ] * ( i - c ) + avg * c ) / ( i )
stat [ ' cpu_samples ' ] [ name ] = [ ]
msg = f " avg: { stat [ ' avg ' ] [ ' total ' ] : .2% } , min: { stat [ ' min ' ] [ ' total ' ] : .2% } , max: { stat [ ' max ' ] [ ' total ' ] : .2% } { os . path . basename ( k ) } "
if args . detailed_times :
for stat_type in [ ' avg ' , ' min ' , ' max ' ] :
msg + = f " \n { stat_type } : { [ ( name + ' : ' + str ( round ( stat [ stat_type ] [ name ] * 100 , 2 ) ) ) for name in cpu_time_names ] } "
l . append ( ( os . path . basename ( k ) , stat [ ' avg ' ] [ ' total ' ] , msg ) )
l . sort ( key = lambda x : - x [ 1 ] )
for x in l :
print ( x [ 2 ] )
print ( ' avg sum: {:.2%} over {} samples {} seconds \n ' . format (
sum ( stat [ ' avg ' ] [ ' total ' ] for k , stat in stats . items ( ) ) , i , i * SLEEP_INTERVAL
) )