diff options
Diffstat (limited to 'simpleperf/cmd_record_test.cpp')
-rw-r--r-- | simpleperf/cmd_record_test.cpp | 307 |
1 files changed, 64 insertions, 243 deletions
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp index 184f9359..fdc26e60 100644 --- a/simpleperf/cmd_record_test.cpp +++ b/simpleperf/cmd_record_test.cpp @@ -23,7 +23,6 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> -#include <android-base/strings.h> #include <map> #include <memory> @@ -32,7 +31,6 @@ #include "command.h" #include "environment.h" -#include "ETMRecorder.h" #include "event_selection_set.h" #include "get_test_data.h" #include "record.h" @@ -40,7 +38,6 @@ #include "test_util.h" #include "thread_tree.h" -using namespace simpleperf; using namespace PerfFileFormat; static std::unique_ptr<Command> RecordCmd() { @@ -389,11 +386,12 @@ TEST(record_cmd, kernel_symbol) { ASSERT_TRUE(success); } -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); +// 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; + } std::string file_path; uint32_t file_type; uint64_t min_vaddr; @@ -401,24 +399,13 @@ static void ProcessSymbolsInPerfDataFile( 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)) { - for (const auto& symbol : symbols) { - if (callback(symbol, file_type)) { - return; - } + if (!symbols.empty()) { + has_dumped_symbols = true; } } -} - -// 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) { @@ -454,14 +441,24 @@ 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; - auto callback = [&](const Symbol&, uint32_t file_type) { - if (file_type == DSO_KERNEL) { + 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()) { has_kernel_symbols = true; } - return has_kernel_symbols; - }; - ProcessSymbolsInPerfDataFile(tmpfile.path, callback); + } ASSERT_TRUE(has_kernel_symbols); } @@ -566,7 +563,8 @@ TEST(record_cmd, record_meta_info_feature) { ASSERT_TRUE(RunRecordCmd({}, tmpfile.path)); std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path); ASSERT_TRUE(reader); - auto& info_map = reader->GetMetaInfoFeature(); + std::unordered_map<std::string, std::string> info_map; + ASSERT_TRUE(reader->ReadMetaInfoFeature(&info_map)); ASSERT_NE(info_map.find("simpleperf_version"), info_map.end()); ASSERT_NE(info_map.find("timestamp"), info_map.end()); #if defined(__ANDROID__) @@ -604,7 +602,8 @@ TEST(record_cmd, trace_offcpu_option) { ASSERT_TRUE(RunRecordCmd({"--trace-offcpu", "-f", "1000"}, tmpfile.path)); std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path); ASSERT_TRUE(reader); - auto info_map = reader->GetMetaInfoFeature(); + std::unordered_map<std::string, std::string> info_map; + ASSERT_TRUE(reader->ReadMetaInfoFeature(&info_map)); ASSERT_EQ(info_map["trace_offcpu"], "true"); CheckEventType(tmpfile.path, "sched:sched_switch", 1u, 0u); } @@ -623,7 +622,8 @@ TEST(record_cmd, clockid_option) { ASSERT_TRUE(RunRecordCmd({"--clockid", "monotonic"}, tmpfile.path)); std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path); ASSERT_TRUE(reader); - auto info_map = reader->GetMetaInfoFeature(); + std::unordered_map<std::string, std::string> info_map; + ASSERT_TRUE(reader->ReadMetaInfoFeature(&info_map)); ASSERT_EQ(info_map["clockid"], "monotonic"); } } @@ -744,82 +744,47 @@ 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", "--abi", GetABI(), 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: - const char* GetABI() { -#if defined(__i386__) - return "x86"; -#elif defined(__x86_64__) - return "x86_64"; -#elif defined(__aarch64__) - return "arm64-v8a"; -#elif defined(__arm__) - return "armeabi-v7a"; -#else - #error "unrecognized ABI" -#endif - } - - 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(helper.StartApp("am start " + app_name + "/.MainActivity")); - - ASSERT_TRUE(helper.RecordData("--app " + app_name + " -g --duration 3")); + 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); // 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"; - 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)); + 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); } TEST(record_cmd, app_option_for_debuggable_app) { @@ -833,147 +798,3 @@ TEST(record_cmd, app_option_for_profileable_app) { TEST_REQUIRE_APPS(); 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"})); -} - -TEST(record_cmd, cs_etm_event) { - if (!ETMRecorder::GetInstance().CheckEtmSupport()) { - GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; - return; - } - TemporaryFile tmpfile; - ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm"}, tmpfile.path)); - std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path); - ASSERT_TRUE(reader); - - // cs-etm uses sample period instead of sample freq. - ASSERT_EQ(reader->AttrSection().size(), 1u); - const perf_event_attr* attr = reader->AttrSection()[0].attr; - ASSERT_EQ(attr->freq, 0); - ASSERT_EQ(attr->sample_period, 1); - - bool has_auxtrace_info = false; - bool has_auxtrace = false; - bool has_aux = false; - ASSERT_TRUE(reader->ReadDataSection([&](std::unique_ptr<Record> r) { - if (r->type() == PERF_RECORD_AUXTRACE_INFO) { - has_auxtrace_info = true; - } else if (r->type() == PERF_RECORD_AUXTRACE) { - has_auxtrace = true; - } else if (r->type() == PERF_RECORD_AUX) { - has_aux = true; - } - return true; - })); - ASSERT_TRUE(has_auxtrace_info); - ASSERT_TRUE(has_auxtrace); - ASSERT_TRUE(has_aux); -} - -TEST(record_cmd, aux_buffer_size_option) { - if (!ETMRecorder::GetInstance().CheckEtmSupport()) { - GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; - return; - } - ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--aux-buffer-size", "1m"})); - // not page size aligned - ASSERT_FALSE(RunRecordCmd({"-e", "cs-etm", "--aux-buffer-size", "1024"})); - // not power of two - ASSERT_FALSE(RunRecordCmd({"-e", "cs-etm", "--aux-buffer-size", "12k"})); -} - -TEST(record_cmd, include_filter_option) { - TEST_REQUIRE_HW_COUNTER(); - if (!ETMRecorder::GetInstance().CheckEtmSupport()) { - GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device"; - return; - } - FILE* fp = popen("which sleep", "r"); - ASSERT_TRUE(fp != nullptr); - std::string path; - ASSERT_TRUE(android::base::ReadFdToString(fileno(fp), &path)); - pclose(fp); - path = android::base::Trim(path); - std::string sleep_exec_path; - ASSERT_TRUE(android::base::Realpath(path, &sleep_exec_path)); - // --include-filter doesn't apply to cpu-cycles. - ASSERT_FALSE(RunRecordCmd({"--include-filter", sleep_exec_path})); - TemporaryFile record_file; - ASSERT_TRUE( - RunRecordCmd({"-e", "cs-etm", "--include-filter", sleep_exec_path}, record_file.path)); - TemporaryFile inject_file; - ASSERT_TRUE( - CreateCommandInstance("inject")->Run({"-i", record_file.path, "-o", inject_file.path})); - std::string data; - ASSERT_TRUE(android::base::ReadFileToString(inject_file.path, &data)); - // Only instructions in sleep_exec_path are traced. - for (auto& line : android::base::Split(data, "\n")) { - if (android::base::StartsWith(line, "dso ")) { - std::string dso = line.substr(strlen("dso "), sleep_exec_path.size()); - ASSERT_EQ(dso, sleep_exec_path); - } - } -}
\ No newline at end of file |