summaryrefslogtreecommitdiff
path: root/simpleperf/app_api
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2019-03-01 17:24:59 -0800
committerYabin Cui <yabinc@google.com>2019-05-01 11:22:49 -0700
commite2f99e6102781356aa00ef38efa0802d6ec63fe2 (patch)
treedb128cb5d24ccc228821a911e17dfcadbf2b1822 /simpleperf/app_api
parentf225e8c9342f9916b3be6b6c9eaabe46031c7c4c (diff)
downloadextras-e2f99e6102781356aa00ef38efa0802d6ec63fe2.tar.gz
simpleperf: add test for start/stop api.
Add testApiProfiler class in test.py to test start/stop api. Avoid starting more than one profile threads in CppApi and JavaApi. Also add assert check to show --trace-offcpu and pause/resume recording can't be used at the same time. Also add app_api in simpleperf_script.zip to release it in ndk. Also use vfork instead of fork in cpp_api to avoid deadlock when forking new processes. Bug: 123717243 Test: run test.py TestApiProfiler* on Android N/O/P/Q. Change-Id: Id382cf59f59bd7f7efb04259cc301ba4b955db78
Diffstat (limited to 'simpleperf/app_api')
-rw-r--r--simpleperf/app_api/cpp/simpleperf.cpp94
-rw-r--r--simpleperf/app_api/java/com/android/simpleperf/ProfileSession.java10
2 files changed, 81 insertions, 23 deletions
diff --git a/simpleperf/app_api/cpp/simpleperf.cpp b/simpleperf/app_api/cpp/simpleperf.cpp
index 23bb4fb5..54b331a3 100644
--- a/simpleperf/app_api/cpp/simpleperf.cpp
+++ b/simpleperf/app_api/cpp/simpleperf.cpp
@@ -188,6 +188,7 @@ class ProfileSessionImpl {
pid_t simpleperf_pid_ = -1;
int control_fd_ = -1;
int reply_fd_ = -1;
+ bool trace_offcpu_ = false;
};
ProfileSessionImpl::~ProfileSessionImpl() {
@@ -204,6 +205,11 @@ void ProfileSessionImpl::StartRecording(const std::vector<std::string> &args) {
if (state_ != NOT_YET_STARTED) {
Abort("startRecording: session in wrong state %d", state_);
}
+ for (const auto& arg : args) {
+ if (arg == "--trace-offcpu") {
+ trace_offcpu_ = true;
+ }
+ }
std::string simpleperf_path = FindSimpleperf();
CheckIfPerfEnabled();
CreateSimpleperfDataDir();
@@ -216,6 +222,9 @@ void ProfileSessionImpl::PauseRecording() {
if (state_ != STARTED) {
Abort("pauseRecording: session in wrong state %d", state_);
}
+ if (trace_offcpu_) {
+ Abort("--trace-offcpu doesn't work well with pause/resume recording");
+ }
SendCmd("pause");
state_ = PAUSED;
}
@@ -270,6 +279,61 @@ static bool IsExecutableFile(const std::string& path) {
return false;
}
+static std::string ReadFile(FILE* fp) {
+ std::string s;
+ if (fp == nullptr) {
+ return s;
+ }
+ char buf[200];
+ while (true) {
+ ssize_t n = fread(buf, 1, sizeof(buf), fp);
+ if (n <= 0) {
+ break;
+ }
+ s.insert(s.end(), buf, buf + n);
+ }
+ fclose(fp);
+ return s;
+}
+
+static bool RunCmd(std::vector<const char*> args, std::string* stdout) {
+ int stdout_fd[2];
+ if (pipe(stdout_fd) != 0) {
+ return false;
+ }
+ args.push_back(nullptr);
+ // Fork handlers (like gsl_library_close) may hang in a multi-thread environment.
+ // So we use vfork instead of fork to avoid calling them.
+ int pid = vfork();
+ if (pid == -1) {
+ return false;
+ }
+ if (pid == 0) {
+ // child process
+ close(stdout_fd[0]);
+ dup2(stdout_fd[1], 1);
+ close(stdout_fd[1]);
+ execvp(const_cast<char*>(args[0]), const_cast<char**>(args.data()));
+ _exit(1);
+ }
+ // parent process
+ close(stdout_fd[1]);
+ int status;
+ pid_t result = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+ if (result == -1) {
+ Abort("failed to call waitpid: %s", strerror(errno));
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ return false;
+ }
+ if (stdout == nullptr) {
+ close(stdout_fd[0]);
+ } else {
+ *stdout = ReadFile(fdopen(stdout_fd[0], "r"));
+ }
+ return true;
+}
+
std::string ProfileSessionImpl::FindSimpleperf() {
// 1. Try /data/local/tmp/simpleperf first. Probably it's newer than /system/bin/simpleperf.
std::string simpleperf_path = FindSimpleperfInTempDir();
@@ -292,38 +356,21 @@ std::string ProfileSessionImpl::FindSimpleperfInTempDir() {
}
// 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) {
+ if (!RunCmd({"/system/bin/cp", path.c_str(), to_path.c_str()}, nullptr)) {
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) {
+ if (!RunCmd({to_path.c_str()}, nullptr)) {
return "";
}
return to_path;
}
-static std::string ReadFile(FILE* fp) {
- std::string s;
- char buf[200];
- while (true) {
- ssize_t n = fread(buf, 1, sizeof(buf), fp);
- if (n <= 0) {
- break;
- }
- s.insert(s.end(), buf, buf + n);
- }
- return s;
-}
-
void ProfileSessionImpl::CheckIfPerfEnabled() {
- FILE* fp = popen("/system/bin/getprop security.perf_harden", "re");
- if (fp == nullptr) {
+ std::string s;
+ if (!RunCmd({"/system/bin/getprop", "security.perf_harden"}, &s)) {
return; // Omit check if getprop doesn't exist.
}
- 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_profiler.py.");
}
@@ -366,7 +413,9 @@ void ProfileSessionImpl::CreateSimpleperfProcess(const std::string &simpleperf_p
argv[args.size()] = nullptr;
// 3. Start simpleperf process.
- int pid = fork();
+ // Fork handlers (like gsl_library_close) may hang in a multi-thread environment.
+ // So we use vfork instead of fork to avoid calling them.
+ int pid = vfork();
if (pid == -1) {
Abort("failed to fork: %s", strerror(errno));
}
@@ -415,7 +464,6 @@ ProfileSession::ProfileSession() {
Abort("failed to open /proc/self/cmdline: %s", strerror(errno));
}
std::string s = ReadFile(fp);
- fclose(fp);
for (int i = 0; i < s.size(); i++) {
if (s[i] == '\0') {
s = s.substr(0, i);
diff --git a/simpleperf/app_api/java/com/android/simpleperf/ProfileSession.java b/simpleperf/app_api/java/com/android/simpleperf/ProfileSession.java
index f1e2b202..ce043c41 100644
--- a/simpleperf/app_api/java/com/android/simpleperf/ProfileSession.java
+++ b/simpleperf/app_api/java/com/android/simpleperf/ProfileSession.java
@@ -65,6 +65,7 @@ public class ProfileSession {
private String appDataDir;
private String simpleperfDataDir;
private Process simpleperfProcess;
+ private boolean traceOffcpu = false;
/**
* @param appDataDir the same as android.content.Context.getDataDir().
@@ -115,6 +116,11 @@ public class ProfileSession {
if (state != State.NOT_YET_STARTED) {
throw new AssertionError("startRecording: session in wrong state " + state);
}
+ for (String arg : args) {
+ if (arg.equals("--trace-offcpu")) {
+ traceOffcpu = true;
+ }
+ }
String simpleperfPath = findSimpleperf();
checkIfPerfEnabled();
createSimpleperfDataDir();
@@ -129,6 +135,10 @@ public class ProfileSession {
if (state != State.STARTED) {
throw new AssertionError("pauseRecording: session in wrong state " + state);
}
+ if (traceOffcpu) {
+ throw new AssertionError(
+ "--trace-offcpu option doesn't work well with pause/resume recording");
+ }
sendCmd("pause");
state = State.PAUSED;
}