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