blob: 27c7f65c72b06c10f64e2dbf277cd3c46db667f7 [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>
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;
Nick Kralevich45a884f2015-02-02 14:37:22 -080069 int fd = open(filename, O_RDONLY | O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080070 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
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400122static long long
123get_uptime_jiffies()
124{
125 char buff[64];
126 long long jiffies = 0;
127
128 if (proc_read("/proc/uptime", buff, sizeof(buff)) > 0)
129 jiffies = 100LL*strtod(buff,NULL);
130
131 return jiffies;
132}
133
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800134static void
135log_header(void)
136{
137 FILE* out;
138 char cmdline[1024];
139 char uname[128];
140 char cpuinfo[128];
141 char* cpu;
142 char date[32];
143 time_t now_t = time(NULL);
144 struct tm now = *localtime(&now_t);
145 strftime(date, sizeof(date), "%x %X", &now);
146
Nick Kralevich45a884f2015-02-02 14:37:22 -0800147 out = fopen( LOG_HEADER, "we" );
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800148 if (out == NULL)
149 return;
150
151 proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
152 proc_read("/proc/version", uname, sizeof(uname));
153 proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo));
154
155 cpu = strchr( cpuinfo, ':' );
156 if (cpu) {
157 char* p = strchr(cpu, '\n');
158 cpu += 2;
159 if (p)
160 *p = 0;
161 }
162
163 fprintf(out, "version = %s\n", VERSION);
164 fprintf(out, "title = Boot chart for Android ( %s )\n", date);
165 fprintf(out, "system.uname = %s\n", uname);
166 fprintf(out, "system.release = 0.0\n");
167 fprintf(out, "system.cpu = %s\n", cpu);
168 fprintf(out, "system.kernel.options = %s\n", cmdline);
169 fclose(out);
170}
171
172static void
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800173open_log_file(int* plogfd, const char* logfile)
174{
175 int logfd = *plogfd;
176
177 /* create log file if needed */
178 if (logfd < 0)
179 {
Nick Kralevich45a884f2015-02-02 14:37:22 -0800180 logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0755);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800181 if (logfd < 0) {
182 *plogfd = -2;
183 return;
184 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800185 *plogfd = logfd;
186 }
187}
188
189static void
190do_log_uptime(FileBuff log)
191{
192 char buff[65];
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400193 int len;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800194
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400195 snprintf(buff,sizeof(buff),"%lld\n",get_uptime_jiffies());
196 len = strlen(buff);
197 file_buff_write(log, buff, len);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800198}
199
200static void
201do_log_ln(FileBuff log)
202{
203 file_buff_write(log, "\n", 1);
204}
205
206
207static void
208do_log_file(FileBuff log, const char* procfile)
209{
210 char buff[1024];
211 int fd;
212
213 do_log_uptime(log);
214
215 /* append file content */
Nick Kralevich45a884f2015-02-02 14:37:22 -0800216 fd = open(procfile,O_RDONLY|O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800217 if (fd >= 0) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800218 for (;;) {
219 int ret;
220 ret = unix_read(fd, buff, sizeof(buff));
221 if (ret <= 0)
222 break;
223
224 file_buff_write(log, buff, ret);
225 if (ret < (int)sizeof(buff))
226 break;
227 }
228 close(fd);
229 }
230
231 do_log_ln(log);
232}
233
234static void
235do_log_procs(FileBuff log)
236{
237 DIR* dir = opendir("/proc");
238 struct dirent* entry;
239
240 do_log_uptime(log);
241
242 while ((entry = readdir(dir)) != NULL) {
243 /* only match numeric values */
244 char* end;
245 int pid = strtol( entry->d_name, &end, 10);
246 if (end != NULL && end > entry->d_name && *end == 0) {
247 char filename[32];
248 char buff[1024];
249 char cmdline[1024];
250 int len;
251 int fd;
252
253 /* read command line and extract program name */
254 snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
255 proc_read(filename, cmdline, sizeof(cmdline));
256
257 /* read process stat line */
258 snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
Nick Kralevich45a884f2015-02-02 14:37:22 -0800259 fd = open(filename,O_RDONLY|O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800260 if (fd >= 0) {
261 len = unix_read(fd, buff, sizeof(buff)-1);
262 close(fd);
263 if (len > 0) {
264 int len2 = strlen(cmdline);
265 if (len2 > 0) {
266 /* we want to substitute the process name with its real name */
267 const char* p1;
268 const char* p2;
269 buff[len] = 0;
270 p1 = strchr(buff, '(');
271 p2 = strchr(p1, ')');
272 file_buff_write(log, buff, p1+1-buff);
273 file_buff_write(log, cmdline, strlen(cmdline));
274 file_buff_write(log, p2, strlen(p2));
275 } else {
276 /* no substitution */
277 file_buff_write(log,buff,len);
278 }
279 }
280 }
281 }
282 }
283 closedir(dir);
284 do_log_ln(log);
285}
286
287static FileBuffRec log_stat[1];
288static FileBuffRec log_procs[1];
289static FileBuffRec log_disks[1];
290
291/* called to setup bootcharting */
292int bootchart_init( void )
293{
294 int ret;
295 char buff[4];
296 int timeout = 0, count = 0;
297
298 buff[0] = 0;
299 proc_read( LOG_STARTFILE, buff, sizeof(buff) );
300 if (buff[0] != 0) {
301 timeout = atoi(buff);
302 }
303 else {
304 /* when running with emulator, androidboot.bootchart=<timeout>
305 * might be passed by as kernel parameters to specify the bootchart
306 * timeout. this is useful when using -wipe-data since the /data
307 * partition is fresh
308 */
309 char cmdline[1024];
310 char* s;
311#define KERNEL_OPTION "androidboot.bootchart="
312 proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
313 s = strstr(cmdline, KERNEL_OPTION);
314 if (s) {
315 s += sizeof(KERNEL_OPTION)-1;
316 timeout = atoi(s);
317 }
318 }
319 if (timeout == 0)
320 return 0;
321
322 if (timeout > BOOTCHART_MAX_TIME_SEC)
323 timeout = BOOTCHART_MAX_TIME_SEC;
324
325 count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
326
327 do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR);
328
329 file_buff_open(log_stat, LOG_STAT);
330 file_buff_open(log_procs, LOG_PROCS);
331 file_buff_open(log_disks, LOG_DISK);
332
333 /* create kernel process accounting file */
334 {
Nick Kralevich45a884f2015-02-02 14:37:22 -0800335 int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0644);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800336 if (fd >= 0) {
337 close(fd);
338 acct( LOG_ACCT );
339 }
340 }
341
342 log_header();
343 return count;
344}
345
346/* called each time you want to perform a bootchart sampling op */
347int bootchart_step( void )
348{
349 do_log_file(log_stat, "/proc/stat");
350 do_log_file(log_disks, "/proc/diskstats");
351 do_log_procs(log_procs);
352
353 /* we stop when /data/bootchart-stop contains 1 */
354 {
355 char buff[2];
356 if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
357 return -1;
358 }
359 }
360
361 return 0;
362}
363
364void bootchart_finish( void )
365{
366 unlink( LOG_STOPFILE );
367 file_buff_done(log_stat);
368 file_buff_done(log_disks);
369 file_buff_done(log_procs);
370 acct(NULL);
371}
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400372
373/* called to get time (in ms) used by bootchart */
374long long bootchart_gettime( void )
375{
376 return 10LL*get_uptime_jiffies();
377}