bootchart.c revision 4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53
1/* this code is used to generate a boot sequence profile that can be used 2 * with the 'bootchart' graphics generation tool. see www.bootchart.org 3 * note that unlike the original bootchartd, this is not a Bash script but 4 * some C code that is run right from the init script. 5 */ 6 7#include <stdio.h> 8#include <time.h> 9#include <dirent.h> 10#include <unistd.h> 11#include <fcntl.h> 12#include <unistd.h> 13#include <fcntl.h> 14#include <unistd.h> 15#include <fcntl.h> 16#include <errno.h> 17#include <stdlib.h> 18#include <sys/stat.h> 19 20#define VERSION "0.8" 21#define SAMPLE_PERIOD 0.2 22#define LOG_ROOT "/tmp/bootchart" 23#define LOG_STAT LOG_ROOT"/proc_stat.log" 24#define LOG_PROCS LOG_ROOT"/proc_ps.log" 25#define LOG_DISK LOG_ROOT"/proc_diskstats.log" 26#define LOG_HEADER LOG_ROOT"/header" 27#define LOG_ACCT LOG_ROOT"/kernel_pacct" 28 29#define LOG_STARTFILE "/data/bootchart" 30#define LOG_STOPFILE "/data/bootchart-stop" 31 32static int 33unix_read(int fd, void* buff, int len) 34{ 35 int ret; 36 do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR); 37 return ret; 38} 39 40static int 41unix_write(int fd, const void* buff, int len) 42{ 43 int ret; 44 do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR); 45 return ret; 46} 47 48static int 49proc_read(const char* filename, char* buff, size_t buffsize) 50{ 51 int len = 0; 52 int fd = open(filename, O_RDONLY); 53 if (fd >= 0) { 54 len = unix_read(fd, buff, buffsize-1); 55 close(fd); 56 } 57 buff[len] = 0; 58 return len; 59} 60 61#define FILE_BUFF_SIZE 65536 62#define FILE_BUFF_RESERVE (FILE_BUFF_SIZE - 4096) 63 64typedef struct { 65 int count; 66 int fd; 67 char data[FILE_BUFF_SIZE]; 68} FileBuffRec, *FileBuff; 69 70static void 71file_buff_open( FileBuff buff, const char* path ) 72{ 73 buff->count = 0; 74 buff->fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755); 75} 76 77static void 78file_buff_write( FileBuff buff, const void* src, int len ) 79{ 80 while (len > 0) { 81 int avail = sizeof(buff->data) - buff->count; 82 if (avail > len) 83 avail = len; 84 85 memcpy( buff->data + buff->count, src, avail ); 86 len -= avail; 87 src = (char*)src + avail; 88 89 buff->count += avail; 90 if (buff->count == FILE_BUFF_SIZE) { 91 unix_write( buff->fd, buff->data, buff->count ); 92 buff->count = 0; 93 } 94 } 95} 96 97static void 98file_buff_done( FileBuff buff ) 99{ 100 if (buff->count > 0) { 101 unix_write( buff->fd, buff->data, buff->count ); 102 buff->count = 0; 103 } 104} 105 106static void 107log_header(void) 108{ 109 FILE* out; 110 char cmdline[1024]; 111 char uname[128]; 112 char cpuinfo[128]; 113 char* cpu; 114 char date[32]; 115 time_t now_t = time(NULL); 116 struct tm now = *localtime(&now_t); 117 strftime(date, sizeof(date), "%x %X", &now); 118 119 out = fopen( LOG_HEADER, "w" ); 120 if (out == NULL) 121 return; 122 123 proc_read("/proc/cmdline", cmdline, sizeof(cmdline)); 124 proc_read("/proc/version", uname, sizeof(uname)); 125 proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo)); 126 127 cpu = strchr( cpuinfo, ':' ); 128 if (cpu) { 129 char* p = strchr(cpu, '\n'); 130 cpu += 2; 131 if (p) 132 *p = 0; 133 } 134 135 fprintf(out, "version = %s\n", VERSION); 136 fprintf(out, "title = Boot chart for Android ( %s )\n", date); 137 fprintf(out, "system.uname = %s\n", uname); 138 fprintf(out, "system.release = 0.0\n"); 139 fprintf(out, "system.cpu = %s\n", cpu); 140 fprintf(out, "system.kernel.options = %s\n", cmdline); 141 fclose(out); 142} 143 144static void 145close_on_exec(int fd) 146{ 147 fcntl(fd, F_SETFD, FD_CLOEXEC); 148} 149 150static void 151open_log_file(int* plogfd, const char* logfile) 152{ 153 int logfd = *plogfd; 154 155 /* create log file if needed */ 156 if (logfd < 0) 157 { 158 logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC,0755); 159 if (logfd < 0) { 160 *plogfd = -2; 161 return; 162 } 163 close_on_exec(logfd); 164 *plogfd = logfd; 165 } 166} 167 168static void 169do_log_uptime(FileBuff log) 170{ 171 char buff[65]; 172 int fd, ret, len; 173 174 fd = open("/proc/uptime",O_RDONLY); 175 if (fd >= 0) { 176 int ret; 177 close_on_exec(fd); 178 ret = unix_read(fd, buff, 64); 179 close(fd); 180 buff[64] = 0; 181 if (ret >= 0) { 182 long long jiffies = 100LL*strtod(buff,NULL); 183 int len; 184 snprintf(buff,sizeof(buff),"%lld\n",jiffies); 185 len = strlen(buff); 186 file_buff_write(log, buff, len); 187 } 188 } 189} 190 191static void 192do_log_ln(FileBuff log) 193{ 194 file_buff_write(log, "\n", 1); 195} 196 197 198static void 199do_log_file(FileBuff log, const char* procfile) 200{ 201 char buff[1024]; 202 int fd; 203 204 do_log_uptime(log); 205 206 /* append file content */ 207 fd = open(procfile,O_RDONLY); 208 if (fd >= 0) { 209 close_on_exec(fd); 210 for (;;) { 211 int ret; 212 ret = unix_read(fd, buff, sizeof(buff)); 213 if (ret <= 0) 214 break; 215 216 file_buff_write(log, buff, ret); 217 if (ret < (int)sizeof(buff)) 218 break; 219 } 220 close(fd); 221 } 222 223 do_log_ln(log); 224} 225 226static void 227do_log_procs(FileBuff log) 228{ 229 DIR* dir = opendir("/proc"); 230 struct dirent* entry; 231 232 do_log_uptime(log); 233 234 while ((entry = readdir(dir)) != NULL) { 235 /* only match numeric values */ 236 char* end; 237 int pid = strtol( entry->d_name, &end, 10); 238 if (end != NULL && end > entry->d_name && *end == 0) { 239 char filename[32]; 240 char buff[1024]; 241 char cmdline[1024]; 242 int len; 243 int fd; 244 245 /* read command line and extract program name */ 246 snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid); 247 proc_read(filename, cmdline, sizeof(cmdline)); 248 249 /* read process stat line */ 250 snprintf(filename,sizeof(filename),"/proc/%d/stat",pid); 251 fd = open(filename,O_RDONLY); 252 if (fd >= 0) { 253 len = unix_read(fd, buff, sizeof(buff)-1); 254 close(fd); 255 if (len > 0) { 256 int len2 = strlen(cmdline); 257 if (len2 > 0) { 258 /* we want to substitute the process name with its real name */ 259 const char* p1; 260 const char* p2; 261 buff[len] = 0; 262 p1 = strchr(buff, '('); 263 p2 = strchr(p1, ')'); 264 file_buff_write(log, buff, p1+1-buff); 265 file_buff_write(log, cmdline, strlen(cmdline)); 266 file_buff_write(log, p2, strlen(p2)); 267 } else { 268 /* no substitution */ 269 file_buff_write(log,buff,len); 270 } 271 } 272 } 273 } 274 } 275 closedir(dir); 276 do_log_ln(log); 277} 278 279static FileBuffRec log_stat[1]; 280static FileBuffRec log_procs[1]; 281static FileBuffRec log_disks[1]; 282 283/* called to setup bootcharting */ 284int bootchart_init( void ) 285{ 286 int ret; 287 char buff[4]; 288 289 buff[0] = 0; 290 proc_read( LOG_STARTFILE, buff, sizeof(buff) ); 291 if (buff[0] != '1') 292 return -1; 293 294 do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR); 295 296 file_buff_open(log_stat, LOG_STAT); 297 file_buff_open(log_procs, LOG_PROCS); 298 file_buff_open(log_disks, LOG_DISK); 299 300 /* create kernel process accounting file */ 301 { 302 int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC,0644); 303 if (fd >= 0) { 304 close(fd); 305 acct( LOG_ACCT ); 306 } 307 } 308 309 log_header(); 310 return 0; 311} 312 313/* called each time you want to perform a bootchart sampling op */ 314int bootchart_step( void ) 315{ 316 do_log_file(log_stat, "/proc/stat"); 317 do_log_file(log_disks, "/proc/diskstats"); 318 do_log_procs(log_procs); 319 320 /* we stop when /data/bootchart-stop contains 1 */ 321 { 322 char buff[2]; 323 if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') { 324 return -1; 325 } 326 } 327 return 0; 328} 329 330void bootchart_finish( void ) 331{ 332 unlink( LOG_STOPFILE ); 333 file_buff_done(log_stat); 334 file_buff_done(log_disks); 335 file_buff_done(log_procs); 336 acct(NULL); 337} 338