163 lines
5.0 KiB
C++
163 lines
5.0 KiB
C++
/*
|
|
* 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;
|
|
}
|
|
|