2013-03-12 01:47:58 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2013-02-13 03:14:15 +04:00
/***
2013-03-12 01:47:58 +04:00
This file is part of systemd .
2013-02-13 03:14:15 +04:00
2013-06-11 20:26:03 +04:00
Copyright ( C ) 2009 - 2013 Intel Corporation
2013-02-13 03:14:15 +04:00
Authors :
Auke Kok < auke - jan . h . kok @ intel . com >
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2012-10-18 03:01:12 +04:00
# 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>
2013-01-10 23:34:58 +04:00
# include "util.h"
2014-08-18 22:59:11 +04:00
# include "time-util.h"
2013-03-29 07:41:07 +04:00
# include "strxcpyx.h"
2013-03-12 01:47:58 +04:00
# include "store.h"
# include "bootchart.h"
2014-04-24 19:50:51 +04:00
# include "cgroup-util.h"
2012-10-18 03:01:12 +04:00
/*
* 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 ] ;
2013-04-25 01:56:15 +04:00
static int skip = 0 ;
2012-10-18 03:01:12 +04:00
DIR * proc ;
2013-03-12 01:47:58 +04:00
int procfd = - 1 ;
2012-10-18 03:01:12 +04:00
2013-03-12 01:47:58 +04:00
double gettime_ns ( void ) {
2013-01-10 23:34:59 +04:00
struct timespec n ;
2012-10-18 03:01:12 +04:00
2013-01-10 23:34:59 +04:00
clock_gettime ( CLOCK_MONOTONIC , & n ) ;
2012-10-18 03:01:12 +04:00
2014-08-18 22:59:11 +04:00
return ( n . tv_sec + ( n . tv_nsec / ( double ) NSEC_PER_SEC ) ) ;
2012-10-18 03:01:12 +04:00
}
2014-07-31 12:15:39 +04:00
static double gettime_up ( void ) {
struct timespec n ;
2013-01-10 01:38:03 +04:00
2014-07-31 12:15:39 +04:00
clock_gettime ( CLOCK_BOOTTIME , & n ) ;
2014-08-18 22:59:11 +04:00
return ( n . tv_sec + ( n . tv_nsec / ( double ) NSEC_PER_SEC ) ) ;
2014-07-31 12:15:39 +04:00
}
2013-01-10 01:38:03 +04:00
2014-07-31 12:15:39 +04:00
void log_uptime ( void ) {
2013-03-12 01:47:58 +04:00
if ( arg_relative )
2014-07-31 12:15:39 +04:00
graph_start = log_start = gettime_ns ( ) ;
else {
double uptime = gettime_up ( ) ;
log_start = gettime_ns ( ) ;
2013-01-10 01:38:03 +04:00
graph_start = log_start - uptime ;
2014-07-31 12:15:39 +04:00
}
2012-10-18 03:01:12 +04:00
}
2013-03-12 01:47:58 +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
}
2013-03-29 07:41:07 +04:00
static int pid_cmdline_strscpy ( char * buffer , size_t buf_len , int pid ) {
2013-03-12 01:47:58 +04:00
char filename [ PATH_MAX ] ;
2013-04-18 11:11:22 +04:00
_cleanup_close_ int fd = - 1 ;
2013-03-12 01:47:58 +04:00
ssize_t n ;
2013-03-07 11:52:54 +04:00
2013-03-12 01:47:58 +04:00
sprintf ( filename , " %d/cmdline " , pid ) ;
fd = openat ( procfd , filename , O_RDONLY ) ;
if ( fd < 0 )
return - errno ;
2013-03-07 11:52:54 +04:00
2013-03-12 01:47:58 +04:00
n = read ( fd , buffer , buf_len - 1 ) ;
2013-03-07 11:52:54 +04:00
if ( n > 0 ) {
int i ;
for ( i = 0 ; i < n ; i + + )
if ( buffer [ i ] = = ' \0 ' )
buffer [ i ] = ' ' ;
buffer [ n ] = ' \0 ' ;
}
2013-03-12 01:47:58 +04:00
return 0 ;
2013-03-07 11:52:54 +04:00
}
2012-10-18 03:01:12 +04:00
2013-04-25 01:56:15 +04:00
void log_sample ( int sample , struct list_sample_data * * ptr ) {
2013-01-10 01:38:03 +04:00
static int vmstat ;
static int schedstat ;
2013-03-31 03:34:23 +04:00
char buf [ 4096 ] ;
2013-01-10 01:38:03 +04:00
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 ;
2013-02-13 03:36:29 +04:00
int fd ;
2013-04-25 01:56:15 +04:00
struct list_sample_data * sampledata ;
struct ps_sched_struct * ps_prev = NULL ;
sampledata = * ptr ;
2013-02-13 03:36:29 +04:00
/* all the per-process stuff goes here */
if ( ! proc ) {
/* find all processes */
proc = opendir ( " /proc " ) ;
if ( ! proc )
return ;
procfd = dirfd ( proc ) ;
} else {
rewinddir ( proc ) ;
}
2013-01-10 01:38:03 +04:00
if ( ! vmstat ) {
/* block stuff */
2013-02-13 03:36:29 +04:00
vmstat = openat ( procfd , " vmstat " , O_RDONLY ) ;
2013-01-10 01:38:03 +04:00
if ( vmstat = = - 1 ) {
2014-02-13 17:45:51 +04:00
log_error ( " Failed to open /proc/vmstat: %m " ) ;
exit ( EXIT_FAILURE ) ;
2013-01-10 01:38:03 +04:00
}
}
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 ;
2013-01-10 23:34:58 +04:00
if ( streq ( key , " pgpgin " ) )
2013-04-25 01:56:15 +04:00
sampledata - > blockstat . bi = atoi ( val ) ;
2013-01-10 23:34:58 +04:00
if ( streq ( key , " pgpgout " ) ) {
2013-04-25 01:56:15 +04:00
sampledata - > blockstat . bo = atoi ( val ) ;
2013-01-10 01:38:03 +04:00
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 */
2013-02-13 03:36:29 +04:00
schedstat = openat ( procfd , " schedstat " , O_RDONLY ) ;
2013-01-10 01:38:03 +04:00
if ( schedstat = = - 1 ) {
2014-02-13 17:45:51 +04:00
log_error ( " Failed to open /proc/schedstat: %m " ) ;
exit ( EXIT_FAILURE ) ;
2013-01-10 01:38:03 +04:00
}
}
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 ;
2013-04-25 01:56:15 +04:00
sampledata - > runtime [ c ] = atoll ( rt ) ;
sampledata - > waittime [ c ] = atoll ( wt ) ;
2013-01-10 01:38:03 +04:00
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 ;
}
2013-03-12 01:47:58 +04:00
if ( arg_entropy ) {
2013-01-10 01:38:03 +04:00
if ( ! e_fd ) {
2013-02-13 03:36:29 +04:00
e_fd = openat ( procfd , " sys/kernel/random/entropy_avail " , O_RDONLY ) ;
2013-01-10 01:38:03 +04:00
}
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-04-25 01:56:15 +04:00
sampledata - > entropy_avail = atoi ( buf ) ;
2013-01-10 17:36:42 +04:00
}
2013-01-10 01:38:03 +04:00
}
}
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 ) {
2013-04-18 11:11:22 +04:00
_cleanup_fclose_ FILE * st = NULL ;
2013-01-10 01:38:03 +04:00
char t [ 32 ] ;
struct ps_struct * parent ;
2014-02-13 17:45:51 +04:00
ps - > next_ps = new0 ( struct ps_struct , 1 ) ;
2013-01-10 01:38:03 +04:00
if ( ! ps - > next_ps ) {
2014-02-13 17:45:51 +04:00
log_oom ( ) ;
2013-01-10 01:38:03 +04:00
exit ( EXIT_FAILURE ) ;
}
ps = ps - > next_ps ;
ps - > pid = pid ;
2014-02-13 17:45:51 +04:00
ps - > sample = new0 ( struct ps_sched_struct , 1 ) ;
2013-01-10 01:38:03 +04:00
if ( ! ps - > sample ) {
2014-02-13 17:45:51 +04:00
log_oom ( ) ;
2013-01-10 01:38:03 +04:00
exit ( EXIT_FAILURE ) ;
}
2013-04-25 01:56:15 +04:00
ps - > sample - > sampledata = sampledata ;
2013-01-10 01:38:03 +04:00
pscount + + ;
/* mark our first sample */
2013-10-15 14:35:13 +04:00
ps - > first = ps - > last = ps - > sample ;
2013-04-25 01:56:15 +04:00
ps - > sample - > runtime = atoll ( rt ) ;
ps - > sample - > waittime = atoll ( wt ) ;
2013-01-10 01:38:03 +04:00
/* get name, start time */
if ( ! ps - > sched ) {
2013-02-13 03:36:29 +04:00
sprintf ( filename , " %d/sched " , pid ) ;
ps - > sched = openat ( procfd , filename , O_RDONLY ) ;
2013-01-10 01:38:03 +04:00
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 ;
2013-03-29 07:41:07 +04:00
strscpy ( ps - > name , sizeof ( ps - > name ) , key ) ;
2013-03-07 11:52:54 +04:00
/* cmdline */
2013-03-12 01:47:58 +04:00
if ( arg_show_cmdline )
2013-03-29 07:41:07 +04:00
pid_cmdline_strscpy ( ps - > name , sizeof ( ps - > name ) , pid ) ;
2013-03-07 11:52:54 +04:00
2013-01-10 01:38:03 +04:00
/* 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 ;
2014-04-24 19:50:51 +04:00
if ( arg_show_cgroup )
/* if this fails, that's OK */
cg_pid_get_path ( SYSTEMD_CGROUP_CONTROLLER ,
ps - > pid , & ps - > cgroup ) ;
2013-01-10 01:38:03 +04:00
/* ppid */
2013-02-13 03:36:29 +04:00
sprintf ( filename , " %d/stat " , pid ) ;
fd = openat ( procfd , filename , O_RDONLY ) ;
st = fdopen ( fd , " r " ) ;
2013-01-10 01:38:03 +04:00
if ( ! st )
continue ;
if ( ! fscanf ( st , " %*s %*s %*s %i " , & p ) ) {
continue ;
}
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 ;
2013-12-30 03:09:56 +04:00
if ( parent - > pid ! = ps - > ppid ) {
2013-01-10 01:38:03 +04:00
/* 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 ) {
2013-02-13 03:36:29 +04:00
sprintf ( filename , " %d/schedstat " , pid ) ;
ps - > schedstat = openat ( procfd , filename , O_RDONLY ) ;
2013-01-10 01:38:03 +04:00
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 ;
2014-02-13 17:45:51 +04:00
ps - > sample - > next = new0 ( struct ps_sched_struct , 1 ) ;
2013-04-25 01:56:15 +04:00
if ( ! ps - > sample ) {
2014-02-13 17:45:51 +04:00
log_oom ( ) ;
exit ( EXIT_FAILURE ) ;
2013-04-25 01:56:15 +04:00
}
ps - > sample - > next - > prev = ps - > sample ;
ps - > sample = ps - > sample - > next ;
ps - > last = ps - > sample ;
ps - > sample - > runtime = atoll ( rt ) ;
ps - > sample - > waittime = atoll ( wt ) ;
ps - > sample - > sampledata = sampledata ;
ps - > sample - > ps_new = ps ;
if ( ps_prev ) {
ps_prev - > cross = ps - > sample ;
}
ps_prev = ps - > sample ;
ps - > total = ( ps - > last - > runtime - ps - > first - > runtime )
/ 1000000000.0 ;
2013-01-10 01:38:03 +04:00
2013-03-12 01:47:58 +04:00
if ( ! arg_pss )
2013-01-10 01:38:03 +04:00
goto catch_rename ;
2013-04-25 01:56:15 +04:00
2013-01-10 01:38:03 +04:00
/* Pss */
if ( ! ps - > smaps ) {
2013-02-13 03:36:29 +04:00
sprintf ( filename , " %d/smaps " , pid ) ;
fd = openat ( procfd , filename , O_RDONLY ) ;
ps - > smaps = fdopen ( fd , " r " ) ;
2013-01-10 01:38:03 +04:00
if ( ! ps - > smaps )
continue ;
setvbuf ( ps - > smaps , smaps_buf , _IOFBF , sizeof ( smaps_buf ) ) ;
2013-04-25 01:56:15 +04:00
}
else {
rewind ( ps - > smaps ) ;
}
/* test to see if we need to skip another field */
if ( skip = = 0 ) {
if ( fgets ( buf , sizeof ( buf ) , ps - > smaps ) = = NULL ) {
continue ;
}
if ( fread ( buf , 1 , 28 * 15 , ps - > smaps ) ! = ( 28 * 15 ) ) {
continue ;
}
if ( buf [ 392 ] = = ' V ' ) {
skip = 2 ;
}
else {
skip = 1 ;
}
2013-01-10 01:38:03 +04:00
rewind ( ps - > smaps ) ;
}
while ( 1 ) {
int pss_kb ;
2013-04-25 01:56:15 +04:00
/* skip one line, this contains the object mapped. */
if ( fgets ( buf , sizeof ( buf ) , ps - > smaps ) = = NULL ) {
2013-01-10 01:38:03 +04:00
break ;
2013-04-25 01:56:15 +04:00
}
2013-01-10 01:38:03 +04:00
/* then there's a 28 char 14 line block */
2013-04-25 01:56:15 +04:00
if ( fread ( buf , 1 , 28 * 14 , ps - > smaps ) ! = 28 * 14 ) {
2013-01-10 01:38:03 +04:00
break ;
2013-04-25 01:56:15 +04:00
}
2013-01-10 01:38:03 +04:00
pss_kb = atoi ( & buf [ 61 ] ) ;
2013-04-25 01:56:15 +04:00
ps - > sample - > pss + = pss_kb ;
2013-01-10 01:38:03 +04:00
2013-04-25 01:56:15 +04:00
/* skip one more line if this is a newer kernel */
if ( skip = = 2 ) {
if ( fgets ( buf , sizeof ( buf ) , ps - > smaps ) = = NULL )
break ;
}
}
if ( ps - > sample - > pss > ps - > pss_max )
ps - > pss_max = ps - > 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 */
2013-03-12 01:47:58 +04:00
mod = ( arg_hz < 4.0 ) ? 4.0 : ( arg_hz / 4.0 ) ;
2013-04-25 01:56:15 +04:00
if ( ( ( samples - ps - > pid ) + pid ) % ( int ) ( mod ) = = 0 ) {
2013-01-10 01:38:03 +04:00
/* re-fetch name */
/* get name, start time */
if ( ! ps - > sched ) {
2013-02-13 03:36:29 +04:00
sprintf ( filename , " %d/sched " , pid ) ;
ps - > sched = openat ( procfd , filename , O_RDONLY ) ;
2013-01-10 01:38:03 +04:00
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 ;
2013-03-29 07:41:07 +04:00
strscpy ( ps - > name , sizeof ( ps - > name ) , key ) ;
2013-03-07 11:52:54 +04:00
/* cmdline */
2013-03-12 01:47:58 +04:00
if ( arg_show_cmdline )
2013-03-29 07:41:07 +04:00
pid_cmdline_strscpy ( ps - > name , sizeof ( ps - > name ) , pid ) ;
2013-01-10 01:38:03 +04:00
}
}
2012-10-18 03:01:12 +04:00
}