auto import from //depot/cupcake/@135843
diff --git a/init/bootchart.c b/init/bootchart.c
new file mode 100644
index 0000000..f72fcaa
--- /dev/null
+++ b/init/bootchart.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * 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 <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>
+#include "bootchart.h"
+
+#define VERSION         "0.8"
+#define SAMPLE_PERIOD   0.2
+#define LOG_ROOT        "/data/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-start"
+#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 ? len : 0] = 0;
+    return len;
+}
+
+#define FILE_BUFF_SIZE    65536
+
+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;
+        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];
+    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);
+        }
+    }
+    if (timeout == 0)
+        return 0;
+
+    if (timeout > BOOTCHART_MAX_TIME_SEC)
+        timeout = BOOTCHART_MAX_TIME_SEC;
+
+    count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
+
+    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 count;
+}
+
+/* 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);
+}