diff options
author | Yabin Cui <yabinc@google.com> | 2019-02-25 15:22:43 -0800 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2019-02-28 13:50:41 -0800 |
commit | 1befe4fd62dffee1e9a9d19549d18e8604c726e2 (patch) | |
tree | 9369ff7c55379233a8982cab212c81af73662720 /simpleperf/app_api | |
parent | c65e54940b4b7350cc4a2f72e2f2190269909ec5 (diff) | |
download | extras-1befe4fd62dffee1e9a9d19549d18e8604c726e2.tar.gz |
simpleperf: make app api available for profileable apps.
Add api-prepare cmd to prepare recording via app api.
Add api-collect cmd to collect recording data generated by app api. The
recording data is compressed into a zip file.
The two added cmds support both debuggable apps and profileable apps.
Move api_app_profiler.py to api_profiler.py. And use the two added cmds in it.
Also improve app_api code:
1. Fix finding simpleperf.
2. Use time based output filenames.
Bug: 123717243
Test: test manually, will add run python tests later.
Change-Id: I88c20578d01a84bc20ea72276f2cab0f3c4d9109
Diffstat (limited to 'simpleperf/app_api')
4 files changed, 118 insertions, 31 deletions
diff --git a/simpleperf/app_api/cpp/simpleperf.cpp b/simpleperf/app_api/cpp/simpleperf.cpp index 11e70e6f..23bb4fb5 100644 --- a/simpleperf/app_api/cpp/simpleperf.cpp +++ b/simpleperf/app_api/cpp/simpleperf.cpp @@ -23,6 +23,7 @@ #include <sys/socket.h> #include <sys/stat.h> #include <sys/wait.h> +#include <time.h> #include <unistd.h> #include <mutex> @@ -38,7 +39,7 @@ enum RecordCmd { class RecordOptionsImpl { public: - std::string output_filename = "perf.data"; + std::string output_filename; std::string event = "cpu-cycles"; size_t freq = 4000; double duration_in_second = 0.0; @@ -97,9 +98,27 @@ RecordOptions& RecordOptions::TraceOffCpu() { return *this; } +static std::string GetDefaultOutputFilename() { + time_t t = time(nullptr); + struct tm tm; + if (localtime_r(&t, &tm) != &tm) { + return "perf.data"; + } + char* buf = nullptr; + asprintf(&buf, "perf-%02d-%02d-%02d-%02d-%02d.data", tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec); + std::string result = buf; + free(buf); + return result; +} + std::vector<std::string> RecordOptions::ToRecordArgs() const { std::vector<std::string> args; - args.insert(args.end(), {"-o", impl_->output_filename}); + std::string output_filename = impl_->output_filename; + if (output_filename.empty()) { + output_filename = GetDefaultOutputFilename(); + } + args.insert(args.end(), {"-o", output_filename}); args.insert(args.end(), {"-e", impl_->event}); args.insert(args.end(), {"-f", std::to_string(impl_->freq)}); if (impl_->duration_in_second != 0.0) { @@ -147,6 +166,7 @@ class ProfileSessionImpl { private: std::string FindSimpleperf(); + std::string FindSimpleperfInTempDir(); void CheckIfPerfEnabled(); void CreateSimpleperfDataDir(); void CreateSimpleperfProcess(const std::string& simpleperf_path, @@ -251,21 +271,39 @@ static bool IsExecutableFile(const std::string& path) { } std::string ProfileSessionImpl::FindSimpleperf() { - std::vector<std::string> candidates = { - // For debuggable apps, simpleperf is put to the appDir by api_app_profiler.py. - app_data_dir_ + "/simpleperf", - // For profileable apps on Android >= Q, use simpleperf in system image. - "/system/bin/simpleperf" - }; - for (const auto& path : candidates) { - if (IsExecutableFile(path)) { - return path; - } + // 1. Try /data/local/tmp/simpleperf first. Probably it's newer than /system/bin/simpleperf. + std::string simpleperf_path = FindSimpleperfInTempDir(); + if (!simpleperf_path.empty()) { + return simpleperf_path; } - Abort("can't find simpleperf on device. Please run api_app_profiler.py."); + // 2. Try /system/bin/simpleperf, which is available on Android >= Q. + simpleperf_path = "/system/bin/simpleperf"; + if (IsExecutableFile(simpleperf_path)) { + return simpleperf_path; + } + Abort("can't find simpleperf on device. Please run api_profiler.py."); return ""; } +std::string ProfileSessionImpl::FindSimpleperfInTempDir() { + const std::string path = "/data/local/tmp/simpleperf"; + if (!IsExecutableFile(path)) { + return ""; + } + // Copy it to app_dir to execute it. + const std::string to_path = app_data_dir_ + "/simpleperf"; + const std::string copy_cmd = "cp " + path + " " + to_path; + if (system(copy_cmd.c_str()) != 0) { + return ""; + } + const std::string test_cmd = to_path; + // For apps with target sdk >= 29, executing app data file isn't allowed. So test executing it. + if (system(test_cmd.c_str()) != 0) { + return ""; + } + return to_path; +} + static std::string ReadFile(FILE* fp) { std::string s; char buf[200]; @@ -287,7 +325,7 @@ void ProfileSessionImpl::CheckIfPerfEnabled() { std::string s = ReadFile(fp); pclose(fp); if (!s.empty() && s[0] == '1') { - Abort("linux perf events aren't enabled on the device. Please run api_app_profiler.py."); + Abort("linux perf events aren't enabled on the device. Please run api_profiler.py."); } } diff --git a/simpleperf/app_api/cpp/simpleperf.h b/simpleperf/app_api/cpp/simpleperf.h index d6704fc8..309b37b7 100644 --- a/simpleperf/app_api/cpp/simpleperf.h +++ b/simpleperf/app_api/cpp/simpleperf.h @@ -42,7 +42,8 @@ class RecordOptions { RecordOptions(); ~RecordOptions(); /** - * Set output filename. Default is perf.data. The file will be generated under simpleperf_data/. + * Set output filename. Default is perf-<month>-<day>-<hour>-<minute>-<second>.data. + * The file will be generated under simpleperf_data/. */ RecordOptions& SetOutputFilename(const std::string& filename); diff --git a/simpleperf/app_api/java/com/android/simpleperf/ProfileSession.java b/simpleperf/app_api/java/com/android/simpleperf/ProfileSession.java index 10cbb518..f1e2b202 100644 --- a/simpleperf/app_api/java/com/android/simpleperf/ProfileSession.java +++ b/simpleperf/app_api/java/com/android/simpleperf/ProfileSession.java @@ -174,19 +174,52 @@ public class ProfileSession { } private String findSimpleperf() { - String[] candidates = new String[]{ - // For debuggable apps, simpleperf is put to the appDir by api_app_profiler.py. - appDataDir + "/simpleperf", - // For profileable apps on Android >= Q, use simpleperf in system image. - "/system/bin/simpleperf" - }; - for (String path : candidates) { - File file = new File(path); - if (file.isFile()) { - return path; - } + // 1. Try /data/local/tmp/simpleperf. Probably it's newer than /system/bin/simpleperf. + String simpleperfPath = findSimpleperfInTempDir(); + if (simpleperfPath != null) { + return simpleperfPath; + } + // 2. Try /system/bin/simpleperf, which is available on Android >= Q. + simpleperfPath = "/system/bin/simpleperf"; + if (isExecutableFile(simpleperfPath)) { + return simpleperfPath; + } + throw new Error("can't find simpleperf on device. Please run api_profiler.py."); + } + + private boolean isExecutableFile(String path) { + File file = new File(path); + return file.canExecute(); + } + + private String findSimpleperfInTempDir() { + String path = "/data/local/tmp/simpleperf"; + File file = new File(path); + if (!file.isFile()){ + return null; + } + // Copy it to app dir to execute it. + String toPath = appDataDir + "/simpleperf"; + try { + Process process = new ProcessBuilder() + .command("cp", path, toPath).start(); + process.waitFor(); + } catch (Exception e) { + return null; + } + if (!isExecutableFile(toPath)) { + return null; + } + // For apps with target sdk >= 29, executing app data file isn't allowed. So test executing + // it. + try { + Process process = new ProcessBuilder() + .command(toPath).start(); + process.waitFor(); + } catch (Exception e) { + return null; } - throw new Error("can't find simpleperf on device. Please run api_app_profiler.py."); + return toPath; } private void checkIfPerfEnabled() { @@ -206,7 +239,7 @@ public class ProfileSession { value = readInputStream(process.getInputStream()); if (value.startsWith("1")) { throw new Error("linux perf events aren't enabled on the device." + - " Please run api_app_profiler.py."); + " Please run api_profiler.py."); } } diff --git a/simpleperf/app_api/java/com/android/simpleperf/RecordOptions.java b/simpleperf/app_api/java/com/android/simpleperf/RecordOptions.java index de5a715c..3ed39fb7 100644 --- a/simpleperf/app_api/java/com/android/simpleperf/RecordOptions.java +++ b/simpleperf/app_api/java/com/android/simpleperf/RecordOptions.java @@ -17,6 +17,9 @@ package com.android.simpleperf; import android.system.Os; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; @@ -39,7 +42,8 @@ import java.util.List; public class RecordOptions { /** - * Set output filename. Default is perf.data. The file will be generated under simpleperf_data/. + * Set output filename. Default is perf-<month>-<day>-<hour>-<minute>-<second>.data. + * The file will be generated under simpleperf_data/. */ public RecordOptions setOutputFilename(String filename) { outputFilename = filename; @@ -110,8 +114,13 @@ public class RecordOptions { */ public List<String> toRecordArgs() { ArrayList<String> args = new ArrayList<>(); + + String filename = outputFilename; + if (filename == null) { + filename = getDefaultOutputFilename(); + } args.add("-o"); - args.add(outputFilename); + args.add(filename); args.add("-e"); args.add(event); args.add("-f"); @@ -146,7 +155,13 @@ public class RecordOptions { return args; } - private String outputFilename = "perf.data"; + private String getDefaultOutputFilename() { + LocalDateTime time = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("'perf'-MM-dd-HH-mm-ss'.data'"); + return time.format(formatter); + } + + private String outputFilename; private String event = "cpu-cycles"; private int freq = 4000; private double durationInSecond = 0.0; |