2012-10-18 03:01:12 +04:00
/*
* log . c
*
* Copyright ( C ) 2009 - 2012 Intel Coproration
*
* Authors :
* Auke Kok < auke - jan . h . kok @ intel . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; version 2
* of the License .
*/
# define _GNU_SOURCE 1
# include <unistd.h>
# include <stdlib.h>
# include <limits.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <stdio.h>
# include <string.h>
# include <dirent.h>
# include <fcntl.h>
# include <time.h>
# include "bootchart.h"
/*
* Alloc a static 4 k buffer for stdio - primarily used to increase
* PSS buffering from the default 1 k stdin buffer to reduce
* read ( ) overhead .
*/
static char smaps_buf [ 4096 ] ;
DIR * proc ;
double gettime_ns ( void )
{
2013-01-10 01:38:03 +04:00
struct timespec now ;
2012-10-18 03:01:12 +04:00
2013-01-10 01:38:03 +04:00
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
2012-10-18 03:01:12 +04:00
2013-01-10 01:38:03 +04:00
return ( now . tv_sec + ( now . tv_nsec / 1000000000.0 ) ) ;
2012-10-18 03:01:12 +04:00
}
void log_uptime ( void )
{
2013-01-10 01:38:03 +04:00
FILE * f ;
char str [ 32 ] ;
double uptime ;
f = fopen ( " /proc/uptime " , " r " ) ;
if ( ! f )
return ;
if ( ! fscanf ( f , " %s %*s " , str ) ) {
fclose ( f ) ;
return ;
}
fclose ( f ) ;
uptime = strtod ( str , NULL ) ;
log_start = gettime_ns ( ) ;
/* start graph at kernel boot time */
if ( relative )
graph_start = log_start ;
else
graph_start = log_start - uptime ;
2012-10-18 03:01:12 +04:00
}
static char * bufgetline ( char * buf )
{
2013-01-10 01:38:03 +04:00
char * c ;
2012-10-18 03:01:12 +04:00
2013-01-10 01:38:03 +04:00
if ( ! buf )
return NULL ;
2012-10-18 03:01:12 +04:00
2013-01-10 01:38:03 +04:00
c = strchr ( buf , ' \n ' ) ;
if ( c )
c + + ;
return c ;
2012-10-18 03:01:12 +04:00
}
void log_sample ( int sample )
{
2013-01-10 01:38:03 +04:00
static int vmstat ;
static int schedstat ;
FILE * st ;
char buf [ 4095 ] ;
char key [ 256 ] ;
char val [ 256 ] ;
char rt [ 256 ] ;
char wt [ 256 ] ;
char * m ;
int c ;
int p ;
int mod ;
static int e_fd ;
ssize_t s ;
ssize_t n ;
struct dirent * ent ;
if ( ! vmstat ) {
/* block stuff */
vmstat = open ( " /proc/vmstat " , O_RDONLY ) ;
if ( vmstat = = - 1 ) {
perror ( " open /proc/vmstat " ) ;
exit ( EXIT_FAILURE ) ;
}
}
n = pread ( vmstat , buf , sizeof ( buf ) - 1 , 0 ) ;
if ( n < = 0 ) {
close ( vmstat ) ;
return ;
}
buf [ n ] = ' \0 ' ;
m = buf ;
while ( m ) {
if ( sscanf ( m , " %s %s " , key , val ) < 2 )
goto vmstat_next ;
if ( ! strcmp ( key , " pgpgin " ) )
blockstat [ sample ] . bi = atoi ( val ) ;
if ( ! strcmp ( key , " pgpgout " ) ) {
blockstat [ sample ] . bo = atoi ( val ) ;
break ;
}
2012-10-18 03:01:12 +04:00
vmstat_next :
2013-01-10 01:38:03 +04:00
m = bufgetline ( m ) ;
if ( ! m )
break ;
}
if ( ! schedstat ) {
/* overall CPU utilization */
schedstat = open ( " /proc/schedstat " , O_RDONLY ) ;
if ( schedstat = = - 1 ) {
perror ( " open /proc/schedstat " ) ;
exit ( EXIT_FAILURE ) ;
}
}
n = pread ( schedstat , buf , sizeof ( buf ) - 1 , 0 ) ;
if ( n < = 0 ) {
close ( schedstat ) ;
return ;
}
buf [ n ] = ' \0 ' ;
m = buf ;
while ( m ) {
if ( sscanf ( m , " %s %*s %*s %*s %*s %*s %*s %s %s " , key , rt , wt ) < 3 )
goto schedstat_next ;
if ( strstr ( key , " cpu " ) ) {
c = atoi ( ( const char * ) ( key + 3 ) ) ;
if ( c > MAXCPUS )
/* Oops, we only have room for MAXCPUS data */
break ;
cpustat [ c ] . sample [ sample ] . runtime = atoll ( rt ) ;
cpustat [ c ] . sample [ sample ] . waittime = atoll ( wt ) ;
if ( c = = cpus )
cpus = c + 1 ;
}
2012-10-18 03:01:12 +04:00
schedstat_next :
2013-01-10 01:38:03 +04:00
m = bufgetline ( m ) ;
if ( ! m )
break ;
}
if ( entropy ) {
if ( ! e_fd ) {
e_fd = open ( " /proc/sys/kernel/random/entropy_avail " , O_RDONLY ) ;
}
if ( e_fd ) {
n = pread ( e_fd , buf , sizeof ( buf ) - 1 , 0 ) ;
2013-01-10 17:36:42 +04:00
if ( n > 0 ) {
buf [ n ] = ' \0 ' ;
2013-01-10 01:38:03 +04:00
entropy_avail [ sample ] = atoi ( buf ) ;
2013-01-10 17:36:42 +04:00
}
2013-01-10 01:38:03 +04:00
}
}
/* all the per-process stuff goes here */
if ( ! proc ) {
/* find all processes */
proc = opendir ( " /proc " ) ;
if ( ! proc )
return ;
} else {
rewinddir ( proc ) ;
}
while ( ( ent = readdir ( proc ) ) ! = NULL ) {
char filename [ PATH_MAX ] ;
int pid ;
struct ps_struct * ps ;
if ( ( ent - > d_name [ 0 ] < ' 0 ' ) | | ( ent - > d_name [ 0 ] > ' 9 ' ) )
continue ;
pid = atoi ( ent - > d_name ) ;
if ( pid > = MAXPIDS )
continue ;
ps = ps_first ;
while ( ps - > next_ps ) {
ps = ps - > next_ps ;
if ( ps - > pid = = pid )
break ;
}
/* end of our LL? then append a new record */
if ( ps - > pid ! = pid ) {
char t [ 32 ] ;
struct ps_struct * parent ;
ps - > next_ps = malloc ( sizeof ( struct ps_struct ) ) ;
if ( ! ps - > next_ps ) {
perror ( " malloc(ps_struct) " ) ;
exit ( EXIT_FAILURE ) ;
}
memset ( ps - > next_ps , 0 , sizeof ( struct ps_struct ) ) ;
ps = ps - > next_ps ;
ps - > pid = pid ;
ps - > sample = malloc ( sizeof ( struct ps_sched_struct ) * ( len + 1 ) ) ;
if ( ! ps - > sample ) {
perror ( " malloc(ps_struct) " ) ;
exit ( EXIT_FAILURE ) ;
}
memset ( ps - > sample , 0 , sizeof ( struct ps_sched_struct ) * ( len + 1 ) ) ;
pscount + + ;
/* mark our first sample */
ps - > first = sample ;
/* get name, start time */
if ( ! ps - > sched ) {
sprintf ( filename , " /proc/%d/sched " , pid ) ;
ps - > sched = open ( filename , O_RDONLY ) ;
if ( ps - > sched = = - 1 )
continue ;
}
s = pread ( ps - > sched , buf , sizeof ( buf ) - 1 , 0 ) ;
if ( s < = 0 ) {
close ( ps - > sched ) ;
continue ;
}
2013-01-10 17:36:42 +04:00
buf [ s ] = ' \0 ' ;
2013-01-10 01:38:03 +04:00
if ( ! sscanf ( buf , " %s %*s %*s " , key ) )
continue ;
strncpy ( ps - > name , key , 16 ) ;
/* discard line 2 */
m = bufgetline ( buf ) ;
if ( ! m )
continue ;
m = bufgetline ( m ) ;
if ( ! m )
continue ;
if ( ! sscanf ( m , " %*s %*s %s " , t ) )
continue ;
ps - > starttime = strtod ( t , NULL ) / 1000.0 ;
/* ppid */
sprintf ( filename , " /proc/%d/stat " , pid ) ;
st = fopen ( filename , " r " ) ;
if ( ! st )
continue ;
if ( ! fscanf ( st , " %*s %*s %*s %i " , & p ) ) {
fclose ( st ) ;
continue ;
}
fclose ( st ) ;
ps - > ppid = p ;
/*
* setup child pointers
*
* these are used to paint the tree coherently later
* each parent has a LL of children , and a LL of siblings
*/
if ( pid = = 1 )
continue ; /* nothing to do for init atm */
/* kthreadd has ppid=0, which breaks our tree ordering */
if ( ps - > ppid = = 0 )
ps - > ppid = 1 ;
parent = ps_first ;
while ( ( parent - > next_ps & & parent - > pid ! = ps - > ppid ) )
parent = parent - > next_ps ;
if ( ( ! parent ) | | ( parent - > pid ! = ps - > ppid ) ) {
/* orphan */
ps - > ppid = 1 ;
parent = ps_first - > next_ps ;
}
ps - > parent = parent ;
if ( ! parent - > children ) {
/* it's the first child */
parent - > children = ps ;
} else {
/* walk all children and append */
struct ps_struct * children ;
children = parent - > children ;
while ( children - > next )
children = children - > next ;
children - > next = ps ;
}
}
/* else -> found pid, append data in ps */
/* below here is all continuous logging parts - we get here on every
* iteration */
/* rt, wt */
if ( ! ps - > schedstat ) {
sprintf ( filename , " /proc/%d/schedstat " , pid ) ;
ps - > schedstat = open ( filename , O_RDONLY ) ;
if ( ps - > schedstat = = - 1 )
continue ;
}
2013-01-10 17:36:42 +04:00
s = pread ( ps - > schedstat , buf , sizeof ( buf ) - 1 , 0 ) ;
if ( s < = 0 ) {
2013-01-10 01:38:03 +04:00
/* clean up our file descriptors - assume that the process exited */
close ( ps - > schedstat ) ;
if ( ps - > sched )
close ( ps - > sched ) ;
//if (ps->smaps)
// fclose(ps->smaps);
continue ;
}
2013-01-10 17:36:42 +04:00
buf [ s ] = ' \0 ' ;
2013-01-10 01:38:03 +04:00
if ( ! sscanf ( buf , " %s %s %*s " , rt , wt ) )
continue ;
ps - > last = sample ;
ps - > sample [ sample ] . runtime = atoll ( rt ) ;
ps - > sample [ sample ] . waittime = atoll ( wt ) ;
ps - > total = ( ps - > sample [ ps - > last ] . runtime
- ps - > sample [ ps - > first ] . runtime )
/ 1000000000.0 ;
if ( ! pss )
goto catch_rename ;
/* Pss */
if ( ! ps - > smaps ) {
sprintf ( filename , " /proc/%d/smaps " , pid ) ;
ps - > smaps = fopen ( filename , " r " ) ;
if ( ! ps - > smaps )
continue ;
setvbuf ( ps - > smaps , smaps_buf , _IOFBF , sizeof ( smaps_buf ) ) ;
} else {
rewind ( ps - > smaps ) ;
}
while ( 1 ) {
int pss_kb ;
/* skip one line, this contains the object mapped */
if ( fgets ( buf , sizeof ( buf ) , ps - > smaps ) = = NULL )
break ;
/* then there's a 28 char 14 line block */
if ( fread ( buf , 1 , 28 * 14 , ps - > smaps ) ! = 28 * 14 )
break ;
pss_kb = atoi ( & buf [ 61 ] ) ;
ps - > sample [ sample ] . pss + = pss_kb ;
}
if ( ps - > sample [ sample ] . pss > ps - > pss_max )
ps - > pss_max = ps - > sample [ sample ] . pss ;
2012-10-18 03:01:12 +04:00
catch_rename :
2013-01-10 01:38:03 +04:00
/* catch process rename, try to randomize time */
mod = ( hz < 4.0 ) ? 4.0 : ( hz / 4.0 ) ;
if ( ( ( samples - ps - > first ) + pid ) % ( int ) ( mod ) = = 0 ) {
/* re-fetch name */
/* get name, start time */
if ( ! ps - > sched ) {
sprintf ( filename , " /proc/%d/sched " , pid ) ;
ps - > sched = open ( filename , O_RDONLY ) ;
if ( ps - > sched = = - 1 )
continue ;
}
2013-01-10 17:36:42 +04:00
s = pread ( ps - > sched , buf , sizeof ( buf ) - 1 , 0 ) ;
if ( s < = 0 ) {
2013-01-10 01:38:03 +04:00
/* clean up file descriptors */
close ( ps - > sched ) ;
if ( ps - > schedstat )
close ( ps - > schedstat ) ;
//if (ps->smaps)
// fclose(ps->smaps);
continue ;
}
2013-01-10 17:36:42 +04:00
buf [ s ] = ' \0 ' ;
2013-01-10 01:38:03 +04:00
if ( ! sscanf ( buf , " %s %*s %*s " , key ) )
continue ;
strncpy ( ps - > name , key , 16 ) ;
}
}
2012-10-18 03:01:12 +04:00
}