diff --git a/init/README.BOOTCHART b/init/README.BOOTCHART deleted file mode 100644 index 245e4522c..000000000 --- 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 option to boot with bootcharting -activated for 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 ff4c0cf3b..e97335d95 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 #include @@ -31,15 +26,14 @@ #include #include #include +#include #include #include -#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 + +#include -#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; +// 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//stat only has truncated task names, so get the full + // name from /proc//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= - * 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= + // 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 @@ 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; + 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 @@ 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') { - 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 @@ 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 */ - 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 (elapsed_time >= BOOTCHART_POLLING_MS) { + /* count missed samples */ + while (elapsed_time >= BOOTCHART_POLLING_MS) { + elapsed_time -= BOOTCHART_POLLING_MS; + g_remaining_samples--; } - if (g_remaining_samples > 0) { - remaining_time = BOOTCHART_POLLING_MS - elapsed_time; - if (*timeout < 0 || *timeout > remaining_time) { - *timeout = remaining_time; - } + /* 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; } } } - -void bootchart_finish( void ) -{ - unlink( LOG_STOPFILE ); - file_buff_done(log_stat); - file_buff_done(log_disks); - file_buff_done(log_procs); - acct(NULL); -} diff --git a/init/bootchart.h b/init/bootchart.h index 784295056..cf61d8350 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 ff8b06377..6e7fc00d6 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -17,7 +17,7 @@ #ifndef _INIT_PROPERTY_H #define _INIT_PROPERTY_H -#include +#include #include extern void handle_property_set_fd(void); diff --git a/init/readme.txt b/init/readme.txt index 16a91866b..32eb4abbc 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -110,6 +110,7 @@ class 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. 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 option to boot with +bootcharting activated for 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 @@ 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