Home | History | Annotate | Download | only in init
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /* this code is used to generate a boot sequence profile that can be used
     18  * with the 'bootchart' graphics generation tool. see www.bootchart.org
     19  * note that unlike the original bootchartd, this is not a Bash script but
     20  * some C code that is run right from the init script.
     21  */
     22 
     23 #include <stdio.h>
     24 #include <time.h>
     25 #include <dirent.h>
     26 #include <unistd.h>
     27 #include <fcntl.h>
     28 #include <unistd.h>
     29 #include <fcntl.h>
     30 #include <unistd.h>
     31 #include <fcntl.h>
     32 #include <errno.h>
     33 #include <stdlib.h>
     34 #include <sys/stat.h>
     35 #include "bootchart.h"
     36 
     37 #define VERSION         "0.8"
     38 #define SAMPLE_PERIOD   0.2
     39 #define LOG_ROOT        "/data/bootchart"
     40 #define LOG_STAT        LOG_ROOT"/proc_stat.log"
     41 #define LOG_PROCS       LOG_ROOT"/proc_ps.log"
     42 #define LOG_DISK        LOG_ROOT"/proc_diskstats.log"
     43 #define LOG_HEADER      LOG_ROOT"/header"
     44 #define LOG_ACCT        LOG_ROOT"/kernel_pacct"
     45 
     46 #define LOG_STARTFILE   "/data/bootchart-start"
     47 #define LOG_STOPFILE    "/data/bootchart-stop"
     48 
     49 static int
     50 unix_read(int  fd, void*  buff, int  len)
     51 {
     52     int  ret;
     53     do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR);
     54     return ret;
     55 }
     56 
     57 static int
     58 unix_write(int  fd, const void*  buff, int  len)
     59 {
     60     int  ret;
     61     do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR);
     62     return ret;
     63 }
     64 
     65 static int
     66 proc_read(const char*  filename, char* buff, size_t  buffsize)
     67 {
     68     int  len = 0;
     69     int  fd  = open(filename, O_RDONLY);
     70     if (fd >= 0) {
     71         len = unix_read(fd, buff, buffsize-1);
     72         close(fd);
     73     }
     74     buff[len > 0 ? len : 0] = 0;
     75     return len;
     76 }
     77 
     78 #define FILE_BUFF_SIZE    65536
     79 
     80 typedef struct {
     81     int   count;
     82     int   fd;
     83     char  data[FILE_BUFF_SIZE];
     84 } FileBuffRec, *FileBuff;
     85 
     86 static void
     87 file_buff_open( FileBuff  buff, const char*  path )
     88 {
     89     buff->count = 0;
     90     buff->fd    = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755);
     91 }
     92 
     93 static void
     94 file_buff_write( FileBuff  buff, const void*  src, int  len )
     95 {
     96     while (len > 0) {
     97         int  avail = sizeof(buff->data) - buff->count;
     98         if (avail > len)
     99             avail = len;
    100 
    101         memcpy( buff->data + buff->count, src, avail );
    102         len -= avail;
    103         src  = (char*)src + avail;
    104 
    105         buff->count += avail;
    106         if (buff->count == FILE_BUFF_SIZE) {
    107             unix_write( buff->fd, buff->data, buff->count );
    108             buff->count = 0;
    109         }
    110     }
    111 }
    112 
    113 static void
    114 file_buff_done( FileBuff  buff )
    115 {
    116     if (buff->count > 0) {
    117         unix_write( buff->fd, buff->data, buff->count );
    118         buff->count = 0;
    119     }
    120 }
    121 
    122 static void
    123 log_header(void)
    124 {
    125     FILE*      out;
    126     char       cmdline[1024];
    127     char       uname[128];
    128     char       cpuinfo[128];
    129     char*      cpu;
    130     char       date[32];
    131     time_t     now_t = time(NULL);
    132     struct tm  now = *localtime(&now_t);
    133     strftime(date, sizeof(date), "%x %X", &now);
    134 
    135     out = fopen( LOG_HEADER, "w" );
    136     if (out == NULL)
    137         return;
    138 
    139     proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
    140     proc_read("/proc/version", uname, sizeof(uname));
    141     proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo));
    142 
    143     cpu = strchr( cpuinfo, ':' );
    144     if (cpu) {
    145         char*  p = strchr(cpu, '\n');
    146         cpu += 2;
    147         if (p)
    148             *p = 0;
    149     }
    150 
    151     fprintf(out, "version = %s\n", VERSION);
    152     fprintf(out, "title = Boot chart for Android ( %s )\n", date);
    153     fprintf(out, "system.uname = %s\n", uname);
    154     fprintf(out, "system.release = 0.0\n");
    155     fprintf(out, "system.cpu = %s\n", cpu);
    156     fprintf(out, "system.kernel.options = %s\n", cmdline);
    157     fclose(out);
    158 }
    159 
    160 static void
    161 close_on_exec(int  fd)
    162 {
    163     fcntl(fd, F_SETFD, FD_CLOEXEC);
    164 }
    165 
    166 static void
    167 open_log_file(int*  plogfd, const char*  logfile)
    168 {
    169     int    logfd = *plogfd;
    170 
    171     /* create log file if needed */
    172     if (logfd < 0)
    173     {
    174         logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC,0755);
    175         if (logfd < 0) {
    176             *plogfd = -2;
    177             return;
    178         }
    179         close_on_exec(logfd);
    180         *plogfd = logfd;
    181     }
    182 }
    183 
    184 static void
    185 do_log_uptime(FileBuff  log)
    186 {
    187     char  buff[65];
    188     int   fd, ret, len;
    189 
    190     fd = open("/proc/uptime",O_RDONLY);
    191     if (fd >= 0) {
    192         int  ret;
    193         ret = unix_read(fd, buff, 64);
    194         close(fd);
    195         buff[64] = 0;
    196         if (ret >= 0) {
    197             long long  jiffies = 100LL*strtod(buff,NULL);
    198             int        len;
    199             snprintf(buff,sizeof(buff),"%lld\n",jiffies);
    200             len = strlen(buff);
    201             file_buff_write(log, buff, len);
    202         }
    203     }
    204 }
    205 
    206 static void
    207 do_log_ln(FileBuff  log)
    208 {
    209     file_buff_write(log, "\n", 1);
    210 }
    211 
    212 
    213 static void
    214 do_log_file(FileBuff  log, const char*  procfile)
    215 {
    216     char   buff[1024];
    217     int    fd;
    218 
    219     do_log_uptime(log);
    220 
    221     /* append file content */
    222     fd = open(procfile,O_RDONLY);
    223     if (fd >= 0) {
    224         close_on_exec(fd);
    225         for (;;) {
    226             int  ret;
    227             ret = unix_read(fd, buff, sizeof(buff));
    228             if (ret <= 0)
    229                 break;
    230 
    231             file_buff_write(log, buff, ret);
    232             if (ret < (int)sizeof(buff))
    233                 break;
    234         }
    235         close(fd);
    236     }
    237 
    238     do_log_ln(log);
    239 }
    240 
    241 static void
    242 do_log_procs(FileBuff  log)
    243 {
    244     DIR*  dir = opendir("/proc");
    245     struct dirent*  entry;
    246 
    247     do_log_uptime(log);
    248 
    249     while ((entry = readdir(dir)) != NULL) {
    250         /* only match numeric values */
    251         char*  end;
    252         int    pid = strtol( entry->d_name, &end, 10);
    253         if (end != NULL && end > entry->d_name && *end == 0) {
    254             char  filename[32];
    255             char  buff[1024];
    256             char  cmdline[1024];
    257             int   len;
    258             int   fd;
    259 
    260             /* read command line and extract program name */
    261             snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
    262             proc_read(filename, cmdline, sizeof(cmdline));
    263 
    264             /* read process stat line */
    265             snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
    266             fd = open(filename,O_RDONLY);
    267             if (fd >= 0) {
    268                len = unix_read(fd, buff, sizeof(buff)-1);
    269                close(fd);
    270                if (len > 0) {
    271                     int  len2 = strlen(cmdline);
    272                     if (len2 > 0) {
    273                         /* we want to substitute the process name with its real name */
    274                         const char*  p1;
    275                         const char*  p2;
    276                         buff[len] = 0;
    277                         p1 = strchr(buff, '(');
    278                         p2 = strchr(p1, ')');
    279                         file_buff_write(log, buff, p1+1-buff);
    280                         file_buff_write(log, cmdline, strlen(cmdline));
    281                         file_buff_write(log, p2, strlen(p2));
    282                     } else {
    283                         /* no substitution */
    284                         file_buff_write(log,buff,len);
    285                     }
    286                }
    287             }
    288         }
    289     }
    290     closedir(dir);
    291     do_log_ln(log);
    292 }
    293 
    294 static FileBuffRec  log_stat[1];
    295 static FileBuffRec  log_procs[1];
    296 static FileBuffRec  log_disks[1];
    297 
    298 /* called to setup bootcharting */
    299 int   bootchart_init( void )
    300 {
    301     int  ret;
    302     char buff[4];
    303     int  timeout = 0, count = 0;
    304 
    305     buff[0] = 0;
    306     proc_read( LOG_STARTFILE, buff, sizeof(buff) );
    307     if (buff[0] != 0) {
    308         timeout = atoi(buff);
    309     }
    310     else {
    311         /* when running with emulator, androidboot.bootchart=<timeout>
    312          * might be passed by as kernel parameters to specify the bootchart
    313          * timeout. this is useful when using -wipe-data since the /data
    314          * partition is fresh
    315          */
    316         char  cmdline[1024];
    317         char* s;
    318 #define  KERNEL_OPTION  "androidboot.bootchart="
    319         proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
    320         s = strstr(cmdline, KERNEL_OPTION);
    321         if (s) {
    322             s      += sizeof(KERNEL_OPTION)-1;
    323             timeout = atoi(s);
    324         }
    325     }
    326     if (timeout == 0)
    327         return 0;
    328 
    329     if (timeout > BOOTCHART_MAX_TIME_SEC)
    330         timeout = BOOTCHART_MAX_TIME_SEC;
    331 
    332     count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
    333 
    334     do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR);
    335 
    336     file_buff_open(log_stat,  LOG_STAT);
    337     file_buff_open(log_procs, LOG_PROCS);
    338     file_buff_open(log_disks, LOG_DISK);
    339 
    340     /* create kernel process accounting file */
    341     {
    342         int  fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC,0644);
    343         if (fd >= 0) {
    344             close(fd);
    345             acct( LOG_ACCT );
    346         }
    347     }
    348 
    349     log_header();
    350     return count;
    351 }
    352 
    353 /* called each time you want to perform a bootchart sampling op */
    354 int  bootchart_step( void )
    355 {
    356     do_log_file(log_stat,   "/proc/stat");
    357     do_log_file(log_disks,  "/proc/diskstats");
    358     do_log_procs(log_procs);
    359 
    360     /* we stop when /data/bootchart-stop contains 1 */
    361     {
    362         char  buff[2];
    363         if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
    364             return -1;
    365         }
    366     }
    367 
    368     return 0;
    369 }
    370 
    371 void  bootchart_finish( void )
    372 {
    373     unlink( LOG_STOPFILE );
    374     file_buff_done(log_stat);
    375     file_buff_done(log_disks);
    376     file_buff_done(log_procs);
    377     acct(NULL);
    378 }
    379