diff options
author | Yabin Cui <yabinc@google.com> | 2022-01-25 00:29:09 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2022-01-25 00:29:09 +0000 |
commit | 17fc71870f3f98dcca010e951633965010e885e3 (patch) | |
tree | 8f36aad9efe2344f14eff927af1b57ab186dc9e6 | |
parent | c393ae9015e55a9a168b56dbbb5dca5a714f9689 (diff) | |
parent | 321bfe67349a43fc704c0ddb5f2df69d7806d791 (diff) | |
download | extras-17fc71870f3f98dcca010e951633965010e885e3.tar.gz |
Merge "simpleperf: add boot-record command."
-rw-r--r-- | simpleperf/Android.bp | 11 | ||||
-rw-r--r-- | simpleperf/cmd_boot_record.cpp | 192 | ||||
-rw-r--r-- | simpleperf/cmd_boot_record_test.cpp | 41 | ||||
-rw-r--r-- | simpleperf/command.cpp | 2 | ||||
-rw-r--r-- | simpleperf/main.cpp | 4 | ||||
-rw-r--r-- | simpleperf/simpleperf.rc | 3 |
6 files changed, 253 insertions, 0 deletions
diff --git a/simpleperf/Android.bp b/simpleperf/Android.bp index beb8a773..62d5ef79 100644 --- a/simpleperf/Android.bp +++ b/simpleperf/Android.bp @@ -238,6 +238,11 @@ cc_defaults { "utils.cpp", ], target: { + android: { + srcs: [ + "cmd_boot_record.cpp", + ], + }, linux: { srcs: [ "CallChainJoiner.cpp", @@ -313,6 +318,7 @@ cc_binary { ], }, }, + init_rc: ["simpleperf.rc"], } cc_library { @@ -548,6 +554,11 @@ cc_defaults { "utils_test.cpp", ], target: { + android: { + srcs: [ + "cmd_boot_record_test.cpp", + ], + }, linux: { srcs: [ "CallChainJoiner_test.cpp", diff --git a/simpleperf/cmd_boot_record.cpp b/simpleperf/cmd_boot_record.cpp new file mode 100644 index 00000000..8d037631 --- /dev/null +++ b/simpleperf/cmd_boot_record.cpp @@ -0,0 +1,192 @@ +/* + * 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 diff --git a/simpleperf/cmd_boot_record_test.cpp b/simpleperf/cmd_boot_record_test.cpp new file mode 100644 index 00000000..25b2aff0 --- /dev/null +++ b/simpleperf/cmd_boot_record_test.cpp @@ -0,0 +1,41 @@ +/* + * 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 <gtest/gtest.h> + +#include <android-base/properties.h> + +#include "command.h" +#include "test_util.h" +#include "workload.h" + +using namespace simpleperf; + +static std::unique_ptr<Command> BootRecordCmd() { + return CreateCommandInstance("boot-record"); +} + +TEST(cmd_boot_record, smoke) { + TEST_REQUIRE_ROOT(); + ASSERT_TRUE(BootRecordCmd()->Run({"--enable", "-a -g --duration 1"})); + ASSERT_EQ(android::base::GetProperty("persist.simpleperf.boot_record", ""), "-a -g --duration 1"); + // After reboot, init script will run boot-record cmd with --record. But since we can't reboot + // the device to test the option, run it directly here. + ASSERT_TRUE(BootRecordCmd()->Run({"--record", "-a --duration 0.001"})); + ASSERT_TRUE(BootRecordCmd()->Run({"--disable"})); + ASSERT_EQ(android::base::GetProperty("persist.simpleperf.boot_record", ""), ""); + ASSERT_TRUE(Workload::RunCmd({"rm", "-rf", "/data/simpleperf_boot_data"})); +} diff --git a/simpleperf/command.cpp b/simpleperf/command.cpp index 0a891e93..f48b4649 100644 --- a/simpleperf/command.cpp +++ b/simpleperf/command.cpp @@ -183,6 +183,7 @@ const std::vector<std::string> GetAllCommandNames() { return names; } +extern void RegisterBootRecordCommand(); extern void RegisterDumpRecordCommand(); extern void RegisterHelpCommand(); extern void RegisterInjectCommand(); @@ -217,6 +218,7 @@ class CommandRegister { RegisterMonitorCommand(); #if defined(__ANDROID__) RegisterAPICommands(); + RegisterBootRecordCommand(); #endif #endif } diff --git a/simpleperf/main.cpp b/simpleperf/main.cpp index d6234520..62fcd6f7 100644 --- a/simpleperf/main.cpp +++ b/simpleperf/main.cpp @@ -20,12 +20,16 @@ #include "command.h" #include "environment.h" +#include "utils.h" using namespace simpleperf; #if defined(__ANDROID__) bool AndroidSecurityCheck() { + if (IsRoot()) { + return true; + } // Simpleperf can be executed by the shell, or by apps themselves. To avoid malicious apps // exploiting perf_event_open interface via simpleperf, simpleperf needs proof that the user // is expecting simpleperf to be ran: diff --git a/simpleperf/simpleperf.rc b/simpleperf/simpleperf.rc new file mode 100644 index 00000000..213b6fae --- /dev/null +++ b/simpleperf/simpleperf.rc @@ -0,0 +1,3 @@ + +on boot && property:ro.debuggable=1 && property:persist.simpleperf.boot_record=* + exec_background /system/bin/simpleperf boot-record --log-to-android-buffer --record "${persist.simpleperf.boot_record}" |