Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf7560f1 authored by Elliott Hughes's avatar Elliott Hughes Committed by Gerrit Code Review
Browse files

Merge "Further refactoring of the bootchart code."

parents 265df6f5 841b263f
Loading
Loading
Loading
Loading

init/README.BOOTCHART

deleted100644 → 0
+0 −46
Original line number Diff line number Diff line
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.
+159 −272
Original line number Diff line number Diff line
@@ -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>

#include <utils/file.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"
@@ -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;
static int g_remaining_samples;
// Max polling time in seconds.
static const int BOOTCHART_MAX_TIME_SEC = 10*60;

static FileBuff log_stat[1];
static FileBuff log_procs[1];
static FileBuff log_disks[1];

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);
    }
    buff[len > 0 ? len : 0] = 0;
    return len;
}

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
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;
static long long g_last_bootchart_time;
static int g_remaining_samples;

        buff->count += avail;
        if (buff->count == FILE_BUFF_SIZE) {
            TEMP_FAILURE_RETRY(write(buff->fd, buff->data, buff->count));
            buff->count = 0;
        }
    }
}
static FILE* log_stat;
static FILE* log_procs;
static FILE* log_disks;

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() {
    std::string uptime;
    if (!android::ReadFileToString("/proc/uptime", &uptime)) {
        return 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;
    return 100LL * strtod(uptime.c_str(), NULL);
}

static void
log_header(void)
{
    FILE*      out;
    char       cmdline[1024];
    char       uname[128];
    char       cpuinfo[128];
    char*      cpu;
static void log_header() {
    char date[32];
    time_t now_t = time(NULL);
    struct tm now = *localtime(&now_t);
    strftime(date, sizeof(date), "%x %X", &now);
    strftime(date, sizeof(date), "%F %T", &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);
    fclose(out);
    char fingerprint[PROP_VALUE_MAX];
    if (property_get("ro.build.fingerprint", fingerprint) == -1) {
        return;
    }

static void
do_log_uptime(FileBuff*  log)
{
    char  buff[65];
    int   len;
    std::string kernel_cmdline;
    android::ReadFileToString("/proc/cmdline", &kernel_cmdline);

    snprintf(buff,sizeof(buff),"%lld\n",get_uptime_jiffies());
    len = strlen(buff);
    file_buff_write(log, buff, len);
    FILE* out = fopen(LOG_HEADER, "we");
    if (out == NULL) {
        return;
    }

static void
do_log_ln(FileBuff*  log)
{
    file_buff_write(log, "\n", 1);
    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(FILE* log) {
    fprintf(log, "%lld\n", get_uptime_jiffies());
}

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;
    std::string content;
    if (android::ReadFileToString(procfile, &content)) {
        fprintf(log, "%s\n", content.c_str());
    }
        close(fd);
}

    do_log_ln(log);
}
static void do_log_procs(FILE* log) {
    do_log_uptime(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 */
        // 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 */
            // /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);
            proc_read(filename, cmdline, sizeof(cmdline));
            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 */
            // 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);
            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);
}

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);
    } else {
        NOTICE("bootcharting ignored\n");
    fputc('\n', log);
}

    return 0;
}
static int bootchart_init() {
    int timeout = 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;
    std::string start;
    android::ReadFileToString(LOG_STARTFILE, &start);
    if (!start.empty()) {
        timeout = atoi(start.c_str());
    } 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.
        std::string cmdline;
        android::ReadFileToString("/proc/cmdline", &cmdline);
#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 (strstr(cmdline.c_str(), KERNEL_OPTION) != NULL) {
            timeout = atoi(cmdline.c_str() + sizeof(KERNEL_OPTION) - 1);
        }
    }
    if (timeout == 0)
