summaryrefslogtreecommitdiff
path: root/simpleperf/environment.cpp
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2019-01-10 15:35:39 -0800
committerYabin Cui <yabinc@google.com>2019-01-23 16:27:00 -0800
commit1a30a5878984bee10aee06e674a7f5cc0269d9ef (patch)
tree73488effe90c67f4902ad462f2bca4eec8d986a2 /simpleperf/environment.cpp
parent918f046c3972527f5e8ac606bd11d1ab180f9524 (diff)
downloadextras-1a30a5878984bee10aee06e674a7f5cc0269d9ef.tar.gz
simpleperf: Use simpleperf_app_runner to profile profileable apps.
Simpleperf uses run-as to run in apps' context to profile debuggable apps. In Android Q, we want to profile <profileable shell="true"> apps. To support that, do below changes: 1. Add simpleperf_app_runner, which is similar to run-as, but is limited to only run simpleperf commands in profileable apps. 2. Add code using simpleperf_app_runner inside simpleperf, so it doesn't change current interface of using simpleperf. Bug: 118835348 Test: run simpleperf manually. Test: run simpleperf_unit_test. Change-Id: I85a8e3c80fe0e3ccdee97de38be968cbccd1d263
Diffstat (limited to 'simpleperf/environment.cpp')
-rw-r--r--simpleperf/environment.cpp202
1 files changed, 140 insertions, 62 deletions
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index 507859ec..8d18b526 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -622,119 +622,197 @@ std::set<pid_t> WaitForAppProcesses(const std::string& package_name) {
}
}
-class ScopedFile {
- public:
- ScopedFile(const std::string& filepath, const std::string& app_package_name = "")
- : filepath_(filepath), app_package_name_(app_package_name) {}
+namespace {
- ~ScopedFile() {
- if (app_package_name_.empty()) {
- unlink(filepath_.c_str());
- } else {
- Workload::RunCmd({"run-as", app_package_name_, "rm", "-rf", filepath_});
+class InAppRunner {
+ public:
+ InAppRunner(const std::string& package_name) : package_name_(package_name) {}
+ virtual ~InAppRunner() {
+ if (!tracepoint_file_.empty()) {
+ unlink(tracepoint_file_.c_str());
}
}
+ virtual bool Prepare() = 0;
+ bool RunCmdInApp(const std::string& cmd, const std::vector<std::string>& args,
+ size_t workload_args_size, const std::string& output_filepath,
+ bool need_tracepoint_events);
+ protected:
+ virtual std::vector<std::string> GetPrefixArgs(const std::string& cmd) = 0;
- private:
- std::string filepath_;
- std::string app_package_name_;
+ const std::string package_name_;
+ std::string tracepoint_file_;
};
-bool RunInAppContext(const std::string& app_package_name, const std::string& cmd,
- const std::vector<std::string>& args, size_t workload_args_size,
- const std::string& output_filepath, bool need_tracepoint_events) {
- // 1. Test if the package exists.
- if (!Workload::RunCmd({"run-as", app_package_name, "echo", ">/dev/null"}, false)) {
- LOG(ERROR) << "Package " << app_package_name << " doesn't exist or isn't debuggable.";
- return false;
- }
-
- // 2. Copy simpleperf binary to the package. Create tracepoint_file if needed.
- std::string simpleperf_path;
- if (!android::base::Readlink("/proc/self/exe", &simpleperf_path)) {
- PLOG(ERROR) << "ReadLink failed";
- return false;
- }
- if (!Workload::RunCmd({"run-as", app_package_name, "cp", simpleperf_path, "simpleperf"})) {
- return false;
- }
- ScopedFile scoped_simpleperf("simpleperf", app_package_name);
- std::unique_ptr<ScopedFile> scoped_tracepoint_file;
- const std::string tracepoint_file = "/data/local/tmp/tracepoint_events";
+bool InAppRunner::RunCmdInApp(const std::string& cmd, const std::vector<std::string>& cmd_args,
+ size_t workload_args_size, const std::string& output_filepath,
+ bool need_tracepoint_events) {
+ // 1. Build cmd args running in app's context.
+ std::vector<std::string> args = GetPrefixArgs(cmd);
+ args.insert(args.end(), {"--in-app", "--log", GetLogSeverityName()});
if (need_tracepoint_events) {
// Since we can't read tracepoint events from tracefs in app's context, we need to prepare
// them in tracepoint_file in shell's context, and pass the path of tracepoint_file to the
// child process using --tracepoint-events option.
+ const std::string tracepoint_file = "/data/local/tmp/tracepoint_events";
if (!android::base::WriteStringToFile(GetTracepointEvents(), tracepoint_file)) {
PLOG(ERROR) << "Failed to store tracepoint events";
return false;
}
- scoped_tracepoint_file.reset(new ScopedFile(tracepoint_file));
+ tracepoint_file_ = tracepoint_file;
+ args.insert(args.end(), {"--tracepoint-events", tracepoint_file_});
}
- // 3. Prepare to start child process to profile.
- std::string output_basename = output_filepath.empty() ? "" :
- android::base::Basename(output_filepath);
- std::vector<std::string> new_args =
- {"run-as", app_package_name, "./simpleperf", cmd, "--in-app", "--log", GetLogSeverityName()};
- if (need_tracepoint_events) {
- new_args.push_back("--tracepoint-events");
- new_args.push_back(tracepoint_file);
+ android::base::unique_fd out_fd;
+ if (!output_filepath.empty()) {
+ // A process running in app's context can't open a file outside it's data directory to write.
+ // So pass it a file descriptor to write.
+ out_fd = FileHelper::OpenWriteOnly(output_filepath);
+ if (out_fd == -1) {
+ PLOG(ERROR) << "Failed to open " << output_filepath;
+ return false;
+ }
+ args.insert(args.end(), {"--out-fd", std::to_string(int(out_fd))});
}
- for (size_t i = 0; i < args.size(); ++i) {
- if (i >= args.size() - workload_args_size || args[i] != "-o") {
- new_args.push_back(args[i]);
- } else {
- new_args.push_back(args[i++]);
- new_args.push_back(output_basename);
+
+ // We can't send signal to a process running in app's context. So use a pipe file to send stop
+ // signal.
+ android::base::unique_fd stop_signal_rfd;
+ android::base::unique_fd stop_signal_wfd;
+ if (!android::base::Pipe(&stop_signal_rfd, &stop_signal_wfd, 0)) {
+ PLOG(ERROR) << "pipe";
+ return false;
+ }
+ args.insert(args.end(), {"--stop-signal-fd", std::to_string(int(stop_signal_rfd))});
+
+ for (size_t i = 0; i < cmd_args.size(); ++i) {
+ if (i < cmd_args.size() - workload_args_size) {
+ // Omit "-o output_file". It is replaced by "--out-fd fd".
+ if (cmd_args[i] == "-o" || cmd_args[i] == "--app") {
+ i++;
+ continue;
+ }
}
+ args.push_back(cmd_args[i]);
+ }
+ char* argv[args.size() + 1];
+ for (size_t i = 0; i < args.size(); ++i) {
+ argv[i] = &args[i][0];
}
- std::unique_ptr<Workload> workload = Workload::CreateWorkload(new_args);
+ argv[args.size()] = nullptr;
+
+ // 2. Run child process in app's context.
+ auto ChildProcFn = [&]() {
+ stop_signal_wfd.reset();
+ execvp(argv[0], argv);
+ exit(1);
+ };
+ std::unique_ptr<Workload> workload = Workload::CreateWorkload(ChildProcFn);
if (!workload) {
return false;
}
+ stop_signal_rfd.reset();
+ // Wait on signals.
IOEventLoop loop;
- bool need_to_kill_child = false;
+ bool need_to_stop_child = false;
std::vector<int> stop_signals = {SIGINT, SIGTERM};
if (!SignalIsIgnored(SIGHUP)) {
stop_signals.push_back(SIGHUP);
}
if (!loop.AddSignalEvents(stop_signals,
- [&]() { need_to_kill_child = true; return loop.ExitLoop(); })) {
+ [&]() { need_to_stop_child = true; return loop.ExitLoop(); })) {
return false;
}
if (!loop.AddSignalEvent(SIGCHLD, [&]() { return loop.ExitLoop(); })) {
return false;
}
- // 4. Create child process to run run-as, and wait for the child process.
if (!workload->Start()) {
return false;
}
if (!loop.RunLoop()) {
return false;
}
- if (need_to_kill_child) {
- // The child process can exit before we kill it, so don't report kill errors.
- Workload::RunCmd({"run-as", app_package_name, "pkill", "simpleperf"}, false);
+ if (need_to_stop_child) {
+ stop_signal_wfd.reset();
}
int exit_code;
if (!workload->WaitChildProcess(&exit_code) || exit_code != 0) {
return false;
}
+ return true;
+}
- // 5. If there is any output file, copy it from the app's directory.
- if (!output_filepath.empty()) {
- if (!Workload::RunCmd({"run-as", app_package_name, "cat", output_basename,
- ">" + output_filepath})) {
- return false;
+class RunAs : public InAppRunner {
+ public:
+ RunAs(const std::string& package_name) : InAppRunner(package_name) {}
+ virtual ~RunAs() {
+ if (simpleperf_copied_in_app_) {
+ Workload::RunCmd({"run-as", package_name_, "rm", "-rf", "simpleperf"});
}
- if (!Workload::RunCmd({"run-as", app_package_name, "rm", output_basename})) {
+ }
+ bool Prepare() override;
+
+ protected:
+ std::vector<std::string> GetPrefixArgs(const std::string& cmd) {
+ return {"run-as", package_name_,
+ simpleperf_copied_in_app_ ? "./simpleperf" : simpleperf_path_, cmd,
+ "--app", package_name_};
+ }
+
+ bool simpleperf_copied_in_app_ = false;
+ std::string simpleperf_path_;
+};
+
+bool RunAs::Prepare() {
+ // Test if run-as can access the package.
+ if (!Workload::RunCmd({"run-as", package_name_, "echo", ">/dev/null", "2>/dev/null"}, false)) {
+ return false;
+ }
+ // run-as can't run /data/local/tmp/simpleperf directly. So copy simpleperf binary if needed.
+ if (!android::base::Readlink("/proc/self/exe", &simpleperf_path_)) {
+ PLOG(ERROR) << "ReadLink failed";
+ return false;
+ }
+ if (android::base::StartsWith(simpleperf_path_, "/system")) {
+ return true;
+ }
+ if (!Workload::RunCmd({"run-as", package_name_, "cp", simpleperf_path_, "simpleperf"})) {
+ return false;
+ }
+ simpleperf_copied_in_app_ = true;
+ return true;
+}
+
+class SimpleperfAppRunner : public InAppRunner {
+ public:
+ SimpleperfAppRunner(const std::string& package_name) : InAppRunner(package_name) {}
+ bool Prepare() override {
+ return GetAndroidVersion() >= kAndroidVersionP + 1;
+ }
+
+ protected:
+ std::vector<std::string> GetPrefixArgs(const std::string& cmd) {
+ return {"simpleperf_app_runner", package_name_, cmd};
+ }
+};
+
+} // namespace
+
+bool RunInAppContext(const std::string& app_package_name, const std::string& cmd,
+ const std::vector<std::string>& args, size_t workload_args_size,
+ const std::string& output_filepath, bool need_tracepoint_events) {
+ std::unique_ptr<InAppRunner> in_app_runner(new RunAs(app_package_name));
+ if (!in_app_runner->Prepare()) {
+ in_app_runner.reset(new SimpleperfAppRunner(app_package_name));
+ if (!in_app_runner->Prepare()) {
+ LOG(ERROR) << "Package " << app_package_name
+ << " doesn't exist or isn't debuggable/profileable.";
return false;
}
}
- return true;
+ return in_app_runner->RunCmdInApp(cmd, args, workload_args_size, output_filepath,
+ need_tracepoint_events);
}
static std::string default_package_name;