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 19 #include <dirent.h> 20 #include <fcntl.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <sys/stat.h> 25 #include <sys/utsname.h> 26 #include <time.h> 27 #include <unistd.h> 28 29 #include <chrono> 30 #include <condition_variable> 31 #include <memory> 32 #include <mutex> 33 #include <thread> 34 35 #include <android-base/file.h> 36 #include <android-base/logging.h> 37 #include <android-base/properties.h> 38 #include <android-base/stringprintf.h> 39 40 using android::base::StringPrintf; 41 using namespace std::chrono_literals; 42 43 namespace android { 44 namespace init { 45 46 static std::thread* g_bootcharting_thread; 47 48 static std::mutex g_bootcharting_finished_mutex; 49 static std::condition_variable g_bootcharting_finished_cv; 50 static bool g_bootcharting_finished; 51 52 static long long get_uptime_jiffies() { 53 std::string uptime; 54 if (!android::base::ReadFileToString("/proc/uptime", &uptime)) return 0; 55 return 100LL * strtod(uptime.c_str(), NULL); 56 } 57 58 static std::unique_ptr<FILE, decltype(&fclose)> fopen_unique(const char* filename, 59 const char* mode) { 60 std::unique_ptr<FILE, decltype(&fclose)> result(fopen(filename, mode), fclose); 61 if (!result) PLOG(ERROR) << "bootchart: failed to open " << filename; 62 return result; 63 } 64 65 static void log_header() { 66 char date[32]; 67 time_t now_t = time(NULL); 68 struct tm now = *localtime(&now_t); 69 strftime(date, sizeof(date), "%F %T", &now); 70 71 utsname uts; 72 if (uname(&uts) == -1) return; 73 74 std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", ""); 75 if (fingerprint.empty()) return; 76 77 std::string kernel_cmdline; 78 android::base::ReadFileToString("/proc/cmdline", &kernel_cmdline); 79 80 auto fp = fopen_unique("/data/bootchart/header", "we"); 81 if (!fp) return; 82 fprintf(&*fp, "version = Android init 0.8\n"); 83 fprintf(&*fp, "title = Boot chart for Android (%s)\n", date); 84 fprintf(&*fp, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine); 85 fprintf(&*fp, "system.release = %s\n", fingerprint.c_str()); 86 // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm. 87 fprintf(&*fp, "system.cpu = %s\n", uts.machine); 88 fprintf(&*fp, "system.kernel.options = %s\n", kernel_cmdline.c_str()); 89 } 90 91 static void log_uptime(FILE* log) { 92 fprintf(log, "%lld\n", get_uptime_jiffies()); 93 } 94 95 static void log_file(FILE* log, const char* procfile) { 96 log_uptime(log); 97 98 std::string content; 99 if (android::base::ReadFileToString(procfile, &content)) { 100 fprintf(log, "%s\n", content.c_str()); 101 } 102 } 103 104 static void log_processes(FILE* log) { 105 log_uptime(log); 106 107 std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir); 108 struct dirent* entry; 109 while ((entry = readdir(dir.get())) != NULL) { 110 // Only match numeric values. 111 int pid = atoi(entry->d_name); 112 if (pid == 0) continue; 113 114 // /proc/<pid>/stat only has truncated task names, so get the full 115 // name from /proc/<pid>/cmdline. 116 std::string cmdline; 117 android::base::ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline); 118 const char* full_name = cmdline.c_str(); // So we stop at the first NUL. 119 120 // Read process stat line. 121 std::string stat; 122 if (android::base::ReadFileToString(StringPrintf("/proc/%d/stat", pid), &stat)) { 123 if (!cmdline.empty()) { 124 // Substitute the process name with its real name. 125 size_t open = stat.find('('); 126 size_t close = stat.find_last_of(')'); 127 if (open != std::string::npos && close != std::string::npos) { 128 stat.replace(open + 1, close - open - 1, full_name); 129 } 130 } 131 fputs(stat.c_str(), log); 132 } 133 } 134 135 fputc('\n', log); 136 } 137 138 static void bootchart_thread_main() { 139 LOG(INFO) << "Bootcharting started"; 140 141 // Open log files. 142 auto stat_log = fopen_unique("/data/bootchart/proc_stat.log", "we"); 143 if (!stat_log) return; 144 auto proc_log = fopen_unique("/data/bootchart/proc_ps.log", "we"); 145 if (!proc_log) return; 146 auto disk_log = fopen_unique("/data/bootchart/proc_diskstats.log", "we"); 147 if (!disk_log) return; 148 149 log_header(); 150 151 while (true) { 152 { 153 std::unique_lock<std::mutex> lock(g_bootcharting_finished_mutex); 154 g_bootcharting_finished_cv.wait_for(lock, 200ms); 155 if (g_bootcharting_finished) break; 156 } 157 158 log_file(&*stat_log, "/proc/stat"); 159 log_file(&*disk_log, "/proc/diskstats"); 160 log_processes(&*proc_log); 161 } 162 163 LOG(INFO) << "Bootcharting finished"; 164 } 165 166 static int do_bootchart_start() { 167 // We don't care about the content, but we do care that /data/bootchart/enabled actually exists. 168 std::string start; 169 if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) { 170 LOG(VERBOSE) << "Not bootcharting"; 171 return 0; 172 } 173 174 g_bootcharting_thread = new std::thread(bootchart_thread_main); 175 return 0; 176 } 177 178 static int do_bootchart_stop() { 179 if (!g_bootcharting_thread) return 0; 180 181 // Tell the worker thread it's time to quit. 182 { 183 std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex); 184 g_bootcharting_finished = true; 185 g_bootcharting_finished_cv.notify_one(); 186 } 187 188 g_bootcharting_thread->join(); 189 delete g_bootcharting_thread; 190 g_bootcharting_thread = nullptr; 191 return 0; 192 } 193 194 int do_bootchart(const std::vector<std::string>& args) { 195 if (args[1] == "start") return do_bootchart_start(); 196 return do_bootchart_stop(); 197 } 198 199 } // namespace init 200 } // namespace android 201