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