blob: 44a24371db4e934e5ac6f6a4431e32b913060cff [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
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>
Elliott Hughesf3cf4382015-02-03 17:12:07 -080034#include <string.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080035#include <sys/stat.h>
36#include "bootchart.h"
37
38#define VERSION "0.8"
39#define SAMPLE_PERIOD 0.2
40#define LOG_ROOT "/data/bootchart"
41#define LOG_STAT LOG_ROOT"/proc_stat.log"
42#define LOG_PROCS LOG_ROOT"/proc_ps.log"
43#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
44#define LOG_HEADER LOG_ROOT"/header"
45#define LOG_ACCT LOG_ROOT"/kernel_pacct"
46
47#define LOG_STARTFILE "/data/bootchart-start"
48#define LOG_STOPFILE "/data/bootchart-stop"
49
50static int
51unix_read(int fd, void* buff, int len)
52{
53 int ret;
54 do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR);
55 return ret;
56}
57
58static int
59unix_write(int fd, const void* buff, int len)
60{
61 int ret;
62 do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR);
63 return ret;
64}
65
66static int
67proc_read(const char* filename, char* buff, size_t buffsize)
68{
69 int len = 0;
Nick Kralevich45a884f2015-02-02 14:37:22 -080070 int fd = open(filename, O_RDONLY | O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080071 if (fd >= 0) {
72 len = unix_read(fd, buff, buffsize-1);
73 close(fd);
74 }
75 buff[len > 0 ? len : 0] = 0;
76 return len;
77}
78
79#define FILE_BUFF_SIZE 65536
80
81typedef struct {
82 int count;
83 int fd;
84 char data[FILE_BUFF_SIZE];
85} FileBuffRec, *FileBuff;
86
87static void
88file_buff_open( FileBuff buff, const char* path )
89{
90 buff->count = 0;
91 buff->fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755);
92}
93
94static void
95file_buff_write( FileBuff buff, const void* src, int len )
96{
97 while (len > 0) {
98 int avail = sizeof(buff->data) - buff->count;
99 if (avail > len)
100 avail = len;
101
102 memcpy( buff->data + buff->count, src, avail );
103 len -= avail;
104 src = (char*)src + avail;
105
106 buff->count += avail;
107 if (buff->count == FILE_BUFF_SIZE) {
108 unix_write( buff->fd, buff->data, buff->count );
109 buff->count = 0;
110 }
111 }
112}
113
114static void
115file_buff_done( FileBuff buff )
116{
117 if (buff->count > 0) {
118 unix_write( buff->fd, buff->data, buff->count );
119 buff->count = 0;
120 }
121}
122
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400123static long long
124get_uptime_jiffies()
125{
126 char buff[64];
127 long long jiffies = 0;
128
129 if (proc_read("/proc/uptime", buff, sizeof(buff)) > 0)
130 jiffies = 100LL*strtod(buff,NULL);
131
132 return jiffies;
133}
134
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800135static void
136log_header(void)
137{
138 FILE* out;
139 char cmdline[1024];
140 char uname[128];
141 char cpuinfo[128];
142 char* cpu;
143 char date[32];
144 time_t now_t = time(NULL);
145 struct tm now = *localtime(&now_t);
146 strftime(date, sizeof(date), "%x %X", &now);
147
Nick Kralevich45a884f2015-02-02 14:37:22 -0800148 out = fopen( LOG_HEADER, "we" );
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800149 if (out == NULL)
150 return;
151
152 proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
153 proc_read("/proc/version", uname, sizeof(uname));
154 proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo));
155
156 cpu = strchr( cpuinfo, ':' );
157 if (cpu) {
158 char* p = strchr(cpu, '\n');
159 cpu += 2;
160 if (p)
161 *p = 0;
162 }
163
164 fprintf(out, "version = %s\n", VERSION);
165 fprintf(out, "title = Boot chart for Android ( %s )\n", date);
166 fprintf(out, "system.uname = %s\n", uname);
167 fprintf(out, "system.release = 0.0\n");
168 fprintf(out, "system.cpu = %s\n", cpu);
169 fprintf(out, "system.kernel.options = %s\n", cmdline);
170 fclose(out);
171}
172
173static void
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800174open_log_file(int* plogfd, const char* logfile)
175{
176 int logfd = *plogfd;
177
178 /* create log file if needed */
179 if (logfd < 0)
180 {
Nick Kralevich45a884f2015-02-02 14:37:22 -0800181 logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0755);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800182 if (logfd < 0) {
183 *plogfd = -2;
184 return;
185 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800186 *plogfd = logfd;
187 }
188}
189
190static void
191do_log_uptime(FileBuff log)
192{
193 char buff[65];
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400194 int len;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800195
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400196 snprintf(buff,sizeof(buff),"%lld\n",get_uptime_jiffies());
197 len = strlen(buff);
198 file_buff_write(log, buff, len);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800199}
200
201static void
202do_log_ln(FileBuff log)
203{
204 file_buff_write(log, "\n", 1);
205}
206
207
208static void
209do_log_file(FileBuff log, const char* procfile)
210{
211 char buff[1024];
212 int fd;
213
214 do_log_uptime(log);
215
216 /* append file content */
Nick Kralevich45a884f2015-02-02 14:37:22 -0800217 fd = open(procfile,O_RDONLY|O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800218 if (fd >= 0) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800219 for (;;) {
220 int ret;
221 ret = unix_read(fd, buff, sizeof(buff));
222 if (ret <= 0)
223 break;
224
225 file_buff_write(log, buff, ret);
226 if (ret < (int)sizeof(buff))
227 break;
228 }
229 close(fd);
230 }
231
232 do_log_ln(log);
233}
234
235static void
236do_log_procs(FileBuff log)
237{
238 DIR* dir = opendir("/proc");
239 struct dirent* entry;
240
241 do_log_uptime(log);
242
243 while ((entry = readdir(dir)) != NULL) {
244 /* only match numeric values */
245 char* end;
246 int pid = strtol( entry->d_name, &end, 10);
247 if (end != NULL && end > entry->d_name && *end == 0) {
248 char filename[32];
249 char buff[1024];
250 char cmdline[1024];
251 int len;
252 int fd;
253
254 /* read command line and extract program name */
255 snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
256 proc_read(filename, cmdline, sizeof(cmdline));
257
258 /* read process stat line */
259 snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
Nick Kralevich45a884f2015-02-02 14:37:22 -0800260 fd = open(filename,O_RDONLY|O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800261 if (fd >= 0) {
262 len = unix_read(fd, buff, sizeof(buff)-1);
263 close(fd);
264 if (len > 0) {
265 int len2 = strlen(cmdline);
266 if (len2 > 0) {
267 /* we want to substitute the process name with its real name */
268 const char* p1;
269 const char* p2;
270 buff[len] = 0;
271 p1 = strchr(buff, '(');
272 p2 = strchr(p1, ')');
273 file_buff_write(log, buff, p1+1-buff);
274 file_buff_write(log, cmdline, strlen(cmdline));
275 file_buff_write(log, p2, strlen(p2));
276 } else {
277 /* no substitution */
278 file_buff_write(log,buff,len);
279 }
280 }
281 }
282 }
283 }
284 closedir(dir);
285 do_log_ln(log);
286}
287
288static FileBuffRec log_stat[1];
289static FileBuffRec log_procs[1];
290static FileBuffRec log_disks[1];
291
292/* called to setup bootcharting */
293int bootchart_init( void )
294{
295 int ret;
296 char buff[4];
297 int timeout = 0, count = 0;
298
299 buff[0] = 0;
300 proc_read( LOG_STARTFILE, buff, sizeof(buff) );
301 if (buff[0] != 0) {
302 timeout = atoi(buff);
303 }
304 else {
305 /* when running with emulator, androidboot.bootchart=<timeout>
306 * might be passed by as kernel parameters to specify the bootchart
307 * timeout. this is useful when using -wipe-data since the /data
308 * partition is fresh
309 */
310 char cmdline[1024];
311 char* s;
312#define KERNEL_OPTION "androidboot.bootchart="
313 proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
314 s = strstr(cmdline, KERNEL_OPTION);
315 if (s) {
316 s += sizeof(KERNEL_OPTION)-1;
317 timeout = atoi(s);
318 }
319 }
320 if (timeout == 0)
321 return 0;
322
323 if (timeout > BOOTCHART_MAX_TIME_SEC)
324 timeout = BOOTCHART_MAX_TIME_SEC;
325
326 count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
327
328 do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR);
329
330 file_buff_open(log_stat, LOG_STAT);
331 file_buff_open(log_procs, LOG_PROCS);
332 file_buff_open(log_disks, LOG_DISK);
333
334 /* create kernel process accounting file */
335 {
Nick Kralevich45a884f2015-02-02 14:37:22 -0800336 int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0644);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800337 if (fd >= 0) {
338 close(fd);
339 acct( LOG_ACCT );
340 }
341 }
342
343 log_header();
344 return count;
345}
346
347/* called each time you want to perform a bootchart sampling op */
348int bootchart_step( void )
349{
350 do_log_file(log_stat, "/proc/stat");
351 do_log_file(log_disks, "/proc/diskstats");
352 do_log_procs(log_procs);
353
354 /* we stop when /data/bootchart-stop contains 1 */
355 {
356 char buff[2];
357 if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
358 return -1;
359 }
360 }
361
362 return 0;
363}
364
365void bootchart_finish( void )
366{
367 unlink( LOG_STOPFILE );
368 file_buff_done(log_stat);
369 file_buff_done(log_disks);
370 file_buff_done(log_procs);
371 acct(NULL);
372}
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400373
374/* called to get time (in ms) used by bootchart */
375long long bootchart_gettime( void )
376{
377 return 10LL*get_uptime_jiffies();
378}