Further refactoring of the bootchart code.

Change-Id: Ifed6ae8d481b605139fd27799574de4c2d0f5908
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index ff4c0cf..e97335d 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -14,15 +14,10 @@
  * limitations under the License.
  */
 
-/* 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 "bootchart.h"
 #include "keywords.h"
 #include "log.h"
+#include "property_service.h"
 
 #include <dirent.h>
 #include <errno.h>
@@ -31,15 +26,14 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <sys/utsname.h>
 #include <time.h>
 #include <unistd.h>
 
-#define BOOTCHART_POLLING_MS             200  /* polling period in ms */
-#define BOOTCHART_DEFAULT_TIME_SEC    (2*60)  /* default polling time in seconds */
-#define BOOTCHART_MAX_TIME_SEC       (10*60)  /* max polling time in seconds */
+#include <string>
 
-#define VERSION         "0.8"
-#define SAMPLE_PERIOD   0.2
+#include <utils/file.h>
+
 #define LOG_ROOT        "/data/bootchart"
 #define LOG_STAT        LOG_ROOT"/proc_stat.log"
 #define LOG_PROCS       LOG_ROOT"/proc_ps.log"
@@ -50,257 +44,133 @@
 #define LOG_STARTFILE   LOG_ROOT"/start"
 #define LOG_STOPFILE    LOG_ROOT"/stop"
 
-#define FILE_BUFF_SIZE    65536
+// Polling period in ms.
+static const int BOOTCHART_POLLING_MS = 200;
 
-struct FileBuff {
-    int   count;
-    int   fd;
-    char  data[FILE_BUFF_SIZE];
-};
+// Default polling time in seconds.
+static const int BOOTCHART_DEFAULT_TIME_SEC = 2*60;
 
-static long long last_bootchart_time;
+// Max polling time in seconds.
+static const int BOOTCHART_MAX_TIME_SEC = 10*60;
+
+static long long g_last_bootchart_time;
 static int g_remaining_samples;
 
-static FileBuff log_stat[1];
-static FileBuff log_procs[1];
-static FileBuff log_disks[1];
+static FILE* log_stat;
+static FILE* log_procs;
+static FILE* log_disks;
 
-static int
-proc_read(const char*  filename, char* buff, size_t  buffsize)
-{
-    int  len = 0;
-    int  fd  = open(filename, O_RDONLY | O_CLOEXEC);
-    if (fd >= 0) {
-        len = TEMP_FAILURE_RETRY(read(fd, buff, buffsize-1));
-        close(fd);
+static long long get_uptime_jiffies() {
+    std::string uptime;
+    if (!android::ReadFileToString("/proc/uptime", &uptime)) {
+        return 0;
     }
-    buff[len > 0 ? len : 0] = 0;
-    return len;
+    return 100LL * strtod(uptime.c_str(), NULL);
 }
 
-static void
-file_buff_open( FileBuff*  buff, const char*  path )
-{
-    buff->count = 0;
-    buff->fd    = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0755);
-}
+static void log_header() {
+    char date[32];
+    time_t now_t = time(NULL);
+    struct tm now = *localtime(&now_t);
+    strftime(date, sizeof(date), "%F %T", &now);
 
-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) {
-            TEMP_FAILURE_RETRY(write(buff->fd, buff->data, buff->count));
-            buff->count = 0;
-        }
-    }
-}
-
-static void
-file_buff_done( FileBuff*  buff )
-{
-    if (buff->count > 0) {
-        TEMP_FAILURE_RETRY(write(buff->fd, buff->data, buff->count));
-        buff->count = 0;
-    }
-}
-
-static long long
-get_uptime_jiffies()
-{
-    char       buff[64];
-    long long  jiffies = 0;
-
-    if (proc_read("/proc/uptime", buff, sizeof(buff)) > 0)
-        jiffies = 100LL*strtod(buff,NULL);
-
-    return jiffies;
-}
-
-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, "we" );
-    if (out == NULL)
+    utsname uts;
+    if (uname(&uts) == -1) {
         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);
+    char fingerprint[PROP_VALUE_MAX];
+    if (property_get("ro.build.fingerprint", fingerprint) == -1) {
+        return;
+    }
+
+    std::string kernel_cmdline;
+    android::ReadFileToString("/proc/cmdline", &kernel_cmdline);
+
+    FILE* out = fopen(LOG_HEADER, "we");
+    if (out == NULL) {
+        return;
+    }
+    fprintf(out, "version = Android init 0.8 " __TIME__  "\n");
+    fprintf(out, "title = Boot chart for Android (%s)\n", date);
+    fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
+    fprintf(out, "system.release = %s\n", fingerprint);
+    // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
+    fprintf(out, "system.cpu = %s\n", uts.machine);
+    fprintf(out, "system.kernel.options = %s\n", kernel_cmdline.c_str());
     fclose(out);
 }
 
