summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2022-01-25 00:29:09 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2022-01-25 00:29:09 +0000
commit17fc71870f3f98dcca010e951633965010e885e3 (patch)
tree8f36aad9efe2344f14eff927af1b57ab186dc9e6
parentc393ae9015e55a9a168b56dbbb5dca5a714f9689 (diff)
parent321bfe67349a43fc704c0ddb5f2df69d7806d791 (diff)
downloadextras-17fc71870f3f98dcca010e951633965010e885e3.tar.gz
Merge "simpleperf: add boot-record command."
-rw-r--r--simpleperf/Android.bp11
-rw-r--r--simpleperf/cmd_boot_record.cpp192
-rw-r--r--simpleperf/cmd_boot_record_test.cpp41
-rw-r--r--simpleperf/command.cpp2
-rw-r--r--simpleperf/main.cpp4
-rw-r--r--simpleperf/simpleperf.rc3
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}"