Home | History | Annotate | Download | only in init
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "bootchart.h"
     18 #include "log.h"
     19 #include "property_service.h"
     20 
     21 #include <dirent.h>
     22 #include <errno.h>
     23 #include <fcntl.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <sys/stat.h>
     28 #include <sys/utsname.h>
     29 #include <time.h>
     30 #include <unistd.h>
     31 
     32 #include <memory>
     33 #include <string>
     34 #include <vector>
     35 
     36 #include <android-base/file.h>
     37 
     38 #define LOG_ROOT        "/data/bootchart"
     39 #define LOG_STAT        LOG_ROOT"/proc_stat.log"
     40 #define LOG_PROCS       LOG_ROOT"/proc_ps.log"
     41 #define LOG_DISK        LOG_ROOT"/proc_diskstats.log"
     42 #define LOG_HEADER      LOG_ROOT"/header"
     43 #define LOG_ACCT        LOG_ROOT"/kernel_pacct"
     44 
     45 #define LOG_STARTFILE   LOG_ROOT"/start"
     46 #define LOG_STOPFILE    LOG_ROOT"/stop"
     47 
     48 // Polling period in ms.
     49 static const int BOOTCHART_POLLING_MS = 200;
     50 
     51 // Max polling time in seconds.
     52 static const int BOOTCHART_MAX_TIME_SEC = 10*60;
     53 
     54 static long long g_last_bootchart_time;
     55 static int g_remaining_samples;
     56 
     57 static FILE* log_stat;
     58 static FILE* log_procs;
     59 static FILE* log_disks;
     60 
     61 static long long get_uptime_jiffies() {
     62     std::string uptime;
     63     if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
     64         return 0;
     65     }
     66     return 100LL * strtod(uptime.c_str(), NULL);
     67 }
     68 
     69 static void log_header() {
     70     char date[32];
     71     time_t now_t = time(NULL);
     72     struct tm now = *localtime(&now_t);
     73     strftime(date, sizeof(date), "%F %T", &now);
     74 
     75     utsname uts;
     76     if (uname(&uts) == -1) {
     77         return;
     78     }
     79 
     80     std::string fingerprint = property_get("ro.build.fingerprint");
     81     if (fingerprint.empty()) {
     82         return;
     83     }
     84 
     85     std::string kernel_cmdline;
     86     android::base::ReadFileToString("/proc/cmdline", &kernel_cmdline);
     87 
     88     FILE* out = fopen(LOG_HEADER, "we");
     89     if (out == NULL) {
     90         return;
     91     }
     92     fprintf(out, "version = Android init 0.8\n");
     93     fprintf(out, "title = Boot chart for Android (%s)\n", date);
     94     fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
     95     fprintf(out, "system.release = %s\n", fingerprint.c_str());
     96     // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
     97     fprintf(out, "system.cpu = %s\n", uts.machine);
     98     fprintf(out, "system.kernel.options = %s\n", kernel_cmdline.c_str());
     99     fclose(out);
    100 }
    101 
    102 static void do_log_uptime(FILE* log) {
    103     fprintf(log, "%lld\n", get_uptime_jiffies());
    104 }
    105 
    106 static void do_log_file(FILE* log, const char* procfile) {
    107     do_log_uptime(log);
    108 
    109     std::string content;
    110     if (android::base::ReadFileToString(procfile, &content)) {
    111         fprintf(log, "%s\n", content.c_str());
    112     }
    113 }
    114 
    115 static void do_log_procs(FILE* log) {
    116     do_log_uptime(log);
    117 
    118     std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
    119     struct dirent* entry;
    120     while ((entry = readdir(dir.get())) != NULL) {
    121         // Only match numeric values.
    122         char* end;
    123         int pid = strtol(entry->d_name, &end, 10);
    124         if (end != NULL && end > entry->d_name && *end == 0) {
    125             char filename[32];
    126 
    127             // /proc/<pid>/stat only has truncated task names, so get the full
    128             // name from /proc/<pid>/cmdline.
    129             snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
    130             std::string cmdline;
    131             android::base::ReadFileToString(filename, &cmdline);
    132             const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
    133 
    134             // Read process stat line.
    135             snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
    136             std::string stat;
    137             if (android::base::ReadFileToString(filename, &stat)) {
    138                 if (!cmdline.empty()) {
    139                     // Substitute the process name with its real name.
    140                     size_t open = stat.find('(');
    141                     size_t close = stat.find_last_of(')');
    142                     if (open != std::string::npos && close != std::string::npos) {
    143                         stat.replace(open + 1, close - open - 1, full_name);
    144                     }
    145                 }
    146                 fputs(stat.c_str(), log);
    147             }
    148         }
    149     }
    150 
    151     fputc('\n', log);
    152 }
    153 
    154 static int bootchart_init() {
    155     int timeout = 0;
    156 
    157     std::string start;
    158     android::base::ReadFileToString(LOG_STARTFILE, &start);
    159     if (!start.empty()) {
    160         timeout = atoi(start.c_str());
    161     } else {
    162         // When running with emulator, androidboot.bootchart=<timeout>
    163         // might be passed by as kernel parameters to specify the bootchart
    164         // timeout. this is useful when using -wipe-data since the /data
    165         // partition is fresh.
    166         std::string cmdline;
    167         const char* s;
    168         android::base::ReadFileToString("/proc/cmdline", &cmdline);
    169 #define KERNEL_OPTION  "androidboot.bootchart="
    170         if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
    171             timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
    172         }
    173     }
    174     if (timeout == 0)
    175         return 0;
    176 
    177     if (timeout > BOOTCHART_MAX_TIME_SEC)
    178         timeout = BOOTCHART_MAX_TIME_SEC;
    179 
    180     int count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
    181 
    182     log_stat = fopen(LOG_STAT, "we");
    183     if (log_stat == NULL) {
    184         return -1;
    185     }
    186     log_procs = fopen(LOG_PROCS, "we");
    187     if (log_procs == NULL) {
    188         fclose(log_stat);
    189         return -1;
    190     }
    191     log_disks = fopen(LOG_DISK, "we");
    192     if (log_disks == NULL) {
    193         fclose(log_stat);
    194         fclose(log_procs);
    195         return -1;
    196     }
    197 
    198     // Create kernel process accounting file.
    199     close(open(LOG_ACCT, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
    200     acct(LOG_ACCT);
    201 
    202     log_header();
    203     return count;
    204 }
    205 
    206 int do_bootchart_init(const std::vector<std::string>& args) {
    207     g_remaining_samples = bootchart_init();
    208     if (g_remaining_samples < 0) {
    209         ERROR("Bootcharting init failure: %s\n", strerror(errno));
    210     } else if (g_remaining_samples > 0) {
    211         NOTICE("Bootcharting started (will run for %d s).\n",
    212                (g_remaining_samples * BOOTCHART_POLLING_MS) / 1000);
    213     } else {
    214         NOTICE("Not bootcharting.\n");
    215     }
    216     return 0;
    217 }
    218 
    219 static int bootchart_step() {
    220     do_log_file(log_stat,   "/proc/stat");
    221     do_log_file(log_disks,  "/proc/diskstats");
    222     do_log_procs(log_procs);
    223 
    224     // Stop if /data/bootchart/stop contains 1.
    225     std::string stop;
    226     if (android::base::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
    227         return -1;
    228     }
    229 
    230     return 0;
    231 }
    232 
    233 /* called to get time (in ms) used by bootchart */
    234 static long long bootchart_gettime() {
    235     return 10LL*get_uptime_jiffies();
    236 }
    237 
    238 static void bootchart_finish() {
    239     unlink(LOG_STOPFILE);
    240     fclose(log_stat);
    241     fclose(log_disks);
    242     fclose(log_procs);
    243     acct(NULL);
    244 }
    245 
    246 void bootchart_sample(int* timeout) {
    247     // Do we have any more bootcharting to do?
    248     if (g_remaining_samples <= 0) {
    249         return;
    250     }
    251 
    252     long long current_time = bootchart_gettime();
    253     int elapsed_time = current_time - g_last_bootchart_time;
    254 
    255     if (elapsed_time >= BOOTCHART_POLLING_MS) {
    256         /* count missed samples */
    257         while (elapsed_time >= BOOTCHART_POLLING_MS) {
    258             elapsed_time -= BOOTCHART_POLLING_MS;
    259             g_remaining_samples--;
    260         }
    261         /* count may be negative, take a sample anyway */
    262         g_last_bootchart_time = current_time;
    263         if (bootchart_step() < 0 || g_remaining_samples <= 0) {
    264             bootchart_finish();
    265             g_remaining_samples = 0;
    266         }
    267     }
    268     if (g_remaining_samples > 0) {
    269         int remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
    270         if (*timeout < 0 || *timeout > remaining_time) {
    271             *timeout = remaining_time;
    272         }
    273     }
    274 }
    275