simpleperf: add --symdir option in report-sample command.
--symdir option is used to provide a directory containing files with
symbols. Mutliple --symdir options can be used to provide more than
one directories. For each symbol directory, simpleperf collects build
id for all elf files under it recursively. Then simpleperf can use
the collected build ids to find files with symbols.
Also fix an error in GetCompleteProcessName().
Bug: 111687223
Test: run simpleperf_unit_test.
Change-Id: Ieac5ebf7451ae85ca15c3eae37bac3c89615580b
diff --git a/simpleperf/cmd_report_sample.cpp b/simpleperf/cmd_report_sample.cpp
index 897f5ef..b2ae8e4 100644
--- a/simpleperf/cmd_report_sample.cpp
+++ b/simpleperf/cmd_report_sample.cpp
@@ -89,6 +89,7 @@
"--remove-unknown-kernel-symbols Remove kernel callchains when kernel symbols\n"
" are not available in perf.data.\n"
"--show-art-frames Show frames of internal methods in the ART Java interpreter.\n"
+"--symdir <dir> Look for files with symbols in a directory recursively.\n"
// clang-format on
),
record_filename_("perf.data"),
@@ -261,6 +262,13 @@
remove_unknown_kernel_symbols_ = true;
} else if (args[i] == "--show-art-frames") {
show_art_frames_ = true;
+ } else if (args[i] == "--symdir") {
+ if (!NextArgumentOrError(args, &i)) {
+ return false;
+ }
+ if (!Dso::AddSymbolDir(args[i])) {
+ return false;
+ }
} else {
ReportUnknownOption(args, i);
return false;
diff --git a/simpleperf/cmd_report_sample_test.cpp b/simpleperf/cmd_report_sample_test.cpp
index f298cfb..a393713 100644
--- a/simpleperf/cmd_report_sample_test.cpp
+++ b/simpleperf/cmd_report_sample_test.cpp
@@ -152,3 +152,12 @@
ASSERT_NE(data.find("mangled_symbol: _ZN7android8hardware14IPCThreadState14talkWithDriverEb"),
std::string::npos);
}
+
+TEST(cmd_report_sample, symdir_option) {
+ std::string data;
+ GetProtobufReport(PERF_DATA_FOR_BUILD_ID_CHECK, &data);
+ ASSERT_EQ(data.find("symbol: main"), std::string::npos);
+ GetProtobufReport(PERF_DATA_FOR_BUILD_ID_CHECK, &data,
+ {"--symdir", GetTestDataDir() + CORRECT_SYMFS_FOR_BUILD_ID_CHECK});
+ ASSERT_NE(data.find("symbol: main"), std::string::npos);
+}
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index a9c86f4..8e641a8 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -55,20 +55,46 @@
}
}
symfs_dir_ = dirname;
- build_id_to_file_map_.clear();
std::string build_id_list_file = symfs_dir_ + "build_id_list";
std::string build_id_list;
if (android::base::ReadFileToString(build_id_list_file, &build_id_list)) {
for (auto& line : android::base::Split(build_id_list, "\n")) {
std::vector<std::string> items = android::base::Split(line, "=");
if (items.size() == 2u) {
- build_id_to_file_map_[items[0]] = items[1];
+ build_id_to_file_map_[items[0]] = symfs_dir_ + items[1];
}
}
}
return true;
}
+bool DebugElfFileFinder::AddSymbolDir(const std::string& symbol_dir) {
+ if (!IsDir(symbol_dir)) {
+ LOG(ERROR) << "Invalid symbol dir " << symbol_dir;
+ return false;
+ }
+ std::string dir = symbol_dir;
+ if (dir.size() > 1 && dir.back() == '/') {
+ dir.pop_back();
+ }
+ CollectBuildIdInDir(dir);
+ return true;
+}
+
+void DebugElfFileFinder::CollectBuildIdInDir(const std::string& dir) {
+ for (const std::string& entry : GetEntriesInDir(dir)) {
+ std::string path = dir + "/" + entry;
+ if (IsDir(path)) {
+ CollectBuildIdInDir(path);
+ } else {
+ BuildId build_id;
+ if (GetBuildIdFromElfFile(path, &build_id) == ElfStatus::NO_ERROR) {
+ build_id_to_file_map_[build_id.ToString()] = path;
+ }
+ }
+ }
+}
+
void DebugElfFileFinder::SetVdsoFile(const std::string& vdso_file, bool is_64bit) {
if (is_64bit) {
vdso_64bit_ = vdso_file;
@@ -85,36 +111,37 @@
} else if (!force_64bit && !vdso_32bit_.empty()) {
return vdso_32bit_;
}
- } else if (!symfs_dir_.empty()) {
+ }
+ // 1. Try build_id_to_file_map.
+ if (!build_id_to_file_map_.empty()) {
if (!build_id.IsEmpty() || GetBuildIdFromDsoPath(dso_path, &build_id)) {
- std::string result;
- auto check_path = [&](const std::string& path) {
- BuildId debug_build_id;
- if (GetBuildIdFromDsoPath(path, &debug_build_id) && debug_build_id == build_id) {
- result = path;
- return true;
- }
- return false;
- };
-
- // 1. Try build_id_to_file_map.
auto it = build_id_to_file_map_.find(build_id.ToString());
if (it != build_id_to_file_map_.end()) {
- if (check_path(symfs_dir_ + it->second)) {
- return result;
- }
- }
- // 2. Try concatenating symfs_dir and dso_path.
- if (check_path(symfs_dir_ + dso_path)) {
- return result;
- }
- // 3. Try concatenating /usr/lib/debug and dso_path.
- // Linux host can store debug shared libraries in /usr/lib/debug.
- if (check_path("/usr/lib/debug" + dso_path)) {
- return result;
+ return it->second;
}
}
}
+ auto check_path = [&](const std::string& path) {
+ BuildId debug_build_id;
+ if (GetBuildIdFromDsoPath(path, &debug_build_id)) {
+ if (!build_id.IsEmpty() || GetBuildIdFromDsoPath(dso_path, &build_id)) {
+ if (build_id == debug_build_id) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ // 2. Try concatenating symfs_dir and dso_path.
+ if (!symfs_dir_.empty() && check_path(symfs_dir_ + dso_path)) {
+ return symfs_dir_ + dso_path;
+ }
+ // 3. Try concatenating /usr/lib/debug and dso_path.
+ // Linux host can store debug shared libraries in /usr/lib/debug.
+ if (check_path("/usr/lib/debug" + dso_path)) {
+ return "/usr/lib/debug" + dso_path;
+ }
return dso_path;
}
} // namespace simpleperf_dso_imp
@@ -184,6 +211,10 @@
return debug_elf_file_finder_.SetSymFsDir(symfs_dir);
}
+bool Dso::AddSymbolDir(const std::string& symbol_dir) {
+ return debug_elf_file_finder_.AddSymbolDir(symbol_dir);
+}
+
void Dso::SetVmlinux(const std::string& vmlinux) { vmlinux_ = vmlinux; }
void Dso::SetBuildIds(
diff --git a/simpleperf/dso.h b/simpleperf/dso.h
index 4bd0779..83f4f65 100644
--- a/simpleperf/dso.h
+++ b/simpleperf/dso.h
@@ -36,11 +36,14 @@
public:
void Reset();
bool SetSymFsDir(const std::string& symfs_dir);
+ bool AddSymbolDir(const std::string& symbol_dir);
void SetVdsoFile(const std::string& vdso_file, bool is_64bit);
std::string FindDebugFile(const std::string& dso_path, bool force_64bit,
BuildId& build_id);
private:
+ void CollectBuildIdInDir(const std::string& dir);
+
std::string vdso_64bit_;
std::string vdso_32bit_;
std::string symfs_dir_;
@@ -110,7 +113,13 @@
public:
static void SetDemangle(bool demangle);
static std::string Demangle(const std::string& name);
+ // SymFsDir is used to provide an alternative root directory looking for files with symbols.
+ // For example, if we are searching symbols for /system/lib/libc.so and SymFsDir is /data/symbols,
+ // then we will also search file /data/symbols/system/lib/libc.so.
static bool SetSymFsDir(const std::string& symfs_dir);
+ // SymbolDir is used to add a directory containing files with symbols. Each file under it will
+ // be searched recursively to build a build_id_map.
+ static bool AddSymbolDir(const std::string& symbol_dir);
static void SetVmlinux(const std::string& vmlinux);
static void SetKallsyms(std::string kallsyms) {
if (!kallsyms.empty()) {
diff --git a/simpleperf/dso_test.cpp b/simpleperf/dso_test.cpp
index a1d005a..ab220a1 100644
--- a/simpleperf/dso_test.cpp
+++ b/simpleperf/dso_test.cpp
@@ -67,6 +67,15 @@
ASSERT_EQ(finder.FindDebugFile("[vdso]", true, build_id), fake_vdso64);
}
+TEST(DebugElfFileFinder, add_symbol_dir) {
+ DebugElfFileFinder finder;
+ ASSERT_FALSE(finder.AddSymbolDir(GetTestDataDir() + "dir_not_exist"));
+ ASSERT_EQ(finder.FindDebugFile("elf", false, CHECK_ELF_FILE_BUILD_ID), "elf");
+ ASSERT_TRUE(finder.AddSymbolDir(GetTestDataDir() + CORRECT_SYMFS_FOR_BUILD_ID_CHECK));
+ ASSERT_EQ(finder.FindDebugFile("elf", false, CHECK_ELF_FILE_BUILD_ID),
+ GetTestDataDir() + CORRECT_SYMFS_FOR_BUILD_ID_CHECK + "/elf_for_build_id_check");
+}
+
TEST(dso, dex_file_dso) {
#if defined(__linux__)
for (DsoType dso_type : {DSO_DEX_FILE, DSO_ELF_FILE}) {
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index b90606b..4962e7d 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -761,7 +761,8 @@
s.clear();
}
for (size_t i = 0; i < s.size(); ++i) {
- if (isspace(s[i])) {
+ // /proc/pid/cmdline uses 0 to separate arguments.
+ if (isspace(s[i]) || s[i] == 0) {
s.resize(i);
break;
}
diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h
index 114b645..67bf0ea 100644
--- a/simpleperf/get_test_data.h
+++ b/simpleperf/get_test_data.h
@@ -97,6 +97,8 @@
static const std::string SYMFS_FOR_NO_SYMBOL_TABLE_WARNING = "data/symfs_for_no_symbol_table_warning";
static const std::string SYMFS_FOR_READ_ELF_FILE_WARNING = "data/symfs_for_read_elf_file_warning";
+static BuildId CHECK_ELF_FILE_BUILD_ID("91b1c10fdd9fe2221dfec525497637f2229bfdbb");
+
// generated_by_linux_perf.data is generated by `perf record -F 1 -a -g -- sleep 0.1`.
static const std::string PERF_DATA_GENERATED_BY_LINUX_PERF = "generated_by_linux_perf.data";