# include <cstdio>
# include <cstdlib>
# include <climits>
# include <cassert>
# include <unistd.h>
# include <dirent.h>
# include <memory>
# include <utility>
# include <sstream>
# include <fstream>
# include <algorithm>
# include <functional>
# include <unordered_map>
# include "messaging.hpp"
# include "common/timing.h"
# include "common/utilpp.h"
namespace {
struct ProcCache {
std : : string name ;
std : : vector < std : : string > cmdline ;
std : : string exe ;
} ;
}
int main ( ) {
int err ;
PubMaster publisher ( { " procLog " } ) ;
double jiffy = sysconf ( _SC_CLK_TCK ) ;
size_t page_size = sysconf ( _SC_PAGE_SIZE ) ;
std : : unordered_map < pid_t , ProcCache > proc_cache ;
while ( 1 ) {
capnp : : MallocMessageBuilder msg ;
cereal : : Event : : Builder event = msg . initRoot < cereal : : Event > ( ) ;
event . setLogMonoTime ( nanos_since_boot ( ) ) ;
auto procLog = event . initProcLog ( ) ;
auto orphanage = msg . getOrphanage ( ) ;
// stat
{
std : : vector < capnp : : Orphan < cereal : : ProcLog : : CPUTimes > > otimes ;
std : : ifstream sstat ( " /proc/stat " ) ;
std : : string stat_line ;
while ( std : : getline ( sstat , stat_line ) ) {
if ( util : : starts_with ( stat_line , " cpu " ) ) {
// cpu total
} else if ( util : : starts_with ( stat_line , " cpu " ) ) {
// specific cpu
int id ;
unsigned long utime , ntime , stime , itime ;
unsigned long iowtime , irqtime , sirqtime ;
int count = sscanf ( stat_line . data ( ) , " cpu%d %lu %lu %lu %lu %lu %lu %lu " ,
& id , & utime , & ntime , & stime , & itime , & iowtime , & irqtime , & sirqtime ) ;
auto ltimeo = orphanage . newOrphan < cereal : : ProcLog : : CPUTimes > ( ) ;
auto ltime = ltimeo . get ( ) ;
ltime . setCpuNum ( id ) ;
ltime . setUser ( utime / jiffy ) ;
ltime . setNice ( ntime / jiffy ) ;
ltime . setSystem ( stime / jiffy ) ;
ltime . setIdle ( itime / jiffy ) ;
ltime . setIowait ( iowtime / jiffy ) ;
ltime . setIrq ( irqtime / jiffy ) ;
ltime . setSoftirq ( irqtime / jiffy ) ;
otimes . push_back ( std : : move ( ltimeo ) ) ;
} else {
break ;
}
}
auto ltimes = procLog . initCpuTimes ( otimes . size ( ) ) ;
for ( size_t i = 0 ; i < otimes . size ( ) ; i + + ) {
ltimes . adoptWithCaveats ( i , std : : move ( otimes [ i ] ) ) ;
}
}
// meminfo
{
auto mem = procLog . initMem ( ) ;
std : : ifstream smem ( " /proc/meminfo " ) ;
std : : string mem_line ;
uint64_t mem_total = 0 , mem_free = 0 , mem_available = 0 , mem_buffers = 0 ;
uint64_t mem_cached = 0 , mem_active = 0 , mem_inactive = 0 , mem_shared = 0 ;
while ( std : : getline ( smem , mem_line ) ) {
if ( util : : starts_with ( mem_line , " MemTotal: " ) ) sscanf ( mem_line . data ( ) , " MemTotal: % " SCNu64 " kB " , & mem_total ) ;
else if ( util : : starts_with ( mem_line , " MemFree: " ) ) sscanf ( mem_line . data ( ) , " MemFree: % " SCNu64 " kB " , & mem_free ) ;
else if ( util : : starts_with ( mem_line , " MemAvailable: " ) ) sscanf ( mem_line . data ( ) , " MemAvailable: % " SCNu64 " kB " , & mem_available ) ;
else if ( util : : starts_with ( mem_line , " Buffers: " ) ) sscanf ( mem_line . data ( ) , " Buffers: % " SCNu64 " kB " , & mem_buffers ) ;
else if ( util : : starts_with ( mem_line , " Cached: " ) ) sscanf ( mem_line . data ( ) , " Cached: % " SCNu64 " kB " , & mem_cached ) ;
else if ( util : : starts_with ( mem_line , " Active: " ) ) sscanf ( mem_line . data ( ) , " Active: % " SCNu64 " kB " , & mem_active ) ;
else if ( util : : starts_with ( mem_line , " Inactive: " ) ) sscanf ( mem_line . data ( ) , " Inactive: % " SCNu64 " kB " , & mem_inactive ) ;
else if ( util : : starts_with ( mem_line , " Shmem: " ) ) sscanf ( mem_line . data ( ) , " Shmem: % " SCNu64 " kB " , & mem_shared ) ;
}
mem . setTotal ( mem_total * 1024 ) ;
mem . setFree ( mem_free * 1024 ) ;
mem . setAvailable ( mem_available * 1024 ) ;
mem . setBuffers ( mem_buffers * 1024 ) ;
mem . setCached ( mem_cached * 1024 ) ;
mem . setActive ( mem_active * 1024 ) ;
mem . setInactive ( mem_inactive * 1024 ) ;
mem . setShared ( mem_shared * 1024 ) ;
}
// processes
{
std : : vector < capnp : : Orphan < cereal : : ProcLog : : Process > > oprocs ;
struct dirent * de = NULL ;
DIR * d = opendir ( " /proc " ) ;
assert ( d ) ;
while ( ( de = readdir ( d ) ) ) {
if ( ! isdigit ( de - > d_name [ 0 ] ) ) continue ;
pid_t pid = atoi ( de - > d_name ) ;
auto lproco = orphanage . newOrphan < cereal : : ProcLog : : Process > ( ) ;
auto lproc = lproco . get ( ) ;
lproc . setPid ( pid ) ;
char tcomm [ PATH_MAX ] = { 0 } ;
{
std : : string stat = util : : read_file ( util : : string_format ( " /proc/%d/stat " , pid ) ) ;
char state ;
int ppid ;
unsigned long utime , stime ;
long cutime , cstime , priority , nice , num_threads ;
unsigned long long starttime ;
unsigned long vms , rss ;
int processor ;
int count = sscanf ( stat . data ( ) ,
" %*d (%1024[^)]) %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
" %lu %lu %ld %ld %ld %ld %ld %*d %lld "
" %lu %lu %*d %*d %*d %*d %*d %*d %*d "
" %*d %*d %*d %*d %*d %*d %*d %d " ,
tcomm , & state , & ppid ,
& utime , & stime , & cutime , & cstime , & priority , & nice , & num_threads , & starttime ,
& vms , & rss , & processor ) ;
if ( count ! = 14 ) continue ;
lproc . setState ( state ) ;
lproc . setPpid ( ppid ) ;
lproc . setCpuUser ( utime / jiffy ) ;
lproc . setCpuSystem ( stime / jiffy ) ;
lproc . setCpuChildrenUser ( cutime / jiffy ) ;
lproc . setCpuChildrenSystem ( cstime / jiffy ) ;
lproc . setPriority ( priority ) ;
lproc . setNice ( nice ) ;
lproc . setNumThreads ( num_threads ) ;
lproc . setStartTime ( starttime / jiffy ) ;
lproc . setMemVms ( vms ) ;
lproc . setMemRss ( ( uint64_t ) rss * page_size ) ;
lproc . setProcessor ( processor ) ;
}
std : : string name ( tcomm ) ;
lproc . setName ( name ) ;
// populate other things from cache
auto cache_it = proc_cache . find ( pid ) ;
ProcCache cache ;
if ( cache_it ! = proc_cache . end ( ) ) {
cache = cache_it - > second ;
}
if ( cache_it = = proc_cache . end ( ) | | cache . name ! = name ) {
cache = ( ProcCache ) {
. name = name ,
. exe = util : : readlink ( util : : string_format ( " /proc/%d/exe " , pid ) ) ,
} ;
// null-delimited cmdline arguments to vector
std : : string cmdline_s = util : : read_file ( util : : string_format ( " /proc/%d/cmdline " , pid ) ) ;
const char * cmdline_p = cmdline_s . c_str ( ) ;
const char * cmdline_ep = cmdline_p + cmdline_s . size ( ) ;
// strip trailing null bytes
while ( ( cmdline_ep - 1 ) > cmdline_p & & * ( cmdline_ep - 1 ) = = 0 ) {
cmdline_ep - - ;
}
while ( cmdline_p < cmdline_ep ) {
std : : string arg ( cmdline_p ) ;
cache . cmdline . push_back ( arg ) ;
cmdline_p + = arg . size ( ) + 1 ;
}
proc_cache [ pid ] = cache ;
}
auto lcmdline = lproc . initCmdline ( cache . cmdline . size ( ) ) ;
for ( size_t i = 0 ; i < lcmdline . size ( ) ; i + + ) {
lcmdline . set ( i , cache . cmdline [ i ] ) ;
}
lproc . setExe ( cache . exe ) ;
oprocs . push_back ( std : : move ( lproco ) ) ;
}
closedir ( d ) ;
auto lprocs = procLog . initProcs ( oprocs . size ( ) ) ;
for ( size_t i = 0 ; i < oprocs . size ( ) ; i + + ) {
lprocs . adoptWithCaveats ( i , std : : move ( oprocs [ i ] ) ) ;
}
}
publisher . send ( " procLog " , msg ) ;
usleep ( 2000000 ) ; // 2 secs
}
return 0 ;
}