bootchart.c revision dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0
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
49static int
50unix_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
57static int
58unix_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
65static int
66proc_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
80typedef struct {
81    int   count;
82    int   fd;
83    char  data[FILE_BUFF_SIZE];
84} FileBuffRec, *FileBuff;
85
86static void
87file_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
93static void
94file_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
113static void
114file_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
122static void
123log_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
160static void
161close_on_exec(int  fd)
162{
163    fcntl(fd, F_SETFD, FD_CLOEXEC);
164}
165
166static void
167open_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
184static void
185do_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
206static void
207do_log_ln(FileBuff  log)
208{
209    file_buff_write(log, "\n", 1);
210}
211
212
213static void
214do_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
241static void
242do_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
294static FileBuffRec  log_stat[1];
295static FileBuffRec  log_procs[1];
296static FileBuffRec  log_disks[1];
297
298/* called to setup bootcharting */
299int   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 */
354int  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
371void  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