diff options
Diffstat (limited to 'simpleperf/environment.cpp')
-rw-r--r-- | simpleperf/environment.cpp | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp index 7fff85f1..03fda1ac 100644 --- a/simpleperf/environment.cpp +++ b/simpleperf/environment.cpp @@ -17,6 +17,7 @@ #include "environment.h" #include <inttypes.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <sys/utsname.h> @@ -37,9 +38,11 @@ #include <sys/system_properties.h> #endif +#include "IOEventLoop.h" #include "read_elf.h" #include "thread_tree.h" #include "utils.h" +#include "workload.h" class LineReader { public: @@ -512,3 +515,104 @@ void PrepareVdsoFile() { } Dso::SetVdsoFile(std::move(tmpfile), sizeof(size_t) == sizeof(uint64_t)); } + +int WaitForAppProcess(const std::string& package_name) { + size_t loop_count = 0; + while (true) { + std::vector<pid_t> pids = GetAllProcesses(); + for (pid_t pid : pids) { + std::string cmdline; + if (!android::base::ReadFileToString("/proc/" + std::to_string(pid) + "/cmdline", &cmdline)) { + // Maybe we don't have permission to read it. + continue; + } + cmdline = android::base::Basename(cmdline); + if (cmdline == package_name) { + if (loop_count > 0u) { + LOG(INFO) << "Got process " << pid << " for package " << package_name; + } + return pid; + } + } + if (++loop_count == 1u) { + LOG(INFO) << "Waiting for process of app " << package_name; + } + usleep(1000); + } +} + +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) { + // 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. + 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; + } + + // 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"}; + 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); + } + } + std::unique_ptr<Workload> workload = Workload::CreateWorkload(new_args); + if (!workload) { + return false; + } + + IOEventLoop loop; + bool need_to_kill_child = false; + if (!loop.AddSignalEvents({SIGINT, SIGTERM, SIGHUP}, + [&]() { need_to_kill_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); + } + int exit_code; + if (!workload->WaitChildProcess(&exit_code) || exit_code != 0) { + return false; + } + + // 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; + } + if (!Workload::RunCmd({"run-as", app_package_name, "rm", output_basename})) { + return false; + } + } + return true; +} |