• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
unix_read(int fd,void * buff,int len)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
unix_write(int fd,const void * buff,int len)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
proc_read(const char * filename,char * buff,size_t buffsize)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
file_buff_open(FileBuff buff,const char * path)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
file_buff_write(FileBuff buff,const void * src,int len)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
file_buff_done(FileBuff buff)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
log_header(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
close_on_exec(int fd)161 close_on_exec(int  fd)
162 {
163     fcntl(fd, F_SETFD, FD_CLOEXEC);
164 }
165 
166 static void
open_log_file(int * plogfd,const char * logfile)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
do_log_uptime(FileBuff log)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
do_log_ln(FileBuff log)207 do_log_ln(FileBuff  log)
208 {
209     file_buff_write(log, "\n", 1);
210 }
211 
212 
213 static void
do_log_file(FileBuff log,const char * procfile)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
do_log_procs(FileBuff log)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 */
bootchart_init(void)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 */
bootchart_step(void)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 
bootchart_finish(void)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