diff options
-rw-r--r-- | simpleperf/cmd_report_test.cpp | 27 | ||||
-rw-r--r-- | simpleperf/get_test_data.h | 38 | ||||
-rw-r--r-- | simpleperf/gtest_main.cpp | 15 | ||||
-rw-r--r-- | simpleperf/main.cpp | 16 | ||||
-rw-r--r-- | simpleperf/read_apk.cpp | 4 | ||||
-rw-r--r-- | simpleperf/read_apk_test.cpp | 7 | ||||
-rw-r--r-- | simpleperf/testdata/data/app/com.example.hellojni-1/base.apk | bin | 89200 -> 6599967 bytes | |||
-rw-r--r-- | simpleperf/testdata/has_embedded_native_libs_apk_perf.data | bin | 46988 -> 487976 bytes | |||
-rw-r--r-- | simpleperf/utils.cpp | 17 | ||||
-rw-r--r-- | simpleperf/utils.h | 4 |
10 files changed, 96 insertions, 32 deletions
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp index bf1ff469..a4bd6f39 100644 --- a/simpleperf/cmd_report_test.cpp +++ b/simpleperf/cmd_report_test.cpp @@ -26,6 +26,7 @@ #include "command.h" #include "event_selection_set.h" #include "get_test_data.h" +#include "perf_regs.h" #include "read_apk.h" #include "test_util.h" @@ -259,6 +260,13 @@ TEST_F(ReportCommandTest, use_branch_address) { hit_set.end()); } +TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) { + Report(NATIVELIB_IN_APK_PERF_DATA); + ASSERT_TRUE(success); + ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), std::string::npos); + ASSERT_NE(content.find("Func2"), std::string::npos); +} + #if defined(__linux__) static std::unique_ptr<Command> RecordCmd() { @@ -277,11 +285,18 @@ TEST_F(ReportCommandTest, dwarf_callgraph) { } } +TEST_F(ReportCommandTest, report_dwarf_callgraph_of_nativelib_in_apk) { + // NATIVELIB_IN_APK_PERF_DATA is recorded on arm64, so can only report callgraph on arm64. + if (GetBuildArch() == ARCH_ARM64) { + Report(NATIVELIB_IN_APK_PERF_DATA, {"-g"}); + ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), std::string::npos); + ASSERT_NE(content.find("Func2"), std::string::npos); + ASSERT_NE(content.find("Func1"), std::string::npos); + ASSERT_NE(content.find("GlobalFunc"), std::string::npos); + } else { + GTEST_LOG_(INFO) << "This test does nothing as it is only run on arm64 devices"; + } +} + #endif -TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) { - Report(NATIVELIB_IN_APK_PERF_DATA); - ASSERT_TRUE(success); - ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), std::string::npos); - ASSERT_NE(content.find("GlobalFunc"), std::string::npos); -} diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h index e485e7b9..c380a7de 100644 --- a/simpleperf/get_test_data.h +++ b/simpleperf/get_test_data.h @@ -26,23 +26,45 @@ const std::string& GetTestDataDir(); bool IsRoot(); +// The source code of elf and elf_with_mini_debug_info is testdata/elf_file_source.cpp. +static const std::string ELF_FILE = "elf"; +static const std::string ELF_FILE_WITH_MINI_DEBUG_INFO = "elf_with_mini_debug_info"; +// perf.data is generated by sampling on three processes running different +// executables: elf, t1, t2 (all generated by elf_file_source.cpp, but with different +// executable name). static const std::string PERF_DATA = "perf.data"; +// perf_g_fp.data is generated by sampling on one process running elf using --call-graph fp option. static const std::string CALLGRAPH_FP_PERF_DATA = "perf_g_fp.data"; +// perf_b.data is generated by sampling on one process running elf using -b option. static const std::string BRANCH_PERF_DATA = "perf_b.data"; +// perf_with_mini_debug_info.data is generated by sampling on one process running +// elf_with_mini_debug_info. static const std::string PERF_DATA_WITH_MINI_DEBUG_INFO = "perf_with_mini_debug_info.data"; -static const std::string ELF_FILE = "elf"; -static const std::string ELF_FILE_WITH_MINI_DEBUG_INFO = "elf_with_mini_debug_info"; +static BuildId elf_file_build_id("0b12a384a9f4a3f3659b7171ca615dbec3a81f71"); + +// To generate apk supporting execution on shared libraries in apk: +// 1. Add android:extractNativeLibs=false in AndroidManifest.xml. +// 2. Use `zip -0` to store native libraries in apk without compression. +// 3. Use `zipalign -p 4096` to make native libraries in apk start at page boundaries. +// +// The logical in libhello-jni.so is as below: +// volatile int GlobalVar; +// +// while (true) { +// GlobalFunc() -> Func1() -> Func2() +// } +// And most time is spent in Func2(). static const std::string APK_FILE = "data/app/com.example.hellojni-1/base.apk"; static const std::string NATIVELIB_IN_APK = "lib/arm64-v8a/libhello-jni.so"; +// has_embedded_native_libs_apk_perf.data is generated by sampling on one process running +// APK_FILE using -g --no-unwind option. static const std::string NATIVELIB_IN_APK_PERF_DATA = "has_embedded_native_libs_apk_perf.data"; +// The offset and size info are extracted from the generated apk file to run read_apk tests. +constexpr size_t NATIVELIB_OFFSET_IN_APK = 0x639000; +constexpr size_t NATIVELIB_SIZE_IN_APK = 0x1678; -constexpr size_t NATIVELIB_OFFSET_IN_APK = 0x8000; -constexpr size_t NATIVELIB_SIZE_IN_APK = 0x15d8; - -static BuildId elf_file_build_id("0b12a384a9f4a3f3659b7171ca615dbec3a81f71"); - -static BuildId native_lib_build_id("b46f51cb9c4b71fb08a2fdbefc2c187894f14008"); +static BuildId native_lib_build_id("8ed5755a7fdc07586ca228b8ee21621bce2c7a97"); #endif // SIMPLE_PERF_GET_TEST_DATA_H_ diff --git a/simpleperf/gtest_main.cpp b/simpleperf/gtest_main.cpp index 099bd81b..1d0a5c9e 100644 --- a/simpleperf/gtest_main.cpp +++ b/simpleperf/gtest_main.cpp @@ -59,6 +59,7 @@ static bool ExtractTestDataFromElfSection() { LOG(ERROR) << "failed to start iterating zip entries"; return false; } + std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration); ZipEntry entry; ZipString name; while (Next(cookie, &entry, &name) == 0) { @@ -87,7 +88,6 @@ static bool ExtractTestDataFromElfSection() { return false; } } - EndIteration(cookie); return true; } #endif // defined(__ANDROID__) @@ -95,13 +95,26 @@ static bool ExtractTestDataFromElfSection() { int main(int argc, char** argv) { InitLogging(argv, android::base::StderrLogger); testing::InitGoogleTest(&argc, argv); + android::base::LogSeverity log_severity = android::base::WARNING; for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) { testdata_dir = argv[i + 1]; i++; + } else if (strcmp(argv[i], "--log") == 0) { + if (i + 1 < argc) { + ++i; + if (!GetLogSeverity(argv[i], &log_severity)) { + LOG(ERROR) << "Unknown log severity: " << argv[i]; + return 1; + } + } else { + LOG(ERROR) << "Missing argument for --log option.\n"; + return 1; + } } } + android::base::ScopedLogSeverity severity(log_severity); #if defined(__ANDROID__) std::unique_ptr<TemporaryDir> tmp_dir; diff --git a/simpleperf/main.cpp b/simpleperf/main.cpp index 75313a54..0a73c2db 100644 --- a/simpleperf/main.cpp +++ b/simpleperf/main.cpp @@ -15,21 +15,14 @@ */ #include <string.h> -#include <map> + #include <string> #include <vector> #include <android-base/logging.h> #include "command.h" - -static std::map<std::string, android::base::LogSeverity> log_severity_map = { - {"verbose", android::base::VERBOSE}, - {"debug", android::base::DEBUG}, - {"warning", android::base::WARNING}, - {"error", android::base::ERROR}, - {"fatal", android::base::FATAL}, -}; +#include "utils.h" int main(int argc, char** argv) { InitLogging(argv, android::base::StderrLogger); @@ -42,10 +35,7 @@ int main(int argc, char** argv) { } else if (strcmp(argv[i], "--log") == 0) { if (i + 1 < argc) { ++i; - auto it = log_severity_map.find(argv[i]); - if (it != log_severity_map.end()) { - log_severity = it->second; - } else { + if (!GetLogSeverity(argv[i], &log_severity)) { LOG(ERROR) << "Unknown log severity: " << argv[i]; return 1; } diff --git a/simpleperf/read_apk.cpp b/simpleperf/read_apk.cpp index 18c76c23..af72cdf8 100644 --- a/simpleperf/read_apk.cpp +++ b/simpleperf/read_apk.cpp @@ -24,10 +24,11 @@ #include <sys/types.h> #include <unistd.h> +#include <memory> + #include <android-base/file.h> #include <android-base/logging.h> #include <ziparchive/zip_archive.h> - #include "read_elf.h" #include "utils.h" @@ -52,6 +53,7 @@ std::unique_ptr<EmbeddedElf> ApkInspector::FindElfInApkByOffsetWithoutCache(cons if (!IsValidApkPath(apk_path)) { return nullptr; } + FileHelper fhelper = FileHelper::OpenReadOnly(apk_path); if (!fhelper) { return nullptr; diff --git a/simpleperf/read_apk_test.cpp b/simpleperf/read_apk_test.cpp index e983a254..d7b30c52 100644 --- a/simpleperf/read_apk_test.cpp +++ b/simpleperf/read_apk_test.cpp @@ -31,9 +31,12 @@ TEST(read_apk, FindElfInApkByOffset) { ApkInspector inspector; ASSERT_TRUE(inspector.FindElfInApkByOffset("/dev/null", 0) == nullptr); ASSERT_TRUE(inspector.FindElfInApkByOffset(GetTestData(APK_FILE), 0) == nullptr); - EmbeddedElf* ee = inspector.FindElfInApkByOffset(GetTestData(APK_FILE), 0x9000); + // Test if we can read the EmbeddedElf using an offset inside its [offset, offset+size] range + // in the apk file. + EmbeddedElf* ee = inspector.FindElfInApkByOffset(GetTestData(APK_FILE), + NATIVELIB_OFFSET_IN_APK + NATIVELIB_SIZE_IN_APK / 2); ASSERT_TRUE(ee != nullptr); - ASSERT_EQ(ee->entry_name(), NATIVELIB_IN_APK); + ASSERT_EQ(NATIVELIB_IN_APK, ee->entry_name()); ASSERT_EQ(NATIVELIB_OFFSET_IN_APK, ee->entry_offset()); ASSERT_EQ(NATIVELIB_SIZE_IN_APK, ee->entry_size()); } diff --git a/simpleperf/testdata/data/app/com.example.hellojni-1/base.apk b/simpleperf/testdata/data/app/com.example.hellojni-1/base.apk Binary files differindex c757e9e8..95ea93a3 100644 --- a/simpleperf/testdata/data/app/com.example.hellojni-1/base.apk +++ b/simpleperf/testdata/data/app/com.example.hellojni-1/base.apk diff --git a/simpleperf/testdata/has_embedded_native_libs_apk_perf.data b/simpleperf/testdata/has_embedded_native_libs_apk_perf.data Binary files differindex f85c9d3f..fafbbbc6 100644 --- a/simpleperf/testdata/has_embedded_native_libs_apk_perf.data +++ b/simpleperf/testdata/has_embedded_native_libs_apk_perf.data diff --git a/simpleperf/utils.cpp b/simpleperf/utils.cpp index f2418a65..dd09fbd3 100644 --- a/simpleperf/utils.cpp +++ b/simpleperf/utils.cpp @@ -25,6 +25,7 @@ #include <unistd.h> #include <algorithm> +#include <map> #include <string> #include <android-base/file.h> @@ -230,3 +231,19 @@ bool XzDecompress(const std::string& compressed_data, std::string* decompressed_ *decompressed_data = std::move(dst); return true; } + +bool GetLogSeverity(const std::string& name, android::base::LogSeverity* severity) { + static std::map<std::string, android::base::LogSeverity> log_severity_map = { + {"verbose", android::base::VERBOSE}, + {"debug", android::base::DEBUG}, + {"warning", android::base::WARNING}, + {"error", android::base::ERROR}, + {"fatal", android::base::FATAL}, + }; + auto it = log_severity_map.find(name); + if (it != log_severity_map.end()) { + *severity = it->second; + return true; + } + return false; +} diff --git a/simpleperf/utils.h b/simpleperf/utils.h index c5c43664..04245012 100644 --- a/simpleperf/utils.h +++ b/simpleperf/utils.h @@ -22,6 +22,7 @@ #include <string> #include <vector> +#include <android-base/logging.h> #include <android-base/macros.h> #include <ziparchive/zip_archive.h> @@ -82,7 +83,6 @@ class FileHelper { DISALLOW_COPY_AND_ASSIGN(FileHelper); }; - class ArchiveHelper { public: ArchiveHelper(int fd, const std::string& debug_filename); @@ -121,4 +121,6 @@ bool MkdirWithParents(const std::string& path); bool XzDecompress(const std::string& compressed_data, std::string* decompressed_data); +bool GetLogSeverity(const std::string& name, android::base::LogSeverity* severity); + #endif // SIMPLE_PERF_UTILS_H_ |