Further refactoring of the bootchart code.

Change-Id: Ifed6ae8d481b605139fd27799574de4c2d0f5908
diff --git a/init/README.BOOTCHART b/init/README.BOOTCHART
deleted file mode 100644
index 245e452..0000000
--- a/init/README.BOOTCHART
+++ /dev/null
@@ -1,46 +0,0 @@
-This version of init contains code to perform "bootcharting", i.e. generating log
-files that can be later processed by the tools provided by www.bootchart.org.
-
-On the emulator, use the new -bootchart <timeout> option to boot with bootcharting
-activated for <timeout> seconds.
-
-Otherwise, flash your device, and start it. Then create a file on the /data partition
-with a command like the following:
-
-  adb shell 'echo $TIMEOUT > /data/bootchart/start'
-
-Where the value of $TIMEOUT corresponds to the wanted bootcharted period in seconds;
-for example, to bootchart for 2 minutes, do:
-
-  adb shell 'echo 120 > /data/bootchart/start'
-
-Reboot your device, bootcharting will begin and stop after the period you gave.
-You can also stop the bootcharting at any moment by doing the following:
-
-  adb shell 'echo 1 > /data/bootchart/stop'
-
-Note that /data/bootchart/stop is deleted automatically by init at the end of the
-bootcharting. This is not the case of /data/bootchart/start, so don't forget to delete it
-when you're done collecting data:
-
-  adb shell rm /data/bootchart/start
-
-The log files are placed in /data/bootchart/. You must run the script tools/grab-bootchart.sh
-which will use ADB to retrieve them and create a bootchart.tgz file that can be used with
-the bootchart parser/renderer, or even uploaded directly to the form located at:
-
-  http://www.bootchart.org/download.html
-
-NOTE: the bootchart.org webform doesn't seem to work at the moment, you can generate an
-      image on your machine by doing the following:
-
-         1/ download the sources from www.bootchart.org
-         2/ unpack them
-         3/ in the source directory, type 'ant' to build the bootchart program
-         4/ type 'java -jar bootchart.jar /path/to/bootchart.tgz
-
-technical note:
-
-This implementation of bootcharting does not use the 'bootchartd' script provided by
-www.bootchart.org, but a C re-implementation that is directly compiled into our init
-program.
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;
+        }
+    }
 }
diff --git a/init/bootchart.h b/init/bootchart.h
index 7842950..cf61d83 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -17,8 +17,6 @@
 #ifndef _BOOTCHART_H
 #define _BOOTCHART_H
 
-int bootchart_init();
 void bootchart_sample(int* timeout);
-void bootchart_finish();
 
 #endif /* _BOOTCHART_H */
diff --git a/init/property_service.h b/init/property_service.h
index ff8b063..6e7fc00 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -17,7 +17,7 @@
 #ifndef _INIT_PROPERTY_H
 #define _INIT_PROPERTY_H
 
-#include <stdbool.h>
+#include <stddef.h>
 #include <sys/system_properties.h>
 
 extern void handle_property_set_fd(void);
diff --git a/init/readme.txt b/init/readme.txt
index 16a9186..32eb4ab 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -110,6 +110,7 @@
 onrestart
     Execute a Command (see below) when service restarts.
 
+
 Triggers
 --------
    Triggers are strings which can be used to match certain kinds
@@ -132,6 +133,7 @@
    The above stub sets test.c to 1 only when
    both test.a=1 and test.b=1
 
+
 Commands
 --------
 
@@ -283,63 +285,41 @@
    State of a named service ("stopped", "running", "restarting")
 
 
-Example init.conf
------------------
+Bootcharting
+------------
 
-# not complete -- just providing some examples of usage
-#
-on boot
-   export PATH /sbin:/system/sbin:/system/bin
-   export LD_LIBRARY_PATH /system/lib
+This version of init contains code to perform "bootcharting": generating log
+files that can be later processed by the tools provided by www.bootchart.org.
 
-   mkdir /dev
-   mkdir /proc
-   mkdir /sys
+On the emulator, use the new -bootchart <timeout> option to boot with
+bootcharting activated for <timeout> seconds.
 
-   mount tmpfs tmpfs /dev
-   mkdir /dev/pts
-   mkdir /dev/socket
-   mount devpts devpts /dev/pts
-   mount proc proc /proc
-   mount sysfs sysfs /sys
+On a device, create /data/bootchart/start with a command like the following:
 
-   write /proc/cpu/alignment 4
+  adb shell 'echo $TIMEOUT > /data/bootchart/start'
 
-   ifup lo
+Where the value of $TIMEOUT corresponds to the desired bootcharted period in
+seconds. Bootcharting will stop after that many seconds have elapsed.
+You can also stop the bootcharting at any moment by doing the following:
 
-   hostname localhost
-   domainname localhost
+  adb shell 'echo 1 > /data/bootchart/stop'
 
-   mount yaffs2 mtd@system /system
-   mount yaffs2 mtd@userdata /data
+Note that /data/bootchart/stop is deleted automatically by init at the end of
+the bootcharting. This is not the case with /data/bootchart/start, so don't
+forget to delete it when you're done collecting data.
 
-   import /system/etc/init.conf
+The log files are written to /data/bootchart/. A script is provided to
+retrieve them and create a bootchart.tgz file that can be used with the
+bootchart command-line utility:
 
-   class_start default
+  sudo apt-get install pybootchartgui
+  $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
+  bootchart ./bootchart.tgz
+  gnome-open bootchart.png
 
-service adbd /sbin/adbd
-   user adb
-   group adb
 
-service usbd /system/bin/usbd -r
-   user usbd
-   group usbd
-   socket usbd 666
-
-service zygote /system/bin/app_process -Xzygote /system/bin --zygote
-   socket zygote 666
-
-service runtime /system/bin/runtime
-   user system
-   group system
-
-service akmd /sbin/akmd
-   disabled
-   user akmd
-   group akmd
-
-Debugging notes
----------------
+Debugging init
+--------------
 By default, programs executed by init will drop stdout and stderr into
 /dev/null. To help with debugging, you can execute your program via the
 Android program logwrapper. This will redirect stdout/stderr into the
@@ -350,7 +330,7 @@
 
 For quicker turnaround when working on init itself, use:
 
-  mm
+  mm -j
   m ramdisk-nodeps
   m bootimage-nodeps
   adb reboot bootloader