simpleperf: inject: Add option to search binaries with filename

When --symdir is given, binaries are searched by build id.
This patch adds --allow-mismatched-build-id in the inject
cmd. When used, it searches binaries by filename and allows
missing or mismatched build ids. It also generates a message
to show the found binary. So users can check if that is correct.

Bug: 361638495
Test: run simpleperf_unit_test
Test: run simpleperf inject --allow-mismatched-build-id
Change-Id: I0c34eeca2b6cd6ec860d7ea97935b97933969b97
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index 1380ada..15a269b 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -54,6 +54,7 @@
 }
 
 void DebugElfFileFinder::Reset() {
+  allow_mismatched_build_id_ = false;
   vdso_64bit_.clear();
   vdso_32bit_.clear();
   symfs_dir_.clear();
@@ -113,8 +114,8 @@
   }
 }
 
-static bool CheckDebugFilePath(const std::string& path, BuildId& build_id,
-                               bool report_build_id_mismatch) {
+bool DebugElfFileFinder::CheckDebugFilePath(const std::string& path, BuildId& build_id,
+                                            bool report_build_id_mismatch) {
   ElfStatus status;
   auto elf = ElfFile::Open(path, &status);
   if (!elf) {
@@ -126,6 +127,10 @@
     return false;
   }
 
+  if (allow_mismatched_build_id_) {
+    return true;
+  }
+
   // Native libraries in apks and kernel modules may not have build ids.
   // So build_id and debug_build_id can either be empty, or have the same value.
   bool match = build_id == debug_build_id;
@@ -152,12 +157,18 @@
 
   // 1. Try build_id_to_file_map.
   if (!build_id_to_file_map_.empty()) {
-    if (!build_id.IsEmpty() || GetBuildIdFromDsoPath(dso_path, &build_id)) {
+    if (!build_id.IsEmpty()) {
       auto it = build_id_to_file_map_.find(build_id.ToString());
       if (it != build_id_to_file_map_.end() && CheckDebugFilePath(it->second, build_id, false)) {
         return it->second;
       }
     }
+    if (allow_mismatched_build_id_) {
+      std::optional<std::string> s = SearchFileMapByPath(dso_path);
+      if (s.has_value()) {
+        return s.value();
+      }
+    }
   }
   if (!symfs_dir_.empty()) {
     // 2. Try concatenating symfs_dir and dso_path.
@@ -205,6 +216,39 @@
   std::replace(elf_path.begin(), elf_path.end(), '/', OS_PATH_SEPARATOR);
   return add_symfs_prefix(elf_path);
 }
+
+std::optional<std::string> DebugElfFileFinder::SearchFileMapByPath(const std::string& path) {
+  std::string filename;
+  if (size_t pos = path.rfind('/'); pos != std::string::npos) {
+    filename = path.substr(pos + 1);
+  } else {
+    filename = path;
+  }
+  std::string best_elf_file;
+  size_t best_match_length = 0;
+  for (const auto& p : build_id_to_file_map_) {
+    const std::string& elf_file = p.second;
+    if (EndsWith(elf_file, filename)) {
+      size_t i = elf_file.size();
+      size_t j = path.size();
+      while (i > 0 && j > 0 && elf_file[i - 1] == path[j - 1]) {
+        i--;
+        j--;
+      }
+      size_t match_length = elf_file.size() - i;
+      if (match_length > best_match_length) {
+        best_elf_file = elf_file;
+        best_match_length = match_length;
+      }
+    }
+  }
+  if (!best_elf_file.empty()) {
+    LOG(INFO) << "Found " << best_elf_file << " for " << path << " by filename";
+    return best_elf_file;
+  }
+  return std::nullopt;
+}
+
 }  // namespace simpleperf_dso_impl
 
 static OneTimeFreeAllocator symbol_name_allocator;
@@ -320,6 +364,10 @@
   return debug_elf_file_finder_.AddSymbolDir(symbol_dir);
 }
 
+void Dso::AllowMismatchedBuildId() {
+  return debug_elf_file_finder_.AllowMismatchedBuildId();
+}
+
 void Dso::SetVmlinux(const std::string& vmlinux) {
   vmlinux_ = vmlinux;
 }