blob: ff4c0cf3b4093f2ed2e4d7b3319fad401e529aca [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
Elliott Hughes24627902015-02-04 10:25:09 -080023#include "bootchart.h"
Yongqin Liua197ff12014-12-05 13:45:02 +080024#include "keywords.h"
25#include "log.h"
Elliott Hughes24627902015-02-04 10:25:09 -080026
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080027#include <dirent.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080028#include <errno.h>
Elliott Hughes24627902015-02-04 10:25:09 -080029#include <fcntl.h>
30#include <stdio.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080031#include <stdlib.h>
Elliott Hughesf3cf4382015-02-03 17:12:07 -080032#include <string.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080033#include <sys/stat.h>
Elliott Hughes24627902015-02-04 10:25:09 -080034#include <time.h>
35#include <unistd.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080036
Yongqin Liua197ff12014-12-05 13:45:02 +080037#define BOOTCHART_POLLING_MS 200 /* polling period in ms */
38#define BOOTCHART_DEFAULT_TIME_SEC (2*60) /* default polling time in seconds */
39#define BOOTCHART_MAX_TIME_SEC (10*60) /* max polling time in seconds */
40
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080041#define VERSION "0.8"
42#define SAMPLE_PERIOD 0.2
43#define LOG_ROOT "/data/bootchart"
44#define LOG_STAT LOG_ROOT"/proc_stat.log"
45#define LOG_PROCS LOG_ROOT"/proc_ps.log"
46#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
47#define LOG_HEADER LOG_ROOT"/header"
48#define LOG_ACCT LOG_ROOT"/kernel_pacct"
49
Yongqin Liua197ff12014-12-05 13:45:02 +080050#define LOG_STARTFILE LOG_ROOT"/start"
51#define LOG_STOPFILE LOG_ROOT"/stop"
52
53#define FILE_BUFF_SIZE 65536
54
55struct FileBuff {
56 int count;
57 int fd;
58 char data[FILE_BUFF_SIZE];
59};
60
61static long long last_bootchart_time;
62static int g_remaining_samples;
63
64static FileBuff log_stat[1];
65static FileBuff log_procs[1];
66static FileBuff log_disks[1];
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080067
68static int
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080069proc_read(const char* filename, char* buff, size_t buffsize)
70{
71 int len = 0;
Nick Kralevich45a884f2015-02-02 14:37:22 -080072 int fd = open(filename, O_RDONLY | O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080073 if (fd >= 0) {
Elliott Hughes24627902015-02-04 10:25:09 -080074 len = TEMP_FAILURE_RETRY(read(fd, buff, buffsize-1));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080075 close(fd);
76 }
77 buff[len > 0 ? len : 0] = 0;
78 return len;
79}
80
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080081static void
Elliott Hughesc0e919c2015-02-04 14:46:36 -080082file_buff_open( FileBuff* buff, const char* path )
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080083{
84 buff->count = 0;
Yongqin Liua197ff12014-12-05 13:45:02 +080085 buff->fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0755);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080086}
87
88static void
Elliott Hughesc0e919c2015-02-04 14:46:36 -080089file_buff_write( FileBuff* buff, const void* src, int len )
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080090{
91 while (len > 0) {
92 int avail = sizeof(buff->data) - buff->count;
93 if (avail > len)
94 avail = len;
95
96 memcpy( buff->data + buff->count, src, avail );
97 len -= avail;
98 src = (char*)src + avail;
99
100 buff->count += avail;
101 if (buff->count == FILE_BUFF_SIZE) {
Elliott Hughes24627902015-02-04 10:25:09 -0800102 TEMP_FAILURE_RETRY(write(buff->fd, buff->data, buff->count));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800103 buff->count = 0;
104 }
105 }
106}
107
108static void
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800109file_buff_done( FileBuff* buff )
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800110{
111 if (buff->count > 0) {
Elliott Hughes24627902015-02-04 10:25:09 -0800112 TEMP_FAILURE_RETRY(write(buff->fd, buff->data, buff->count));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800113 buff->count = 0;
114 }
115}
116
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400117static long long
118get_uptime_jiffies()
119{
120 char buff[64];
121 long long jiffies = 0;
122
123 if (proc_read("/proc/uptime", buff, sizeof(buff)) > 0)
124 jiffies = 100LL*strtod(buff,NULL);
125
126 return jiffies;
127}
128
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800129static void
130log_header(void)
131{
132 FILE* out;
133 char cmdline[1024];
134 char uname[128];
135 char cpuinfo[128];
136 char* cpu;
137 char date[32];
138 time_t now_t = time(NULL);
139 struct tm now = *localtime(&now_t);
140 strftime(date, sizeof(date), "%x %X", &now);
141
Nick Kralevich45a884f2015-02-02 14:37:22 -0800142 out = fopen( LOG_HEADER, "we" );
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800143 if (out == NULL)
144 return;
145
146 proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
147 proc_read("/proc/version", uname, sizeof(uname));
148 proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo));
149
150 cpu = strchr( cpuinfo, ':' );
151 if (cpu) {
152 char* p = strchr(cpu, '\n');
153 cpu += 2;
154 if (p)
155 *p = 0;
156 }
157
158 fprintf(out, "version = %s\n", VERSION);
159 fprintf(out, "title = Boot chart for Android ( %s )\n", date);
160 fprintf(out, "system.uname = %s\n", uname);
161 fprintf(out, "system.release = 0.0\n");
162 fprintf(out, "system.cpu = %s\n", cpu);
163 fprintf(out, "system.kernel.options = %s\n", cmdline);
164 fclose(out);
165}
166
167static void
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800168do_log_uptime(FileBuff* log)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800169{
170 char buff[65];
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400171 int len;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800172
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400173 snprintf(buff,sizeof(buff),"%lld\n",get_uptime_jiffies());
174 len = strlen(buff);
175 file_buff_write(log, buff, len);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800176}
177
178static void
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800179do_log_ln(FileBuff* log)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800180{
181 file_buff_write(log, "\n", 1);
182}
183
184
185static void
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800186do_log_file(FileBuff* log, const char* procfile)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800187{
188 char buff[1024];
189 int fd;
190
191 do_log_uptime(log);
192
193 /* append file content */
Nick Kralevich45a884f2015-02-02 14:37:22 -0800194 fd = open(procfile,O_RDONLY|O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800195 if (fd >= 0) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800196 for (;;) {
Elliott Hughes24627902015-02-04 10:25:09 -0800197 int ret = TEMP_FAILURE_RETRY(read(fd, buff, sizeof(buff)));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800198 if (ret <= 0)
199 break;
200
201 file_buff_write(log, buff, ret);
202 if (ret < (int)sizeof(buff))
203 break;
204 }
205 close(fd);
206 }
207
208 do_log_ln(log);
209}
210
211static void
Elliott Hughesc0e919c2015-02-04 14:46:36 -0800212do_log_procs(FileBuff* log)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800213{
214 DIR* dir = opendir("/proc");
215 struct dirent* entry;
216
217 do_log_uptime(log);
218
219 while ((entry = readdir(dir)) != NULL) {
220 /* only match numeric values */
221 char* end;
222 int pid = strtol( entry->d_name, &end, 10);
223 if (end != NULL && end > entry->d_name && *end == 0) {
224 char filename[32];
225 char buff[1024];
226 char cmdline[1024];
227 int len;
228 int fd;
229
230 /* read command line and extract program name */
231 snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
232 proc_read(filename, cmdline, sizeof(cmdline));
233
234 /* read process stat line */
235 snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
Nick Kralevich45a884f2015-02-02 14:37:22 -0800236 fd = open(filename,O_RDONLY|O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800237 if (fd >= 0) {
Elliott Hughes24627902015-02-04 10:25:09 -0800238 len = TEMP_FAILURE_RETRY(read(fd, buff, sizeof(buff)-1));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800239 close(fd);
240 if (len > 0) {
241 int len2 = strlen(cmdline);
242 if (len2 > 0) {
243 /* we want to substitute the process name with its real name */
244 const char* p1;
245 const char* p2;
246 buff[len] = 0;
247 p1 = strchr(buff, '(');
248 p2 = strchr(p1, ')');
249 file_buff_write(log, buff, p1+1-buff);
250 file_buff_write(log, cmdline, strlen(cmdline));
251 file_buff_write(log, p2, strlen(p2));
252 } else {
253 /* no substitution */
254 file_buff_write(log,buff,len);
255 }
256 }
257 }
258 }
259 }
260 closedir(dir);
261 do_log_ln(log);
262}
263
Yongqin Liua197ff12014-12-05 13:45:02 +0800264int do_bootchart_init(int nargs, char **args)
265{
266 g_remaining_samples = bootchart_init();
267 if (g_remaining_samples < 0) {
268 ERROR("bootcharting init failure\n");
269 } else if (g_remaining_samples > 0) {
270 NOTICE("bootcharting started (will run for %d ms)\n", g_remaining_samples*BOOTCHART_POLLING_MS);
271 } else {
272 NOTICE("bootcharting ignored\n");
273 }
274
275 return 0;
276}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800277
278/* called to setup bootcharting */
279int bootchart_init( void )
280{
281 int ret;
282 char buff[4];
283 int timeout = 0, count = 0;
284
285 buff[0] = 0;
286 proc_read( LOG_STARTFILE, buff, sizeof(buff) );
287 if (buff[0] != 0) {
288 timeout = atoi(buff);
289 }
290 else {
291 /* when running with emulator, androidboot.bootchart=<timeout>
292 * might be passed by as kernel parameters to specify the bootchart
293 * timeout. this is useful when using -wipe-data since the /data
294 * partition is fresh
295 */
296 char cmdline[1024];
297 char* s;
298#define KERNEL_OPTION "androidboot.bootchart="
299 proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
300 s = strstr(cmdline, KERNEL_OPTION);
301 if (s) {
302 s += sizeof(KERNEL_OPTION)-1;
303 timeout = atoi(s);
304 }
305 }
306 if (timeout == 0)
307 return 0;
308
309 if (timeout > BOOTCHART_MAX_TIME_SEC)
310 timeout = BOOTCHART_MAX_TIME_SEC;
311
312 count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
313
Elliott Hughes24627902015-02-04 10:25:09 -0800314 ret = TEMP_FAILURE_RETRY(mkdir(LOG_ROOT,0755));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800315
316 file_buff_open(log_stat, LOG_STAT);
317 file_buff_open(log_procs, LOG_PROCS);
318 file_buff_open(log_disks, LOG_DISK);
319
320 /* create kernel process accounting file */
321 {
Nick Kralevich45a884f2015-02-02 14:37:22 -0800322 int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0644);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800323 if (fd >= 0) {
324 close(fd);
325 acct( LOG_ACCT );
326 }
327 }
328
329 log_header();
330 return count;
331}
332
Yongqin Liua197ff12014-12-05 13:45:02 +0800333static int bootchart_step( void )
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800334{
335 do_log_file(log_stat, "/proc/stat");
336 do_log_file(log_disks, "/proc/diskstats");
337 do_log_procs(log_procs);
338
Yongqin Liua197ff12014-12-05 13:45:02 +0800339 /* we stop when /data/bootchart/stop contains 1 */
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800340 {
341 char buff[2];
342 if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
343 return -1;
344 }
345 }
346
347 return 0;
348}
349
Yongqin Liua197ff12014-12-05 13:45:02 +0800350/* called to get time (in ms) used by bootchart */
351static long long bootchart_gettime() {
352 return 10LL*get_uptime_jiffies();
353}
354
355/* called each time you want to perform a bootchart sampling op */
356void bootchart_sample(int* timeout) {
357 if (g_remaining_samples > 0) {
358 long long current_time;
359 int elapsed_time, remaining_time;
360
361 current_time = bootchart_gettime();
362 elapsed_time = current_time - last_bootchart_time;
363
364 if (elapsed_time >= BOOTCHART_POLLING_MS) {
365 /* count missed samples */
366 while (elapsed_time >= BOOTCHART_POLLING_MS) {
367 elapsed_time -= BOOTCHART_POLLING_MS;
368 g_remaining_samples--;
369 }
370 /* count may be negative, take a sample anyway */
371 last_bootchart_time = current_time;
372 if (bootchart_step() < 0 || g_remaining_samples <= 0) {
373 bootchart_finish();
374 g_remaining_samples = 0;
375 }
376 }
377 if (g_remaining_samples > 0) {
378 remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
379 if (*timeout < 0 || *timeout > remaining_time) {
380 *timeout = remaining_time;
381 }
382 }
383 }
384}
385
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800386void bootchart_finish( void )
387{
388 unlink( LOG_STOPFILE );
389 file_buff_done(log_stat);
390 file_buff_done(log_disks);
391 file_buff_done(log_procs);
392 acct(NULL);
393}