@@ -309,15 +179,25 @@ int bootchart_init( void )
    if (timeout > BOOTCHART_MAX_TIME_SEC)
        timeout = BOOTCHART_MAX_TIME_SEC;

    count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;

    ret = TEMP_FAILURE_RETRY(mkdir(LOG_ROOT,0755));
    int count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;

    file_buff_open(log_stat,  LOG_STAT);
    file_buff_open(log_procs, LOG_PROCS);
    file_buff_open(log_disks, LOG_DISK);
    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;
    }

    /* 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,19 +210,28 @@ int bootchart_init( void )
    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') {
    // Stop if /data/bootchart/stop contains 1.
    std::string stop;
    if (android::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
        return -1;
    }
    }

    return 0;
}
@@ -352,14 +241,22 @@ static long long bootchart_gettime() {
    return 10LL*get_uptime_jiffies();
}

/* called each time you want to perform a bootchart sampling op */
static void bootchart_finish() {
    unlink(LOG_STOPFILE);
    fclose(log_stat);
    fclose(log_disks);
    fclose(log_procs);
    acct(NULL);
}

void bootchart_sample(int* timeout) {
    if (g_remaining_samples > 0) {
        long long current_time;
        int elapsed_time, remaining_time;
    // Do we have any more bootcharting to do?
    if (g_remaining_samples <= 0) {
        return;
    }

        current_time = bootchart_gettime();
        elapsed_time = current_time - last_bootchart_time;
    long long current_time = bootchart_gettime();
    int elapsed_time = current_time - g_last_bootchart_time;

    if (elapsed_time >= BOOTCHART_POLLING_MS) {
        /* count missed samples */
@@ -368,26 +265,16 @@ void bootchart_sample(int* timeout) {
            g_remaining_samples--;
        }
        /* count may be negative, take a sample anyway */
            last_bootchart_time = current_time;
        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) {
            remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
        int remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
        if (*timeout < 0 || *timeout > remaining_time) {
            *timeout = remaining_time;
        }
    }
}
}

void  bootchart_finish( void )
{
    unlink( LOG_STOPFILE );
    file_buff_done(log_stat);
    file_buff_done(log_disks);
    file_buff_done(log_procs);
    acct(NULL);
}
+0 −2
Original line number Diff line number Diff line
@@ -17,8 +17,6 @@
#ifndef _BOOTCHART_H
#define _BOOTCHART_H

int bootchart_init();
void bootchart_sample(int* timeout);
void bootchart_finish();

#endif /* _BOOTCHART_H */
+1 −1
Original line number Diff line number Diff line
@@ -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);
+27 −47
Original line number Diff line number Diff line
@@ -110,6 +110,7 @@ class <name>
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 @@ boot
   The above stub sets test.c to 1 only when
   both test.a=1 and test.b=1


Commands
--------

@@ -283,63 +285,41 @@ init.svc.<name>
   State of a named service ("stopped", "running", "restarting")


Example init.conf
-----------------

# not complete -- just providing some examples of usage
#
on boot
   export PATH /sbin:/system/sbin:/system/bin
   export LD_LIBRARY_PATH /system/lib

   mkdir /dev
   mkdir /proc
   mkdir /sys
Bootcharting
------------

   mount tmpfs tmpfs /dev
   mkdir /dev/pts
   mkdir /dev/socket
   mount devpts devpts /dev/pts
   mount proc proc /proc
   mount sysfs sysfs /sys
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.

   write /proc/cpu/alignment 4
On the emulator, use the new -bootchart <timeout> option to boot with
bootcharting activated for <timeout> seconds.

   ifup lo
On a device, create /data/bootchart/start with a command like the following:

   hostname localhost
   domainname localhost
  adb shell 'echo $TIMEOUT > /data/bootchart/start'

   mount yaffs2 mtd@system /system
   mount yaffs2 mtd@userdata /data
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:

   import /system/etc/init.conf
  adb shell 'echo 1 > /data/bootchart/stop'

   class_start default
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.

service adbd /sbin/adbd
   user adb
   group adb
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:

service usbd /system/bin/usbd -r
   user usbd
   group usbd
   socket usbd 666
  sudo apt-get install pybootchartgui
  $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
  bootchart ./bootchart.tgz
  gnome-open bootchart.png

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 @@ service akmd /system/bin/logwrapper /sbin/akmd

For quicker turnaround when working on init itself, use:

  mm
  mm -j
  m ramdisk-nodeps
  m bootimage-nodeps
  adb reboot bootloader