/* * 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 #include #include #include #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 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 with record options.\n" #endif // clang-format on ) { } bool Run(const std::vector& args); private: bool ParseOptions(const std::vector& 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& 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& args) { const OptionFormatMap option_formats = { {"--enable", {OptionValueType::STRING, OptionType::SINGLE}}, {"--disable", {OptionValueType::NONE, OptionType::SINGLE}}, {"--record", {OptionValueType::STRING, OptionType::SINGLE}}, }; OptionValueMap options; std::vector> ordered_options; std::vector 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 args = android::base::Split(record_options, " "); OptionValueMap options; std::vector> ordered_options; std::vector 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 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 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(new BootRecordCommand); }); } } // namespace simpleperf