summaryrefslogtreecommitdiff
path: root/boottime_tools/bootio
diff options
context:
space:
mode:
Diffstat (limited to 'boottime_tools/bootio')
-rw-r--r--boottime_tools/bootio/Android.mk76
-rw-r--r--boottime_tools/bootio/README.md24
-rw-r--r--boottime_tools/bootio/bootio.cpp162
-rw-r--r--boottime_tools/bootio/bootio.rc10
-rw-r--r--boottime_tools/bootio/bootio_collector.cpp381
-rw-r--r--boottime_tools/bootio/bootio_collector.h39
-rw-r--r--boottime_tools/bootio/protos.proto55
-rw-r--r--boottime_tools/bootio/sepolicy/bootio.te12
-rw-r--r--boottime_tools/bootio/sepolicy/domain.te2
-rw-r--r--boottime_tools/bootio/sepolicy/file.te2
-rw-r--r--boottime_tools/bootio/sepolicy/file_contexts5
11 files changed, 768 insertions, 0 deletions
diff --git a/boottime_tools/bootio/Android.mk b/boottime_tools/bootio/Android.mk
new file mode 100644
index 00000000..e4db8355
--- /dev/null
+++ b/boottime_tools/bootio/Android.mk
@@ -0,0 +1,76 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+bootio_lib_src_files := \
+ protos.proto \
+ bootio_collector.cpp \
+
+bootio_src_files := \
+ bootio.cpp \
+
+bootio_shared_libs := \
+ libbase \
+ libcutils \
+ liblog \
+ libprotobuf-cpp-lite \
+
+bootio_cflags := \
+ -Wall \
+ -Werror \
+ -Wextra \
+
+define bootio_proto_include
+$(call local-generated-sources-dir)/proto/$(LOCAL_PATH)
+endef
+
+# bootio static library
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbootio
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/.. \
+ $(call bootio_proto_include) \
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_CFLAGS := $(bootio_cflags)
+LOCAL_SHARED_LIBRARIES := $(bootio_shared_libs)
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+LOCAL_SRC_FILES := $(bootio_lib_src_files)
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+# bootio binary
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bootio
+LOCAL_CFLAGS := $(bootio_cflags)
+LOCAL_SHARED_LIBRARIES := \
+ $(bootio_shared_libs) \
+ libbootio \
+
+LOCAL_INIT_RC := bootio.rc
+LOCAL_SRC_FILES := $(bootio_src_files)
+
+include $(BUILD_EXECUTABLE)
diff --git a/boottime_tools/bootio/README.md b/boottime_tools/bootio/README.md
new file mode 100644
index 00000000..ca075ded
--- /dev/null
+++ b/boottime_tools/bootio/README.md
@@ -0,0 +1,24 @@
+# bootio #
+
+The bootio tool records I/O for processes during boot.
+To use bootio kernel must be compiled with this flags:
+
+ CONFIG_TASKSTATS=y
+ CONFIG_TASK_DELAY_ACCT=y
+ CONFIG_TASK_XACCT=y
+ CONFIG_TASK_IO_ACCOUNTING=y
+
+To use bootio make sure it's included in product config for the board.
+Create file /data/misc/bootio/start with a command like the following:
+
+ adb shell 'echo "$TIMEOUT $SAMPLES" > /data/misc/bootio/start'
+
+Where the value of $TIMEOUT corresponds to the desired bootio period in
+seconds and $SAMPLES corresponds to the desired number of samples.
+
+Note: /data/misc/bootio/start is not deleted automatically, so don't
+forget to delete it when you're done collecting data.
+
+To see collected logs run:
+
+ adb shell bootio -p
diff --git a/boottime_tools/bootio/bootio.cpp b/boottime_tools/bootio/bootio.cpp
new file mode 100644
index 00000000..629d04a7
--- /dev/null
+++ b/boottime_tools/bootio/bootio.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// The bootio tool provides options to collect I/O stats for processes during boot.
+
+#include <vector>
+#include <getopt.h>
+#include <unistd.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <log/log.h>
+
+#include "bootio_collector.h"
+
+namespace android {
+
+#define LOG_ROOT "/data/misc/bootio"
+#define LOG_START_FILE LOG_ROOT"/start"
+#define SELF_IO "/proc/self/io"
+
+static const int LOG_TIMEOUT_INDEX = 0;
+static const int LOG_SAMPLES_INDEX = 1;
+static const int LOG_MAX_TIMEOUT = 120;
+static const int LOG_MAX_SAMPLES = 30;
+
+void ShowHelp(const char *cmd) {
+ fprintf(stderr, "Usage: %s [options]\n", cmd);
+ fprintf(stderr,
+ "options include:\n"
+ " -h, --help Show this help\n"
+ " -p, --print Dump the boot io data to the console\n"
+ "\nNo options will start data collection process.\n");
+}
+
+void PrintBootIo() {
+ printf("Boot I/O:\n");
+ printf("------------\n");
+ std::unique_ptr <BootioCollector> collector(new BootioCollector(LOG_ROOT));
+ if (collector.get() == NULL) {
+ LOG(ERROR) << "Failed to create data collector";
+ return;
+ }
+ collector->Print();
+}
+
+void StartDataCollection() {
+ if (access(SELF_IO, F_OK) == -1) {
+ LOG(ERROR) << "Kernel doesn't support I/O profiling.";
+ printf("Kernel doesn't support I/O profiling.");
+ return;
+ }
+
+ int timeout = 0;
+ int samples = 0;
+
+ std::string start;
+ android::base::ReadFileToString(LOG_START_FILE, &start);
+
+ if (!start.empty()) {
+ std::vector <std::string> components = android::base::Split(start, " ");
+ if (components.size() != 2) {
+ LOG(ERROR) << "Invalid value in start file." << start;
+ return;
+ }
+ timeout = atoi(components.at(LOG_TIMEOUT_INDEX).c_str());
+ samples = atoi(components.at(LOG_SAMPLES_INDEX).c_str());
+ } else {
+ LOG(INFO) << "No profiling requested. Exiting";
+ printf("Boot I/O: no profiling requested. Exiting.\n");
+ return;
+ }
+ if (timeout <= 0 || samples <= 0) {
+ LOG(ERROR) << "Boot I/O: failed to parse string:" << start;
+ printf("Boot I/O: failed to parse string: %s\n", start.c_str());
+ return;
+ }
+ if (samples > timeout || samples > LOG_MAX_SAMPLES || timeout > LOG_MAX_TIMEOUT) {
+ LOG(ERROR) << "Bad values for bootio. timeout=" << timeout <<
+ " samples=" << samples << " Max timeout=" << LOG_MAX_TIMEOUT <<
+ " Max samples=" << LOG_MAX_SAMPLES;
+ return;
+ }
+ LOG(INFO) << "Boot I/O: collecting data. samples=" << samples << "timeout=" << timeout;
+ printf("Boot I/O: collecting data\ntimeout=%d, samples=%d\n",
+ timeout, samples);
+ std::unique_ptr <BootioCollector> collector(new BootioCollector(LOG_ROOT));
+ if (collector.get() == NULL) {
+ LOG(ERROR) << "Failed to create data collector";
+ return;
+ }
+ collector->StartDataCollection(timeout, samples);
+}
+
+}
+
+int main(int argc, char **argv) {
+ android::base::InitLogging(argv);
+
+ LOG(INFO) << "Bootio started";
+
+ int optionIndex = 0;
+ static const struct option longOptions[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"print", no_argument, NULL, 'p'},
+ {NULL, 0, NULL, 0}
+ };
+
+ int opt = 0;
+ bool startCollection = true;
+ while ((opt = getopt_long(argc, argv, "hlpr:", longOptions, &optionIndex)) != -1) {
+ switch (opt) {
+ case 0: {
+ const std::string option_name = longOptions[optionIndex].name;
+ LOG(ERROR) << "Invalid option: " << option_name;
+ break;
+ }
+
+ case 'h': {
+ android::ShowHelp(argv[0]);
+ startCollection = false;
+ break;
+ }
+
+ case 'p': {
+ android::PrintBootIo();
+ startCollection = false;
+ break;
+ }
+
+ default: {
+ DCHECK_EQ(opt, '?');
+
+ // |optopt| is an external variable set by getopt representing
+ // the value of the invalid option.
+ LOG(ERROR) << "Invalid option: " << optopt;
+ android::ShowHelp(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ if (startCollection) {
+ android::StartDataCollection();
+ }
+
+ return 0;
+}
+
diff --git a/boottime_tools/bootio/bootio.rc b/boottime_tools/bootio/bootio.rc
new file mode 100644
index 00000000..f0074cb4
--- /dev/null
+++ b/boottime_tools/bootio/bootio.rc
@@ -0,0 +1,10 @@
+# This file is the LOCAL_INIT_RC file for the bootio command.
+
+service bootio /system/bin/bootio
+ class main
+ user root
+ group root
+ oneshot
+
+on post-fs-data
+ mkdir /data/misc/bootio 0755 root shell
diff --git a/boottime_tools/bootio/bootio_collector.cpp b/boottime_tools/bootio/bootio_collector.cpp
new file mode 100644
index 00000000..495a9aa4
--- /dev/null
+++ b/boottime_tools/bootio/bootio_collector.cpp
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bootio_collector.h"
+#include <android-base/logging.h>
+#include <android-base/file.h>
+#include <log/log.h>
+#include "protos.pb.h"
+#include "time.h"
+#include <unordered_map>
+#include <inttypes.h>
+#include <dirent.h>
+
+namespace android {
+
+#define CPU_STAT_FILE "/proc/stat"
+#define SAMPLES_FILE "/samples"
+#define PID_STAT_FILE "/proc/%d/stat"
+#define PID_CMDLINE_FILE "/proc/%d/cmdline"
+#define PID_IO_FILE "/proc/%d/io"
+#define PROC_DIR "/proc"
+
+static const int MAX_LINE = 256;
+
+#define die(...) { LOG(ERROR) << (__VA_ARGS__); exit(EXIT_FAILURE); }
+
+void PopulateCpu(CpuData& cpu) {
+ long unsigned utime, ntime, stime, itime;
+ long unsigned iowtime, irqtime, sirqtime;
+ FILE *file;
+ file = fopen(CPU_STAT_FILE, "r");
+ if (!file) die("Could not open /proc/stat.\n");
+ fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &utime, &ntime, &stime,
+ &itime, &iowtime, &irqtime, &sirqtime);
+ fclose(file);
+ cpu.set_utime(utime);
+ cpu.set_ntime(ntime);
+ cpu.set_stime(stime);
+ cpu.set_itime(itime);
+ cpu.set_iowtime(iowtime);
+ cpu.set_irqtime(irqtime);
+ cpu.set_sirqtime(sirqtime);
+}
+
+void ClearPreviousResults(std::string path) {
+ std::string err;
+ if (!android::base::RemoveFileIfExists(path, &err)) {
+ LOG(ERROR) << "failed to remove the file " << path << " " << err;
+ return;
+ }
+}
+
+int ReadIo(char *filename, AppSample *sample) {
+ FILE *file;
+ char line[MAX_LINE];
+ unsigned int rchar, wchar, syscr, syscw, readbytes, writebytes;
+
+ file = fopen(filename, "r");
+ if (!file) return 1;
+ while (fgets(line, MAX_LINE, file)) {
+ sscanf(line, "rchar: %u", &rchar);
+ sscanf(line, "wchar: %u", &wchar);
+ sscanf(line, "syscr: %u", &syscr);
+ sscanf(line, "syscw: %u", &syscw);
+ sscanf(line, "read_bytes: %u", &readbytes);
+ sscanf(line, "write_bytes: %u", &writebytes);
+ }
+ fclose(file);
+ sample->set_rchar(rchar);
+ sample->set_wchar(wchar);
+ sample->set_syscr(syscr);
+ sample->set_syscw(syscw);
+ sample->set_readbytes(readbytes);
+ sample->set_writebytes(writebytes);
+ return 0;
+}
+
+int ReadStatForName(char *filename, AppData *app) {
+ FILE *file;
+ char buf[MAX_LINE], *open_paren, *close_paren;
+
+ file = fopen(filename, "r");
+ if (!file) return 1;
+ fgets(buf, MAX_LINE, file);
+ fclose(file);
+
+ /* Split at first '(' and last ')' to get process name. */
+ open_paren = strchr(buf, '(');
+ close_paren = strrchr(buf, ')');
+ if (!open_paren || !close_paren) return 1;
+
+ *open_paren = *close_paren = '\0';
+ if (!app->has_tname()) {
+ app->set_tname(open_paren + 1, close_paren - open_paren - 1);
+ }
+ return 0;
+}
+
+int ReadStat(char *filename, AppSample *sample) {
+ FILE *file;
+ char buf[MAX_LINE], *open_paren, *close_paren;
+
+ file = fopen(filename, "r");
+ if (!file) return 1;
+ fgets(buf, MAX_LINE, file);
+ fclose(file);
+
+ /* Split at first '(' and last ')' to get process name. */
+ open_paren = strchr(buf, '(');
+ close_paren = strrchr(buf, ')');
+ if (!open_paren || !close_paren) return 1;
+
+ uint64_t utime;
+ uint64_t stime;
+ uint64_t rss;
+
+ /* Scan rest of string. */
+ sscanf(close_paren + 1,
+ " %*c " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
+ "%" PRIu64 /*SCNu64*/
+ "%" PRIu64 /*SCNu64*/ "%*d %*d %*d %*d %*d %*d %*d %*d "
+ "%" PRIu64 /*SCNu64*/ "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d",
+ &utime,
+ &stime,
+ &rss);
+ sample->set_utime(utime);
+ sample->set_stime(stime);
+ sample->set_rss(rss);
+
+ return 0;
+}
+
+int ReadCmdline(char *filename, AppData *app) {
+ FILE *file;
+ char line[MAX_LINE];
+
+ line[0] = '\0';
+ file = fopen(filename, "r");
+ if (!file) return 1;
+ fgets(line, MAX_LINE, file);
+ fclose(file);
+ if (strlen(line) > 0) {
+ app->set_name(line, strlen(line));
+ } else {
+ app->set_name("N/A");
+ }
+ return 0;
+};
+
+void ReadProcData(std::unordered_map<int, AppData*>& pidDataMap, DataContainer& dataContainer,
+ time_t currentTimeUtc, time_t currentUptime) {
+ DIR *procDir;
+ struct dirent *pidDir;
+ pid_t pid;
+ char filename[64];
+ procDir = opendir(PROC_DIR);
+ if (!procDir) die("Could not open /proc.\n");
+ while ((pidDir = readdir(procDir))) {
+ if (!isdigit(pidDir->d_name[0])) {
+ continue;
+ }
+ pid = atoi(pidDir->d_name);
+ AppData *data;
+
+ // TODO: in theory same pid can be shared for multiple processes,
+ // might need add extra check.
+ if (pidDataMap.count(pid) == 0) {
+ data = dataContainer.add_app();
+ data->set_pid(pid);
+ sprintf(filename, PID_STAT_FILE, pid);
+ ReadStatForName(filename, data);
+ sprintf(filename, PID_CMDLINE_FILE, pid);
+ ReadCmdline(filename, data);
+ pidDataMap[pid] = data;
+ } else {
+ data = pidDataMap[pid];
+ }
+ AppSample *sample = data->add_samples();
+ sample->set_timestamp(currentTimeUtc);
+ sample->set_uptime(currentUptime);
+
+ sprintf(filename, PID_STAT_FILE, pid);
+ ReadStat(filename, sample);
+
+ sprintf(filename, PID_IO_FILE, pid);
+ ReadIo(filename, sample);
+ }
+}
+
+uint64_t SumCpuValues(CpuData& cpu) {
+ return cpu.utime() + cpu.ntime() + cpu.stime() + cpu.itime() + cpu.iowtime() +
+ cpu.irqtime() + cpu.sirqtime();
+}
+
+time_t GetUptime() {
+ std::string uptime_str;
+ if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
+ LOG(ERROR) << "Failed to read /proc/uptime";
+ return -1;
+ }
+
+ // Cast intentionally rounds down.
+ return static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
+}
+
+struct Stats {
+ int uptime;
+ float cpu;
+ uint64_t rbytes;
+ uint64_t wbytes;
+};
+
+void PrintPids(DataContainer& data, std::unordered_map<int, uint64_t>& cpuDataMap) {
+ printf("rchar: number of bytes the process read, using any read-like system call "
+ "(from files, pipes, tty...).\n");
+ printf("wchar: number of bytes the process wrote using any write-like system call.\n");
+ printf("wchar: number of bytes the process wrote using any write-like system call.\n");
+ printf("syscr: number of write-like system call invocations that the process performed.\n");
+ printf("rbytes: number of bytes the process directly read from disk.\n");
+ printf("wbytes: number of bytes the process originally dirtied in the page-cache "
+ "(assuming they will go to disk later).\n\n");
+
+ std::unique_ptr<AppSample> bootZeroSample(new AppSample());
+ std::map<int, Stats> statsMap;
+ // Init stats map
+ Stats emptyStat {0, 0., 0, 0};
+ for (auto it = cpuDataMap.begin(); it != cpuDataMap.end(); it++) {
+ statsMap[it->first] = emptyStat;
+ }
+ for (int i = 0; i < data.app_size(); i++) {
+ const AppData appData = data.app(i);
+ printf("\n-----------------------------------------------------------------------------\n");
+ printf("PID:\t%u\n", appData.pid());
+ printf("Name:\t%s\n", appData.name().c_str());
+ printf("ThName:\t%s\n", appData.tname().c_str());
+ printf("%-15s%-13s%-13s%-13s%-13s%-13s%-13s%-13s\n", "Uptime inter.", "rchar", "wchar",
+ "syscr", "syscw", "rbytes", "wbytes", "cpu%");
+ const AppSample *olderSample = NULL;
+ const AppSample *newerSample = NULL;
+ bool isFirstSample = true;
+ for (int j = 0; j < appData.samples_size(); j++) {
+ olderSample = newerSample;
+ newerSample = &(appData.samples(j));
+ if (olderSample == NULL) {
+ olderSample = bootZeroSample.get();
+ }
+ float cpuLoad = 0.;
+ uint64_t cpuDelta;
+ if (isFirstSample) {
+ cpuDelta = cpuDataMap[newerSample->timestamp()];
+ } else {
+ cpuDelta = cpuDataMap[newerSample->timestamp()] -
+ cpuDataMap[olderSample->timestamp()];
+ }
+ if (cpuDelta != 0) {
+ cpuLoad = (newerSample->utime() - olderSample->utime() +
+ newerSample->stime() - olderSample->stime()) * 100. / cpuDelta;
+ }
+ Stats& stats = statsMap[newerSample->timestamp()];
+ stats.uptime = newerSample->uptime();
+ stats.cpu += cpuLoad;
+ stats.rbytes += (newerSample->readbytes() - olderSample->readbytes());
+ stats.wbytes += (newerSample->writebytes() - olderSample->writebytes());
+
+ // Note that all of these are explicitly `long long`s, not int64_t,
+ // so we can't use PRId64 here.
+#define NUMBER "%-13lld"
+ printf("%5lld - %-5lld " NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER "%-9.2f\n",
+#undef NUMBER
+ olderSample->uptime(),
+ newerSample->uptime(),
+ newerSample->rchar() - olderSample->rchar(),
+ newerSample->wchar() - olderSample->wchar(),
+ newerSample->syscr() - olderSample->syscr(),
+ newerSample->syscw() - olderSample->syscw(),
+ newerSample->readbytes() - olderSample->readbytes(),
+ newerSample->writebytes() - olderSample->writebytes(),
+ cpuLoad);
+ isFirstSample = false;
+ }
+ printf("-----------------------------------------------------------------------------\n");
+#define NUMBER "%-13lld"
+ printf("%-15s" NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER "\n",
+#undef NUMBER
+ "Total",
+ newerSample->rchar(),
+ newerSample->wchar(),
+ newerSample->syscr(),
+ newerSample->syscw(),
+ newerSample->readbytes(),
+ newerSample->writebytes());
+ }
+ printf("\nAggregations\n%-10s%-13s%-13s%-13s\n",
+ "Total",
+ "rbytes",
+ "wbytes",
+ "cpu%");
+
+ for (auto it = statsMap.begin(); it != statsMap.end(); it++) {
+ printf("%-10u%-13" PRIu64 "%-13" PRIu64 "%-9.2f\n",
+ it->second.uptime,
+ it->second.rbytes,
+ it->second.wbytes,
+ it->second.cpu);
+ }
+}
+
+}
+
+BootioCollector::BootioCollector(std::string path) {
+ DCHECK_EQ('/', path.back());
+ path_ = path;
+}
+
+void BootioCollector::StartDataCollection(int timeout, int samples) {
+ android::ClearPreviousResults(getStoragePath());
+ int remaining = samples + 1;
+ int delayS = timeout / samples;
+
+ std::unordered_map < int, AppData * > pidDataMap;
+ std::unique_ptr <DataContainer> data(new DataContainer());
+ while (remaining > 0) {
+ time_t currentTimeUtc = time(nullptr);
+ time_t currentUptime = android::GetUptime();
+ CpuData *cpu = data->add_cpu();
+ cpu->set_timestamp(currentTimeUtc);
+ cpu->set_uptime(currentUptime);
+ android::PopulateCpu(*cpu);
+ android::ReadProcData(pidDataMap, *data.get(), currentTimeUtc, currentUptime);
+ remaining--;
+ if (remaining == 0) {
+ continue;
+ }
+ sleep(delayS);
+ }
+ std::string file_data;
+ if (!data->SerializeToString(&file_data)) {
+ LOG(ERROR) << "Failed to serialize";
+ return;
+ }
+ if (!android::base::WriteStringToFile(file_data, getStoragePath())) {
+ LOG(ERROR) << "Failed to write samples";
+ }
+}
+
+void BootioCollector::Print() {
+ std::string file_data;
+ if (!android::base::ReadFileToString(getStoragePath(), &file_data)) {
+ printf("Failed to read data from file.\n");
+ return;
+ }
+ std::unique_ptr <DataContainer> data(new DataContainer());
+ if (!data->ParsePartialFromString(file_data)) {
+ printf("Failed to parse data.\n");
+ return;
+ }
+ std::unordered_map<int, uint64_t> cpuDataMap;
+ for (int i = 0; i < data->cpu_size(); i++) {
+ CpuData cpu_data = data->cpu(i);
+ cpuDataMap[cpu_data.timestamp()] = android::SumCpuValues(cpu_data);
+ }
+ android::PrintPids(*data.get(), cpuDataMap);
+}
+
+
+std::string BootioCollector::getStoragePath() {
+ return path_ + SAMPLES_FILE;
+}
diff --git a/boottime_tools/bootio/bootio_collector.h b/boottime_tools/bootio/bootio_collector.h
new file mode 100644
index 00000000..7296e6f9
--- /dev/null
+++ b/boottime_tools/bootio/bootio_collector.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BOOTIO_COLLECTOR_H_
+#define BOOTIO_COLLECTOR_H_
+
+#include <string>
+#include <android-base/macros.h>
+
+class BootioCollector {
+public:
+ BootioCollector(std::string path);
+
+ void StartDataCollection(int timeout, int samples);
+
+ void Print();
+
+private:
+ std::string getStoragePath();
+
+ std::string path_;
+
+ DISALLOW_COPY_AND_ASSIGN(BootioCollector);
+};
+
+#endif // BOOTIO_COLLECTOR_H_
diff --git a/boottime_tools/bootio/protos.proto b/boottime_tools/bootio/protos.proto
new file mode 100644
index 00000000..d5674ece
--- /dev/null
+++ b/boottime_tools/bootio/protos.proto
@@ -0,0 +1,55 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+message DataContainer {
+ repeated AppData app = 1;
+ repeated CpuData cpu = 2;
+};
+
+message CpuData {
+ required int64 timestamp = 1;
+ required int64 uptime = 2;
+ required int64 utime = 3;
+ required int64 ntime = 4;
+ required int64 stime = 5;
+ required int64 itime = 6;
+ required int64 iowtime = 7;
+ required int64 irqtime = 9;
+ required int64 sirqtime = 10;
+}
+
+message AppData {
+ required int32 pid = 1;
+ required string tname = 2;
+ required string name = 3;
+ repeated AppSample samples = 4;
+}
+
+message AppSample {
+ required int64 timestamp = 1;
+ required int64 uptime = 2;
+ required int64 rchar = 3;
+ required int64 wchar = 4;
+ required int64 syscr = 5;
+ required int64 syscw = 6;
+ required int64 readbytes = 7;
+ required int64 writebytes = 8;
+ required int64 utime = 9;
+ required int64 stime = 10;
+ required int64 rss = 11;
+};
diff --git a/boottime_tools/bootio/sepolicy/bootio.te b/boottime_tools/bootio/sepolicy/bootio.te
new file mode 100644
index 00000000..cd0fb80d
--- /dev/null
+++ b/boottime_tools/bootio/sepolicy/bootio.te
@@ -0,0 +1,12 @@
+# bootio command
+type bootio, domain, coredomain;
+type bootio_exec, exec_type, file_type;
+
+init_daemon_domain(bootio)
+
+# Allow persistent storage in /data/misc/bootio.
+#allow bootio bootio_data_file:dir rw_dir_perms;
+#allow bootio bootio_data_file:file create_file_perms;
+
+# Read access to pseudo filesystems (for /proc/stats, proc/io/io, etc).
+#r_dir_file(bootio, proc)
diff --git a/boottime_tools/bootio/sepolicy/domain.te b/boottime_tools/bootio/sepolicy/domain.te
new file mode 100644
index 00000000..af42fe7e
--- /dev/null
+++ b/boottime_tools/bootio/sepolicy/domain.te
@@ -0,0 +1,2 @@
+# dontaudit bootio kernel:system module_request;
+allow bootio kernel:fd use; \ No newline at end of file
diff --git a/boottime_tools/bootio/sepolicy/file.te b/boottime_tools/bootio/sepolicy/file.te
new file mode 100644
index 00000000..0320bc83
--- /dev/null
+++ b/boottime_tools/bootio/sepolicy/file.te
@@ -0,0 +1,2 @@
+# /data/misc subdirectories
+type bootio_data_file, file_type, data_file_type, core_data_file_type;
diff --git a/boottime_tools/bootio/sepolicy/file_contexts b/boottime_tools/bootio/sepolicy/file_contexts
new file mode 100644
index 00000000..071227c2
--- /dev/null
+++ b/boottime_tools/bootio/sepolicy/file_contexts
@@ -0,0 +1,5 @@
+# System files
+/system/bin/bootio u:object_r:bootio_exec:s0
+
+# Misc data
+/data/misc/bootio(/.*)? u:object_r:bootio_data_file:s0