summaryrefslogtreecommitdiffstats
path: root/init/bootchart.c
diff options
context:
space:
mode:
Diffstat (limited to 'init/bootchart.c')
-rw-r--r--init/bootchart.c337
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);
+}