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