diff options
Diffstat (limited to 'init/bootchart.c')
-rw-r--r-- | init/bootchart.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/init/bootchart.c b/init/bootchart.c new file mode 100644 index 0000000..2afe98b --- /dev/null +++ b/init/bootchart.c @@ -0,0 +1,337 @@ +/* this code is used to generate a boot sequence profile that can be used + * with the 'bootchart' graphics generation tool. see www.bootchart.org + * note that unlike the original bootchartd, this is not a Bash script but + * some C code that is run right from the init script. + */ + +#include <stdio.h> +#include <time.h> +#include <dirent.h> +#include <unistd.h> +#include <fcntl.h> +#include <unistd.h> +#include <fcntl.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/stat.h> + +#define VERSION "0.8" +#define SAMPLE_PERIOD 0.2 +#define LOG_ROOT "/tmp/bootchart" +#define LOG_STAT LOG_ROOT"/proc_stat.log" +#define LOG_PROCS LOG_ROOT"/proc_ps.log" +#define LOG_DISK LOG_ROOT"/proc_diskstats.log" +#define LOG_HEADER LOG_ROOT"/header" +#define LOG_ACCT LOG_ROOT"/kernel_pacct" + +#define LOG_STARTFILE "/data/bootchart" +#define LOG_STOPFILE "/data/bootchart-stop" + +static int +unix_read(int fd, void* buff, int len) +{ + int ret; + do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR); + return ret; +} + +static int +unix_write(int fd, const void* buff, int len) +{ + int ret; + do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR); + return ret; +} + +static int +proc_read(const char* filename, char* buff, size_t buffsize) +{ + int len = 0; + int fd = open(filename, O_RDONLY); + if (fd >= 0) { + len = unix_read(fd, buff, buffsize-1); + close(fd); + } + buff[len] = 0; + return len; +} + +#define FILE_BUFF_SIZE 65536 +#define FILE_BUFF_RESERVE (FILE_BUFF_SIZE - 4096) + +typedef struct { + int count; + int fd; + char data[FILE_BUFF_SIZE]; +} FileBuffRec, *FileBuff; + +static void +file_buff_open( FileBuff buff, const char* path ) +{ + buff->count = 0; + buff->fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755); +} + +static void +file_buff_write( FileBuff buff, const void* src, int len ) +{ + while (len > 0) { + int avail = sizeof(buff->data) - buff->count; + if (avail > len) + avail = len; + + memcpy( buff->data + buff->count, src, avail ); + len -= avail; + src = (char*)src + avail; + + buff->count += avail; + if (buff->count == FILE_BUFF_SIZE) { + unix_write( buff->fd, buff->data, buff->count ); + buff->count = 0; + } + } +} + +static void +file_buff_done( FileBuff buff ) +{ + if (buff->count > 0) { + unix_write( buff->fd, buff->data, buff->count ); + buff->count = 0; + } +} + +static void +log_header(void) +{ + FILE* out; + char cmdline[1024]; + char uname[128]; + char cpuinfo[128]; + char* cpu; + char date[32]; + time_t now_t = time(NULL); + struct tm now = *localtime(&now_t); + strftime(date, sizeof(date), "%x %X", &now); + + out = fopen( LOG_HEADER, "w" ); + if (out == NULL) + return; + + proc_read("/proc/cmdline", cmdline, sizeof(cmdline)); + proc_read("/proc/version", uname, sizeof(uname)); + proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo)); + + cpu = strchr( cpuinfo, ':' ); + if (cpu) { + char* p = strchr(cpu, '\n'); + cpu += 2; + if (p) + *p = 0; + } + + fprintf(out, "version = %s\n", VERSION); + fprintf(out, "title = Boot chart for Android ( %s )\n", date); + fprintf(out, "system.uname = %s\n", uname); + fprintf(out, "system.release = 0.0\n"); + fprintf(out, "system.cpu = %s\n", cpu); + fprintf(out, "system.kernel.options = %s\n", cmdline); + fclose(out); +} + +static void +close_on_exec(int fd) +{ + fcntl(fd, F_SETFD, FD_CLOEXEC); +} + +static void +open_log_file(int* plogfd, const char* logfile) +{ + int logfd = *plogfd; + + /* create log file if needed */ + if (logfd < 0) + { + logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC,0755); + if (logfd < 0) { + *plogfd = -2; + return; + } + close_on_exec(logfd); + *plogfd = logfd; + } +} + +static void +do_log_uptime(FileBuff log) +{ + char buff[65]; + int fd, ret, len; + + fd = open("/proc/uptime",O_RDONLY); + if (fd >= 0) { + int ret; + close_on_exec(fd); + ret = unix_read(fd, buff, 64); + close(fd); + buff[64] = 0; + if (ret >= 0) { + long long jiffies = 100LL*strtod(buff,NULL); + int len; + snprintf(buff,sizeof(buff),"%lld\n",jiffies); + len = strlen(buff); + file_buff_write(log, buff, len); + } + } +} + +static void +do_log_ln(FileBuff log) +{ + file_buff_write(log, "\n", 1); +} + + +static void +do_log_file(FileBuff log, const char* procfile) +{ + char buff[1024]; + int fd; + + do_log_uptime(log); + + /* append file content */ + fd = open(procfile,O_RDONLY); + if (fd >= 0) { + close_on_exec(fd); + for (;;) { + int ret; + ret = unix_read(fd, buff, sizeof(buff)); + if (ret <= 0) + break; + + file_buff_write(log, buff, ret); + if (ret < (int)sizeof(buff)) + break; + } + close(fd); + } + + do_log_ln(log); +} + +static void +do_log_procs(FileBuff log) +{ + DIR* dir = opendir("/proc"); + struct dirent* entry; + + do_log_uptime(log); + + while ((entry = readdir(dir)) != NULL) { + /* only match numeric values */ + char* end; + int pid = strtol( entry->d_name, &end, 10); + if (end != NULL && end > entry->d_name && *end == 0) { + char filename[32]; + char buff[1024]; + char cmdline[1024]; + int len; + int fd; + + /* read command line and extract program name */ + snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid); + proc_read(filename, cmdline, sizeof(cmdline)); + + /* read process stat line */ + snprintf(filename,sizeof(filename),"/proc/%d/stat",pid); + fd = open(filename,O_RDONLY); + if (fd >= 0) { + len = unix_read(fd, buff, sizeof(buff)-1); + close(fd); + if (len > 0) { + int len2 = strlen(cmdline); + if (len2 > 0) { + /* we want to substitute the process name with its real name */ + const char* p1; + const char* p2; + buff[len] = 0; + p1 = strchr(buff, '('); + p2 = strchr(p1, ')'); + file_buff_write(log, buff, p1+1-buff); + file_buff_write(log, cmdline, strlen(cmdline)); + file_buff_write(log, p2, strlen(p2)); + } else { + /* no substitution */ + file_buff_write(log,buff,len); + } + } + } + } + } + closedir(dir); + do_log_ln(log); +} + +static FileBuffRec log_stat[1]; +static FileBuffRec log_procs[1]; +static FileBuffRec log_disks[1]; + +/* called to setup bootcharting */ +int bootchart_init( void ) +{ + int ret; + char buff[4]; + + buff[0] = 0; + proc_read( LOG_STARTFILE, buff, sizeof(buff) ); + if (buff[0] != '1') + return -1; + + do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR); + + file_buff_open(log_stat, LOG_STAT); + file_buff_open(log_procs, LOG_PROCS); + file_buff_open(log_disks, LOG_DISK); + + /* create kernel process accounting file */ + { + int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC,0644); + if (fd >= 0) { + close(fd); + acct( LOG_ACCT ); + } + } + + log_header(); + return 0; +} + +/* called each time you want to perform a bootchart sampling op */ +int bootchart_step( void ) +{ + do_log_file(log_stat, "/proc/stat"); + do_log_file(log_disks, "/proc/diskstats"); + do_log_procs(log_procs); + + /* we stop when /data/bootchart-stop contains 1 */ + { + char buff[2]; + if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') { + return -1; + } + } + return 0; +} + +void bootchart_finish( void ) +{ + unlink( LOG_STOPFILE ); + file_buff_done(log_stat); + file_buff_done(log_disks); + file_buff_done(log_procs); + acct(NULL); +} |