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