193 lines
6.0 KiB
C++
193 lines
6.0 KiB
C++
/*
|
|
* Copyright (C) 2022 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 <sys/stat.h>
|
|
|
|
#include <android-base/properties.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/strings.h>
|
|
|
|
#include "cmd_record_impl.h"
|
|
#include "command.h"
|
|
#include "utils.h"
|
|
#include "workload.h"
|
|
|
|
namespace simpleperf {
|
|
namespace {
|
|
|
|
enum class Action {
|
|
NONE,
|
|
ENABLE,
|
|
DISABLE,
|
|
RECORD,
|
|
};
|
|
|
|
const std::string RECORD_OUT_DIR = "/data/simpleperf_boot_data";
|
|
|
|
class BootRecordCommand : public Command {
|
|
public:
|
|
BootRecordCommand()
|
|
: Command(
|
|
"boot-record", "record at boot time",
|
|
// clang-format off
|
|
"Usage: simpleperf boot-record [options]\n"
|
|
" Record boot-time profiles. Only supported on userdebug/eng build. The record file will be\n"
|
|
" stored in /data/simpleperf_boot_data.\n"
|
|
"--enable <record_options> Enable boot time recording. Record options are options passed\n"
|
|
" to simpleperf record cmd, like \"-a -g --duration 10\".\n"
|
|
"--disable Disable record at boot time.\n"
|
|
#if 0
|
|
// The following option is only used internally.
|
|
"--record <record_options> Record with record options.\n"
|
|
#endif
|
|
// clang-format on
|
|
) {
|
|
}
|
|
|
|
bool Run(const std::vector<std::string>& args);
|
|
|
|
private:
|
|
bool ParseOptions(const std::vector<std::string>& args);
|
|
bool SetRecordOptions(const std::string& record_options);
|
|
bool CheckRecordOptions(const std::string& record_options);
|
|
bool CreateOutDir();
|
|
bool Record();
|
|
std::string GetDefaultOutputFilename();
|
|
|
|
Action action_ = Action::NONE;
|
|
std::string record_options_;
|
|
};
|
|
|
|
bool BootRecordCommand::Run(const std::vector<std::string>& args) {
|
|
if (!ParseOptions(args)) {
|
|
return false;
|
|
}
|
|
if (action_ == Action::ENABLE) {
|
|
if (!CheckRecordOptions(record_options_) || !SetRecordOptions(record_options_) ||
|
|
!CreateOutDir()) {
|
|
return false;
|
|
}
|
|
LOG(INFO) << "After boot, boot profile will be stored in " << RECORD_OUT_DIR;
|
|
return true;
|
|
}
|
|
if (action_ == Action::DISABLE) {
|
|
return SetRecordOptions("");
|
|
}
|
|
if (action_ == Action::RECORD) {
|
|
return Record();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool BootRecordCommand::ParseOptions(const std::vector<std::string>& args) {
|
|
const OptionFormatMap option_formats = {
|
|
{"--enable", {OptionValueType::STRING, OptionType::SINGLE}},
|
|
{"--disable", {OptionValueType::NONE, OptionType::SINGLE}},
|
|
{"--record", {OptionValueType::STRING, OptionType::SINGLE}},
|
|
};
|
|
OptionValueMap options;
|
|
std::vector<std::pair<OptionName, OptionValue>> ordered_options;
|
|
std::vector<std::string> non_option_args;
|
|
if (!PreprocessOptions(args, option_formats, &options, &ordered_options, &non_option_args)) {
|
|
return false;
|
|
}
|
|
if (auto value = options.PullValue("--enable"); value) {
|
|
action_ = Action::ENABLE;
|
|
record_options_ = *value->str_value;
|
|
} else if (options.PullBoolValue("--disable")) {
|
|
action_ = Action::DISABLE;
|
|
} else if (auto value = options.PullValue("--record"); value) {
|
|
action_ = Action::RECORD;
|
|
record_options_ = *value->str_value;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool BootRecordCommand::SetRecordOptions(const std::string& record_options) {
|
|
const std::string prop_name = "persist.simpleperf.boot_record";
|
|
if (!android::base::SetProperty(prop_name, record_options)) {
|
|
LOG(ERROR) << "Failed to SetProperty " << prop_name << " to \"" << record_options << "\"";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool BootRecordCommand::CheckRecordOptions(const std::string& record_options) {
|
|
std::vector<std::string> args = android::base::Split(record_options, " ");
|
|
|
|
OptionValueMap options;
|
|
std::vector<std::pair<OptionName, OptionValue>> ordered_options;
|
|
std::vector<std::string> non_option_args;
|
|
if (!PreprocessOptions(args, GetRecordCmdOptionFormats(), &options, &ordered_options,
|
|
&non_option_args)) {
|
|
LOG(ERROR) << "Invalid record options.";
|
|
return false;
|
|
}
|
|
if (!non_option_args.empty()) {
|
|
LOG(ERROR) << "Running child command isn't allowed";
|
|
return false;
|
|
}
|
|
if (auto value = options.PullValue("-o"); value) {
|
|
LOG(ERROR) << "-o option isn't allowed. The output file is stored in " << RECORD_OUT_DIR;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool BootRecordCommand::CreateOutDir() {
|
|
if (!IsDir(RECORD_OUT_DIR)) {
|
|
if (mkdir(RECORD_OUT_DIR.c_str(), 0775) != 0) {
|
|
PLOG(ERROR) << "failed to create dir " << RECORD_OUT_DIR;
|
|
return false;
|
|
}
|
|
return Workload::RunCmd(
|
|
{"chcon", "u:object_r:simpleperf_boot_data_file:s0", "/data/simpleperf_boot_data"});
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool BootRecordCommand::Record() {
|
|
if (!CheckRecordOptions(record_options_)) {
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string> args = android::base::Split(record_options_, " ");
|
|
std::string output_file = RECORD_OUT_DIR + "/" + GetDefaultOutputFilename();
|
|
args.emplace_back("-o");
|
|
args.emplace_back(output_file);
|
|
std::unique_ptr<Command> record_cmd = CreateCommandInstance("record");
|
|
CHECK(record_cmd != nullptr);
|
|
return record_cmd->Run(args);
|
|
}
|
|
|
|
std::string BootRecordCommand::GetDefaultOutputFilename() {
|
|
time_t t = time(nullptr);
|
|
struct tm tm;
|
|
if (localtime_r(&t, &tm) != &tm) {
|
|
return "perf.data";
|
|
}
|
|
return android::base::StringPrintf("perf-%04d%02d%02d-%02d-%02d-%02d.data", tm.tm_year + 1900,
|
|
tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void RegisterBootRecordCommand() {
|
|
RegisterCommand("boot-record", [] { return std::unique_ptr<Command>(new BootRecordCommand); });
|
|
}
|
|
|
|
} // namespace simpleperf
|