summaryrefslogtreecommitdiff
path: root/simpleperf/app_api
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2019-02-25 15:22:43 -0800
committerYabin Cui <yabinc@google.com>2019-02-28 13:50:41 -0800
commit1befe4fd62dffee1e9a9d19549d18e8604c726e2 (patch)
tree9369ff7c55379233a8982cab212c81af73662720 /simpleperf/app_api
parentc65e54940b4b7350cc4a2f72e2f2190269909ec5 (diff)
downloadextras-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')
-rw-r--r--simpleperf/app_api/cpp/simpleperf.cpp66
-rw-r--r--simpleperf/app_api/cpp/simpleperf.h3
-rw-r--r--simpleperf/app_api/java/com/android/simpleperf/ProfileSession.java59
-rw-r--r--simpleperf/app_api/java/com/android/simpleperf/RecordOptions.java21
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;