/* * 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 #include #include #include #include #include #include #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 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 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 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; }