Loading init/README.BOOTCHARTdeleted 100644 → 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. init/bootchart.cpp +159 −272 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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" Loading @@ -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) Loading @@ -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) { Loading @@ -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; } Loading @@ -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 */ Loading @@ -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); } init/bootchart.h +0 −2 Original line number Diff line number Diff line Loading @@ -17,8 +17,6 @@ #ifndef _BOOTCHART_H #define _BOOTCHART_H int bootchart_init(); void bootchart_sample(int* timeout); void bootchart_finish(); #endif /* _BOOTCHART_H */ init/property_service.h +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading init/readme.txt +27 −47 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -132,6 +133,7 @@ boot The above stub sets test.c to 1 only when both test.a=1 and test.b=1 Commands -------- Loading Loading @@ -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 Loading @@ -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 Loading Loading
init/README.BOOTCHARTdeleted 100644 → 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.
init/bootchart.cpp +159 −272 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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" Loading @@ -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) Loading @@ -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) { Loading @@ -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; } Loading @@ -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 */ Loading @@ -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); }
init/bootchart.h +0 −2 Original line number Diff line number Diff line Loading @@ -17,8 +17,6 @@ #ifndef _BOOTCHART_H #define _BOOTCHART_H int bootchart_init(); void bootchart_sample(int* timeout); void bootchart_finish(); #endif /* _BOOTCHART_H */
init/property_service.h +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading
init/readme.txt +27 −47 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -132,6 +133,7 @@ boot The above stub sets test.c to 1 only when both test.a=1 and test.b=1 Commands -------- Loading Loading @@ -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 Loading @@ -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 Loading