-static void
-do_log_uptime(FileBuff*  log)
-{
-    char  buff[65];
-    int   len;
-
-    snprintf(buff,sizeof(buff),"%lld\n",get_uptime_jiffies());
-    len = strlen(buff);
-    file_buff_write(log, buff, len);
+static void do_log_uptime(FILE* log) {
+    fprintf(log, "%lld\n", get_uptime_jiffies());
 }
 
-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;
-
+static void do_log_file(FILE* log, const char* procfile) {
     do_log_uptime(log);
 
-    /* append file content */
-    fd = open(procfile,O_RDONLY|O_CLOEXEC);
-    if (fd >= 0) {
-        for (;;) {
-            int ret = TEMP_FAILURE_RETRY(read(fd, buff, sizeof(buff)));
-            if (ret <= 0)
-                break;
-
-            file_buff_write(log, buff, ret);
-            if (ret < (int)sizeof(buff))
-                break;
-        }
-        close(fd);
+    std::string content;
+    if (android::ReadFileToString(procfile, &content)) {
+        fprintf(log, "%s\n", content.c_str());
     }
-
-    do_log_ln(log);
 }
 
-static void
-do_log_procs(FileBuff*  log)
-{
-    DIR*  dir = opendir("/proc");
-    struct dirent*  entry;
-
+static void do_log_procs(FILE* log) {
     do_log_uptime(log);
 
+    DIR* dir = opendir("/proc");
+    struct dirent* entry;
     while ((entry = readdir(dir)) != NULL) {
-        /* only match numeric values */
-        char*  end;
-        int    pid = strtol( entry->d_name, &end, 10);
+        // 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;
+            char filename[32];
 
-            /* read command line and extract program name */
-            snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
-            proc_read(filename, cmdline, sizeof(cmdline));
+            // /proc/<pid>/stat only has truncated task names, so get the full
+            // name from /proc/<pid>/cmdline.
+            snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
+            std::string cmdline;
+            android::ReadFileToString(filename, &cmdline);
+            const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
 
-            /* read process stat line */
-            snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
-            fd = open(filename,O_RDONLY|O_CLOEXEC);
-            if (fd >= 0) {
-               len = TEMP_FAILURE_RETRY(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);
+            // Read process stat line.
+            snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
+            std::string stat;
+            if (android::ReadFileToString(filename, &stat)) {
+                if (!cmdline.empty()) {
+                    // Substitute the process name with its real name.
+                    size_t open = stat.find('(');
+                    size_t close = stat.find_last_of(')');
+                    if (open != std::string::npos && close != std::string::npos) {
+                        stat.replace(open + 1, close - open - 1, full_name);
                     }
-               }
+                }
+                fputs(stat.c_str(), log);
             }
         }
     }
     closedir(dir);
-    do_log_ln(log);
+
+    fputc('\n', log);
 }
 
-int do_bootchart_init(int nargs, char **args)
-{
-    g_remaining_samples = bootchart_init();
-    if (g_remaining_samples < 0) {
-        ERROR("bootcharting init failure\n");
-    } else if (g_remaining_samples > 0) {
-        NOTICE("bootcharting started (will run for %d ms)\n", g_remaining_samples*BOOTCHART_POLLING_MS);
+static int bootchart_init() {
+    int timeout = 0;
+
+    std::string start;
+    android::ReadFileToString(LOG_STARTFILE, &start);
+    if (!start.empty()) {
+        timeout = atoi(start.c_str());
     } else {
-        NOTICE("bootcharting ignored\n");
-    }
-
-    return 0;
-}
-
-/* called to setup bootcharting */
-int   bootchart_init( void )
-{
-    int  ret;
-    char buff[4];
-    int  timeout = 0, count = 0;
-
-    buff[0] = 0;
-    proc_read( LOG_STARTFILE, buff, sizeof(buff) );
-    if (buff[0] != 0) {
-        timeout = atoi(buff);
-    }
-    else {
-        /* when running with emulator, androidboot.bootchart=<timeout>
-         * might be passed by as kernel parameters to specify the bootchart
-         * timeout. this is useful when using -wipe-data since the /data
-         * partition is fresh
-         */
-        char  cmdline[1024];
-        char* s;
-#define  KERNEL_OPTION  "androidboot.bootchart="
-        proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
-        s = strstr(cmdline, KERNEL_OPTION);
-        if (s) {
-            s      += sizeof(KERNEL_OPTION)-1;
-            timeout = atoi(s);
+        // When running with emulator, androidboot.bootchart=<timeout>
+        // might be passed by as kernel parameters to specify the bootchart
+        // timeout. this is useful when using -wipe-data since the /data
+        // partition is fresh.
+        std::string cmdline;
+        android::ReadFileToString("/proc/cmdline", &cmdline);
+#define KERNEL_OPTION  "androidboot.bootchart="
+        if (strstr(cmdline.c_str(), KERNEL_OPTION) != NULL) {
+            timeout = atoi(cmdline.c_str() + sizeof(KERNEL_OPTION) - 1);
         }
     }
     if (timeout == 0)
@@ -309,15 +179,25 @@
     if (timeout > BOOTCHART_MAX_TIME_SEC)
         timeout = BOOTCHART_MAX_TIME_SEC;
 
-    count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
+    int count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
 
-    ret = TEMP_FAILURE_RETRY(mkdir(LOG_ROOT,0755));
+    log_stat = fopen(LOG_STAT, "we");
+    if (log_stat == NULL) {
+        return -1;
+    }
+    log_procs = fopen(LOG_PROCS, "we");
+    if (log_procs == NULL) {
+        fclose(log_stat);
+        return -1;
+    }
+    log_disks = fopen(LOG_DISK, "we");
+    if (log_disks == NULL) {
+        fclose(log_stat);
+        fclose(log_procs);
+        return -1;
+    }
 
-    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 */
+    // Create kernel process accounting file.
     {
         int  fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0644);
         if (fd >= 0) {
@@ -330,18 +210,27 @@
     return count;
 }
 
-static int  bootchart_step( void )
-{
+int do_bootchart_init(int nargs, char** args) {
+    g_remaining_samples = bootchart_init();
+    if (g_remaining_samples < 0) {
+        ERROR("bootcharting init failure: %s\n", strerror(errno));
+    } else if (g_remaining_samples > 0) {
+        NOTICE("bootcharting started (will run for %d ms)\n", g_remaining_samples*BOOTCHART_POLLING_MS);
+    } else {
+        NOTICE("bootcharting ignored\n");
+    }
+    return 0;
+}
+
+static int bootchart_step() {
     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;
-        }
+    // Stop if /data/bootchart/stop contains 1.
+    std::string stop;
+    if (android::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
+        return -1;
     }
 
     return 0;
@@ -352,42 +241,40 @@
     return 10LL*get_uptime_jiffies();
 }
 
-/* called each time you want to perform a bootchart sampling op */
-void bootchart_sample(int* timeout) {
-    if (g_remaining_samples > 0) {
-        long long current_time;
-        int elapsed_time, remaining_time;
-
-        current_time = bootchart_gettime();
-        elapsed_time = current_time - last_bootchart_time;
-
-        if (elapsed_time >= BOOTCHART_POLLING_MS) {
-            /* count missed samples */
-            while (elapsed_time >= BOOTCHART_POLLING_MS) {
-                elapsed_time -= BOOTCHART_POLLING_MS;
-                g_remaining_samples--;
-            }
-            /* count may be negative, take a sample anyway */
-            last_bootchart_time = current_time;
-            if (bootchart_step() < 0 || g_remaining_samples <= 0) {
-                bootchart_finish();
-                g_remaining_samples = 0;
-            }
-        }
-        if (g_remaining_samples > 0) {
-            remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
-            if (*timeout < 0 || *timeout > remaining_time) {
-                *timeout = remaining_time;
-            }
-        }
-    }
+static void bootchart_finish() {
+    unlink(LOG_STOPFILE);
+    fclose(log_stat);
+    fclose(log_disks);
+    fclose(log_procs);
+    acct(NULL);
 }
 
-void  bootchart_finish( void )
-{
-    unlink( LOG_STOPFILE );
-    file_buff_done(log_stat);
-    file_buff_done(log_disks);
-    file_buff_done(log_procs);
-    acct(NULL);
+void bootchart_sample(int* timeout) {
+    // Do we have any more bootcharting to do?
+    if (g_remaining_samples <= 0) {
+        return;
+    }
+
+    long long current_time = bootchart_gettime();
+    int elapsed_time = current_time - g_last_bootchart_time;
+
+    if (elapsed_time >= BOOTCHART_POLLING_MS) {
+        /* count missed samples */
+        while (elapsed_time >= BOOTCHART_POLLING_MS) {
+            elapsed_time -= BOOTCHART_POLLING_MS;
+            g_remaining_samples--;
+        }
+        /* count may be negative, take a sample anyway */
+        g_last_bootchart_time = current_time;
+        if (bootchart_step() < 0 || g_remaining_samples <= 0) {
+            bootchart_finish();
+            g_remaining_samples = 0;
+        }
+    }
+    if (g_remaining_samples > 0) {
+        int remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
+        if (*timeout < 0 || *timeout > remaining_time) {
+            *timeout = remaining_time;
+        }
+    }
 }