summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2019-09-30 16:46:26 -0700
committerYabin Cui <yabinc@google.com>2019-10-01 11:36:22 -0700
commit56233ccc31b0877d9eecb648951d3376ea3ab630 (patch)
tree20c97c0501d82955d0aea5d29bb74b7ff6ca7859
parent1720053bc5f952270e274e394423a720d4ef0727 (diff)
downloadextras-56233ccc31b0877d9eecb648951d3376ea3ab630.tar.gz
simpleperf: add tests recording real apps.
This is to test failures more likely to be reproduced when recording real apps. Bug: 141882072 Test: run simpleperf_unit_test on both arm64 and x86. Change-Id: I20da128b6721c32c0205341a16f63414510753c9
-rw-r--r--simpleperf/cmd_record_test.cpp197
-rw-r--r--simpleperf/gtest_main.cpp6
-rw-r--r--simpleperf/testdata/DisplayBitmaps.apkbin0 -> 2166441 bytes
-rw-r--r--simpleperf/testdata/DisplayBitmapsTest.apkbin0 -> 2886160 bytes
-rw-r--r--simpleperf/testdata/EndlessTunnel.apkbin0 -> 615556 bytes
5 files changed, 145 insertions, 58 deletions
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index eade5742..82f494f8 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -389,12 +389,11 @@ TEST(record_cmd, kernel_symbol) {
ASSERT_TRUE(success);
}
-// Check if dumped symbols in perf.data matches our expectation.
-static bool CheckDumpedSymbols(const std::string& path, bool allow_dumped_symbols) {
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(path);
- if (!reader) {
- return false;
- }
+static void ProcessSymbolsInPerfDataFile(
+ const std::string& perf_data_file,
+ const std::function<bool(const Symbol&, uint32_t)>& callback) {
+ auto reader = RecordFileReader::CreateInstance(perf_data_file);
+ ASSERT_TRUE(reader);
std::string file_path;
uint32_t file_type;
uint64_t min_vaddr;
@@ -402,13 +401,24 @@ static bool CheckDumpedSymbols(const std::string& path, bool allow_dumped_symbol
std::vector<Symbol> symbols;
std::vector<uint64_t> dex_file_offsets;
size_t read_pos = 0;
- bool has_dumped_symbols = false;
while (reader->ReadFileFeature(read_pos, &file_path, &file_type, &min_vaddr,
&file_offset_of_min_vaddr, &symbols, &dex_file_offsets)) {
- if (!symbols.empty()) {
- has_dumped_symbols = true;
+ for (const auto& symbol : symbols) {
+ if (callback(symbol, file_type)) {
+ return;
+ }
}
}
+}
+
+// Check if dumped symbols in perf.data matches our expectation.
+static bool CheckDumpedSymbols(const std::string& path, bool allow_dumped_symbols) {
+ bool has_dumped_symbols = false;
+ auto callback = [&](const Symbol&, uint32_t) {
+ has_dumped_symbols = true;
+ return true;
+ };
+ ProcessSymbolsInPerfDataFile(path, callback);
// It is possible that there are no samples hitting functions having symbols.
// So "allow_dumped_symbols = true" doesn't guarantee "has_dumped_symbols = true".
if (!allow_dumped_symbols && has_dumped_symbols) {
@@ -444,24 +454,14 @@ TEST(record_cmd, dump_kernel_symbols) {
}
TemporaryFile tmpfile;
ASSERT_TRUE(RunRecordCmd({"-a", "-o", tmpfile.path, "sleep", "1"}));
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
- ASSERT_TRUE(reader != nullptr);
- std::map<int, SectionDesc> section_map = reader->FeatureSectionDescriptors();
- ASSERT_NE(section_map.find(FEAT_FILE), section_map.end());
- std::string file_path;
- uint32_t file_type;
- uint64_t min_vaddr;
- uint64_t file_offset_of_min_vaddr;
- std::vector<Symbol> symbols;
- std::vector<uint64_t> dex_file_offsets;
- size_t read_pos = 0;
bool has_kernel_symbols = false;
- while (reader->ReadFileFeature(read_pos, &file_path, &file_type, &min_vaddr,
- &file_offset_of_min_vaddr, &symbols, &dex_file_offsets)) {
- if (file_type == DSO_KERNEL && !symbols.empty()) {
+ auto callback = [&](const Symbol&, uint32_t file_type) {
+ if (file_type == DSO_KERNEL) {
has_kernel_symbols = true;
}
- }
+ return has_kernel_symbols;
+ };
+ ProcessSymbolsInPerfDataFile(tmpfile.path, callback);
ASSERT_TRUE(has_kernel_symbols);
}
@@ -744,47 +744,68 @@ TEST(record_cmd, cpu_percent_option) {
ASSERT_FALSE(RunRecordCmd({"--cpu-percent", "101"}));
}
+class RecordingAppHelper {
+ public:
+ bool InstallApk(const std::string& apk_path, const std::string& package_name) {
+ if (Workload::RunCmd({"pm", "install", "-t", apk_path})) {
+ installed_packages_.emplace_back(package_name);
+ return true;
+ }
+ return false;
+ }
+
+ bool StartApp(const std::string& start_cmd) {
+ app_start_proc_ = Workload::CreateWorkload(android::base::Split(start_cmd, " "));
+ return app_start_proc_ && app_start_proc_->Start();
+ }
+
+ bool RecordData(const std::string& record_cmd) {
+ std::vector<std::string> args = android::base::Split(record_cmd, " ");
+ args.emplace_back("-o");
+ args.emplace_back(perf_data_file_.path);
+ return RecordCmd()->Run(args);
+ }
+
+ bool CheckData(const std::function<bool(const char*)>& process_symbol) {
+ bool success = false;
+ auto callback = [&](const Symbol& symbol, uint32_t) {
+ if (process_symbol(symbol.DemangledName())) {
+ success = true;
+ }
+ return success;
+ };
+ ProcessSymbolsInPerfDataFile(perf_data_file_.path, callback);
+ return success;
+ }
+
+ ~RecordingAppHelper() {
+ for (auto& package : installed_packages_) {
+ Workload::RunCmd({"pm", "uninstall", package});
+ }
+ }
+
+ private:
+ std::vector<std::string> installed_packages_;
+ std::unique_ptr<Workload> app_start_proc_;
+ TemporaryFile perf_data_file_;
+};
+
static void TestRecordingApps(const std::string& app_name) {
+ RecordingAppHelper helper;
// Bring the app to foreground to avoid no samples.
- ASSERT_TRUE(Workload::RunCmd({"am", "start", app_name + "/.MainActivity"}));
- TemporaryFile tmpfile;
- ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "--app", app_name, "-g", "--duration", "3"}));
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
- ASSERT_TRUE(reader);
- // Check if having samples.
- bool has_sample = false;
- ASSERT_TRUE(reader->ReadDataSection([&](std::unique_ptr<Record> r) {
- if (r->type() == PERF_RECORD_SAMPLE) {
- has_sample = true;
- }
- return true;
- }));
- ASSERT_TRUE(has_sample);
+ ASSERT_TRUE(helper.StartApp("am start " + app_name + "/.MainActivity"));
+
+ ASSERT_TRUE(helper.RecordData("--app " + app_name + " -g --duration 3"));
// Check if we can profile Java code by looking for a Java method name in dumped symbols, which
// is app_name + ".MainActivity$1.run".
const std::string expected_class_name = app_name + ".MainActivity";
const std::string expected_method_name = "run";
- std::string file_path;
- uint32_t file_type;
- uint64_t min_vaddr;
- uint64_t file_offset_of_min_vaddr;
- std::vector<Symbol> symbols;
- std::vector<uint64_t> dex_file_offsets;
- size_t read_pos = 0;
- bool has_java_symbol = false;
- ASSERT_TRUE(reader->HasFeature(FEAT_FILE));
- while (reader->ReadFileFeature(read_pos, &file_path, &file_type, &min_vaddr,
- &file_offset_of_min_vaddr, &symbols, &dex_file_offsets)) {
- for (const auto& symbol : symbols) {
- const char* name = symbol.DemangledName();
- if (strstr(name, expected_class_name.c_str()) != nullptr &&
- strstr(name, expected_method_name.c_str()) != nullptr) {
- has_java_symbol = true;
- }
- }
- }
- ASSERT_TRUE(has_java_symbol);
+ auto process_symbol = [&](const char* name) {
+ return strstr(name, expected_class_name.c_str()) != nullptr &&
+ strstr(name, expected_method_name.c_str()) != nullptr;
+ };
+ ASSERT_TRUE(helper.CheckData(process_symbol));
}
TEST(record_cmd, app_option_for_debuggable_app) {
@@ -799,6 +820,66 @@ TEST(record_cmd, app_option_for_profileable_app) {
TestRecordingApps("com.android.simpleperf.profileable");
}
+TEST(record_cmd, record_java_app) {
+ RecordingAppHelper helper;
+ // 1. Install apk.
+ ASSERT_TRUE(helper.InstallApk(GetTestData("DisplayBitmaps.apk"),
+ "com.example.android.displayingbitmaps"));
+ ASSERT_TRUE(helper.InstallApk(GetTestData("DisplayBitmapsTest.apk"),
+ "com.example.android.displayingbitmaps.test"));
+
+ // 2. Start the app.
+ ASSERT_TRUE(
+ helper.StartApp("am instrument -w -r -e debug false -e class "
+ "com.example.android.displayingbitmaps.tests.GridViewTest "
+ "com.example.android.displayingbitmaps.test/"
+ "androidx.test.runner.AndroidJUnitRunner"));
+
+ // 3. Record perf.data.
+ ASSERT_TRUE(helper.RecordData(
+ "-e cpu-clock --app com.example.android.displayingbitmaps -g --duration 10"));
+
+ // 4. Check perf.data.
+ auto process_symbol = [&](const char* name) {
+#if !defined(IN_CTS_TEST)
+ const char* expected_name_with_keyguard = "androidx.test.runner"; // when screen is locked
+ if (strstr(name, expected_name_with_keyguard) != nullptr) {
+ return true;
+ }
+#endif
+ const char* expected_name = "androidx.test.espresso"; // when screen stays awake
+ return strstr(name, expected_name) != nullptr;
+ };
+ ASSERT_TRUE(helper.CheckData(process_symbol));
+}
+
+TEST(record_cmd, record_native_app) {
+ RecordingAppHelper helper;
+ // 1. Install apk.
+ ASSERT_TRUE(helper.InstallApk(GetTestData("EndlessTunnel.apk"), "com.google.sample.tunnel"));
+
+ // 2. Start the app.
+ ASSERT_TRUE(
+ helper.StartApp("am start -n com.google.sample.tunnel/android.app.NativeActivity -a "
+ "android.intent.action.MAIN -c android.intent.category.LAUNCHER"));
+
+ // 3. Record perf.data.
+ ASSERT_TRUE(helper.RecordData("-e cpu-clock --app com.google.sample.tunnel -g --duration 10"));
+
+ // 4. Check perf.data.
+ auto process_symbol = [&](const char* name) {
+#if !defined(IN_CTS_TEST)
+ const char* expected_name_with_keyguard = "NativeActivity"; // when screen is locked
+ if (strstr(name, expected_name_with_keyguard) != nullptr) {
+ return true;
+ }
+#endif
+ const char* expected_name = "PlayScene::DoFrame"; // when screen is awake
+ return strstr(name, expected_name) != nullptr;
+ };
+ ASSERT_TRUE(helper.CheckData(process_symbol));
+}
+
TEST(record_cmd, no_cut_samples_option) {
TEST_REQUIRE_HW_COUNTER();
ASSERT_TRUE(RunRecordCmd({"--no-cut-samples"}));
diff --git a/simpleperf/gtest_main.cpp b/simpleperf/gtest_main.cpp
index 6265d6a7..a6062281 100644
--- a/simpleperf/gtest_main.cpp
+++ b/simpleperf/gtest_main.cpp
@@ -68,6 +68,12 @@ class ScopedEnablingPerf {
#endif // defined(__ANDROID__)
int main(int argc, char** argv) {
+ // To test profiling apps, simpleperf_unit_test needs to copy itself to the app's directory,
+ // and run the binary as simpleperf executable.
+ if (android::base::Basename(argv[0]) == "simpleperf") {
+ return RunSimpleperfCmd(argc, argv) ? 0 : 1;
+ }
+
android::base::InitLogging(argv, android::base::StderrLogger);
android::base::LogSeverity log_severity = android::base::WARNING;
testdata_dir = std::string(dirname(argv[0])) + "/testdata";
diff --git a/simpleperf/testdata/DisplayBitmaps.apk b/simpleperf/testdata/DisplayBitmaps.apk
new file mode 100644
index 00000000..dada9e9e
--- /dev/null
+++ b/simpleperf/testdata/DisplayBitmaps.apk
Binary files differ
diff --git a/simpleperf/testdata/DisplayBitmapsTest.apk b/simpleperf/testdata/DisplayBitmapsTest.apk
new file mode 100644
index 00000000..f8cdc7d3
--- /dev/null
+++ b/simpleperf/testdata/DisplayBitmapsTest.apk
Binary files differ
diff --git a/simpleperf/testdata/EndlessTunnel.apk b/simpleperf/testdata/EndlessTunnel.apk
new file mode 100644
index 00000000..09afd7fb
--- /dev/null
+++ b/simpleperf/testdata/EndlessTunnel.apk
Binary files differ