blob: 3d294cf9f7620a118054f1cfbd238a3fa4409d84 [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"
24
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080025#include <dirent.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080026#include <errno.h>
Elliott Hughes24627902015-02-04 10:25:09 -080027#include <fcntl.h>
28#include <stdio.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080029#include <stdlib.h>
Elliott Hughesf3cf4382015-02-03 17:12:07 -080030#include <string.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080031#include <sys/stat.h>
Elliott Hughes24627902015-02-04 10:25:09 -080032#include <time.h>
33#include <unistd.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080034
35#define VERSION "0.8"
36#define SAMPLE_PERIOD 0.2
37#define LOG_ROOT "/data/bootchart"
38#define LOG_STAT LOG_ROOT"/proc_stat.log"
39#define LOG_PROCS LOG_ROOT"/proc_ps.log"
40#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
41#define LOG_HEADER LOG_ROOT"/header"
42#define LOG_ACCT LOG_ROOT"/kernel_pacct"
43
44#define LOG_STARTFILE "/data/bootchart-start"
45#define LOG_STOPFILE "/data/bootchart-stop"
46
47static int
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080048proc_read(const char* filename, char* buff, size_t buffsize)
49{
50 int len = 0;
Nick Kralevich45a884f2015-02-02 14:37:22 -080051 int fd = open(filename, O_RDONLY | O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080052 if (fd >= 0) {
Elliott Hughes24627902015-02-04 10:25:09 -080053 len = TEMP_FAILURE_RETRY(read(fd, buff, buffsize-1));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080054 close(fd);
55 }
56 buff[len > 0 ? len : 0] = 0;
57 return len;
58}
59
60#define FILE_BUFF_SIZE 65536
61
62typedef struct {
63 int count;
64 int fd;
65 char data[FILE_BUFF_SIZE];
66} FileBuffRec, *FileBuff;
67
68static void
69file_buff_open( FileBuff buff, const char* path )
70{
71 buff->count = 0;
72 buff->fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755);
73}
74
75static void
76file_buff_write( FileBuff buff, const void* src, int len )
77{
78 while (len > 0) {
79 int avail = sizeof(buff->data) - buff->count;
80 if (avail > len)
81 avail = len;
82
83 memcpy( buff->data + buff->count, src, avail );
84 len -= avail;
85 src = (char*)src + avail;
86
87 buff->count += avail;
88 if (buff->count == FILE_BUFF_SIZE) {
Elliott Hughes24627902015-02-04 10:25:09 -080089 TEMP_FAILURE_RETRY(write(buff->fd, buff->data, buff->count));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080090 buff->count = 0;
91 }
92 }
93}
94
95static void
96file_buff_done( FileBuff buff )
97{
98 if (buff->count > 0) {
Elliott Hughes24627902015-02-04 10:25:09 -080099 TEMP_FAILURE_RETRY(write(buff->fd, buff->data, buff->count));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800100 buff->count = 0;
101 }
102}
103
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400104static long long
105get_uptime_jiffies()
106{
107 char buff[64];
108 long long jiffies = 0;
109
110 if (proc_read("/proc/uptime", buff, sizeof(buff)) > 0)
111 jiffies = 100LL*strtod(buff,NULL);
112
113 return jiffies;
114}
115
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800116static void
117log_header(void)
118{
119 FILE* out;
120 char cmdline[1024];
121 char uname[128];
122 char cpuinfo[128];
123 char* cpu;
124 char date[32];
125 time_t now_t = time(NULL);
126 struct tm now = *localtime(&now_t);
127 strftime(date, sizeof(date), "%x %X", &now);
128
Nick Kralevich45a884f2015-02-02 14:37:22 -0800129 out = fopen( LOG_HEADER, "we" );
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800130 if (out == NULL)
131 return;
132
133 proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
134 proc_read("/proc/version", uname, sizeof(uname));
135 proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo));
136
137 cpu = strchr( cpuinfo, ':' );
138 if (cpu) {
139 char* p = strchr(cpu, '\n');
140 cpu += 2;
141 if (p)
142 *p = 0;
143 }
144
145 fprintf(out, "version = %s\n", VERSION);
146 fprintf(out, "title = Boot chart for Android ( %s )\n", date);
147 fprintf(out, "system.uname = %s\n", uname);
148 fprintf(out, "system.release = 0.0\n");
149 fprintf(out, "system.cpu = %s\n", cpu);
150 fprintf(out, "system.kernel.options = %s\n", cmdline);
151 fclose(out);
152}
153
154static void
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800155do_log_uptime(FileBuff log)
156{
157 char buff[65];
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400158 int len;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800159
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400160 snprintf(buff,sizeof(buff),"%lld\n",get_uptime_jiffies());
161 len = strlen(buff);
162 file_buff_write(log, buff, len);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800163}
164
165static void
166do_log_ln(FileBuff log)
167{
168 file_buff_write(log, "\n", 1);
169}
170
171
172static void
173do_log_file(FileBuff log, const char* procfile)
174{
175 char buff[1024];
176 int fd;
177
178 do_log_uptime(log);
179
180 /* append file content */
Nick Kralevich45a884f2015-02-02 14:37:22 -0800181 fd = open(procfile,O_RDONLY|O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800182 if (fd >= 0) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800183 for (;;) {
Elliott Hughes24627902015-02-04 10:25:09 -0800184 int ret = TEMP_FAILURE_RETRY(read(fd, buff, sizeof(buff)));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800185 if (ret <= 0)
186 break;
187
188 file_buff_write(log, buff, ret);
189 if (ret < (int)sizeof(buff))
190 break;
191 }
192 close(fd);
193 }
194
195 do_log_ln(log);
196}
197
198static void
199do_log_procs(FileBuff log)
200{
201 DIR* dir = opendir("/proc");
202 struct dirent* entry;
203
204 do_log_uptime(log);
205
206 while ((entry = readdir(dir)) != NULL) {
207 /* only match numeric values */
208 char* end;
209 int pid = strtol( entry->d_name, &end, 10);
210 if (end != NULL && end > entry->d_name && *end == 0) {
211 char filename[32];
212 char buff[1024];
213 char cmdline[1024];
214 int len;
215 int fd;
216
217 /* read command line and extract program name */
218 snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
219 proc_read(filename, cmdline, sizeof(cmdline));
220
221 /* read process stat line */
222 snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
Nick Kralevich45a884f2015-02-02 14:37:22 -0800223 fd = open(filename,O_RDONLY|O_CLOEXEC);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800224 if (fd >= 0) {
Elliott Hughes24627902015-02-04 10:25:09 -0800225 len = TEMP_FAILURE_RETRY(read(fd, buff, sizeof(buff)-1));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800226 close(fd);
227 if (len > 0) {
228 int len2 = strlen(cmdline);
229 if (len2 > 0) {
230 /* we want to substitute the process name with its real name */
231 const char* p1;
232 const char* p2;
233 buff[len] = 0;
234 p1 = strchr(buff, '(');
235 p2 = strchr(p1, ')');
236 file_buff_write(log, buff, p1+1-buff);
237 file_buff_write(log, cmdline, strlen(cmdline));
238 file_buff_write(log, p2, strlen(p2));
239 } else {
240 /* no substitution */
241 file_buff_write(log,buff,len);
242 }
243 }
244 }
245 }
246 }
247 closedir(dir);
248 do_log_ln(log);
249}
250
251static FileBuffRec log_stat[1];
252static FileBuffRec log_procs[1];
253static FileBuffRec log_disks[1];
254
255/* called to setup bootcharting */
256int bootchart_init( void )
257{
258 int ret;
259 char buff[4];
260 int timeout = 0, count = 0;
261
262 buff[0] = 0;
263 proc_read( LOG_STARTFILE, buff, sizeof(buff) );
264 if (buff[0] != 0) {
265 timeout = atoi(buff);
266 }
267 else {
268 /* when running with emulator, androidboot.bootchart=<timeout>
269 * might be passed by as kernel parameters to specify the bootchart
270 * timeout. this is useful when using -wipe-data since the /data
271 * partition is fresh
272 */
273 char cmdline[1024];
274 char* s;
275#define KERNEL_OPTION "androidboot.bootchart="
276 proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
277 s = strstr(cmdline, KERNEL_OPTION);
278 if (s) {
279 s += sizeof(KERNEL_OPTION)-1;
280 timeout = atoi(s);
281 }
282 }
283 if (timeout == 0)
284 return 0;
285
286 if (timeout > BOOTCHART_MAX_TIME_SEC)
287 timeout = BOOTCHART_MAX_TIME_SEC;
288
289 count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
290
Elliott Hughes24627902015-02-04 10:25:09 -0800291 ret = TEMP_FAILURE_RETRY(mkdir(LOG_ROOT,0755));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800292
293 file_buff_open(log_stat, LOG_STAT);
294 file_buff_open(log_procs, LOG_PROCS);
295 file_buff_open(log_disks, LOG_DISK);
296
297 /* create kernel process accounting file */
298 {
Nick Kralevich45a884f2015-02-02 14:37:22 -0800299 int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0644);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800300 if (fd >= 0) {
301 close(fd);
302 acct( LOG_ACCT );
303 }
304 }
305
306 log_header();
307 return count;
308}
309
310/* called each time you want to perform a bootchart sampling op */
311int bootchart_step( void )
312{
313 do_log_file(log_stat, "/proc/stat");
314 do_log_file(log_disks, "/proc/diskstats");
315 do_log_procs(log_procs);
316
317 /* we stop when /data/bootchart-stop contains 1 */
318 {
319 char buff[2];
320 if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
321 return -1;
322 }
323 }
324
325 return 0;
326}
327
328void bootchart_finish( void )
329{
330 unlink( LOG_STOPFILE );
331 file_buff_done(log_stat);
332 file_buff_done(log_disks);
333 file_buff_done(log_procs);
334 acct(NULL);
335}
Bo (Andover) Zhang37003732014-07-24 13:11:35 -0400336
337/* called to get time (in ms) used by bootchart */
338long long bootchart_gettime( void )
339{
340 return 10LL*get_uptime_jiffies();
341}