[Lldb-commits] [lldb] c012408 - Implement better path matching in FileSpecList::FindCompatibleIndex(...).

Greg Clayton via lldb-commits lldb-commits at lists.llvm.org
Mon Aug 8 15:20:49 PDT 2022


Author: Greg Clayton
Date: 2022-08-08T15:20:38-07:00
New Revision: c0124084537bad33180d2e675475baf1a4b79afe

URL: https://github.com/llvm/llvm-project/commit/c0124084537bad33180d2e675475baf1a4b79afe
DIFF: https://github.com/llvm/llvm-project/commit/c0124084537bad33180d2e675475baf1a4b79afe.diff

LOG: Implement better path matching in FileSpecList::FindCompatibleIndex(...).

Currently a FileSpecList::FindFileIndex(...) will only match the specified FileSpec if:
- it has filename and directory and both match exactly
- if has a filename only and any filename in the list matches

Because of this, we modify our breakpoint resolving so it can handle relative paths by doing some extra code that removes the directory from the FileSpec when searching if the path is relative.

This patch is intended to fix breakpoints so they work as users expect them to by adding the following features:
- allow matches to relative paths in the file list to match as long as the relative path is at the end of the specified path at valid directory delimiters
- allow matches to paths to match if the specified path is relative and shorter than the file paths in the list

This allows us to remove the extra logic from BreakpointResolverFileLine.cpp that added support for setting breakpoints with relative paths.

This means we can still set breakpoints with relative paths when the debug info contains full paths. We add the ability to set breakpoints with full paths when the debug info contains relative paths.

Debug info contains "./a/b/c/main.cpp", the following will set breakpoints successfully:
- /build/a/b/c/main.cpp
- a/b/c/main.cpp
- b/c/main.cpp
- c/main.cpp
- main.cpp
- ./c/main.cpp
- ./a/b/c/main.cpp
- ./b/c/main.cpp
- ./main.cpp

This also ensures that we won't match partial directory names, if a relative path is in the list or is used for the match, things must match at the directory level.

The breakpoint resolving code will now use the new FileSpecList::FindCompatibleIndex(...) function to allow this fuzzy matching to work for breakpoints.

Differential Revision: https://reviews.llvm.org/D130401

Added: 
    lldb/test/API/functionalities/breakpoint/breakpoint_command/relative.yaml
    lldb/unittests/Core/FileSpecListTest.cpp

Modified: 
    lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h
    lldb/include/lldb/Core/FileSpecList.h
    lldb/source/Breakpoint/BreakpointResolverFileLine.cpp
    lldb/source/Core/FileSpecList.cpp
    lldb/source/Symbol/CompileUnit.cpp
    lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
    lldb/unittests/Core/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h b/lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h
index bd8d0394d4d2d..e73632aad6a85 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h
@@ -56,7 +56,7 @@ class BreakpointResolverFileLine : public BreakpointResolver {
   CopyForBreakpoint(lldb::BreakpointSP &breakpoint) override;
 
 protected:
-  void FilterContexts(SymbolContextList &sc_list, bool is_relative);
+  void FilterContexts(SymbolContextList &sc_list);
 
   friend class Breakpoint;
   SourceLocationSpec m_location_spec;

diff  --git a/lldb/include/lldb/Core/FileSpecList.h b/lldb/include/lldb/Core/FileSpecList.h
index 117c6b691498c..a5720d85e7cde 100644
--- a/lldb/include/lldb/Core/FileSpecList.h
+++ b/lldb/include/lldb/Core/FileSpecList.h
@@ -116,6 +116,32 @@ class FileSpecList {
   ///     else UINT32_MAX is returned.
   size_t FindFileIndex(size_t idx, const FileSpec &file, bool full) const;
 
+  /// Find a compatible file index.
+  ///
+  /// Find the index of a compatible file in the file spec list that matches \a
+  /// file starting \a idx entries into the file spec list. A file is considered
+  /// compatible if:
+  /// - The file matches exactly (only filename if \a file has no directory)
+  /// - If \a file is relative and any file in the list has this same suffix
+  /// - If any file in the list is relative and the relative path is a suffix
+  ///   of \a file
+  ///
+  /// This is used to implement better matching for setting breakpoints in
+  /// source files where an IDE might specify a full path when setting the
+  /// breakpoint and debug info contains relative paths, if a user specifies
+  /// a relative path when setting a breakpoint.
+  ///
+  /// \param[in] idx
+  ///     An index into the file list.
+  ///
+  /// \param[in] file
+  ///     The file specification to search for.
+  ///
+  /// \return
+  ///     The index of the file that matches \a file if it is found,
+  ///     else UINT32_MAX is returned.
+  size_t FindCompatibleIndex(size_t idx, const FileSpec &file) const;
+
   /// Get file at index.
   ///
   /// Gets a file from the file list. If \a idx is not a valid index, an empty

diff  --git a/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp b/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp
index 449ff124f437b..18d613c70975f 100644
--- a/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp
+++ b/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp
@@ -119,34 +119,14 @@ BreakpointResolverFileLine::SerializeToStructuredData() {
 // handling inlined functions -- in this case we need to make sure we look at
 // the declaration line of the inlined function, NOT the function it was
 // inlined into.
-void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list,
-                                                bool is_relative) {
+void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list) {
   if (m_location_spec.GetExactMatch())
     return; // Nothing to do. Contexts are precise.
 
-  llvm::StringRef relative_path;
-  if (is_relative)
-    relative_path = m_location_spec.GetFileSpec().GetDirectory().GetStringRef();
-
   Log *log = GetLog(LLDBLog::Breakpoints);
   for(uint32_t i = 0; i < sc_list.GetSize(); ++i) {
     SymbolContext sc;
     sc_list.GetContextAtIndex(i, sc);
-    if (is_relative) {
-      // If the path was relative, make sure any matches match as long as the
-      // relative parts of the path match the path from support files
-      auto sc_dir = sc.line_entry.file.GetDirectory().GetStringRef();
-      if (!sc_dir.endswith(relative_path)) {
-        // We had a relative path specified and the relative directory doesn't
-        // match so remove this one
-        LLDB_LOG(log, "removing not matching relative path {0} since it "
-                "doesn't end with {1}", sc_dir, relative_path);
-        sc_list.RemoveContextAtIndex(i);
-        --i;
-        continue;
-      }
-    }
-
     if (!sc.block)
       continue;
 
@@ -230,29 +210,17 @@ Searcher::CallbackReturn BreakpointResolverFileLine::SearchCallback(
   const uint32_t line = m_location_spec.GetLine().value_or(0);
   const llvm::Optional<uint16_t> column = m_location_spec.GetColumn();
 
-  // We'll create a new SourceLocationSpec that can take into account the
-  // relative path case, and we'll use it to resolve the symbol context
-  // of the CUs.
-  FileSpec search_file_spec = m_location_spec.GetFileSpec();
-  const bool is_relative = search_file_spec.IsRelative();
-  if (is_relative)
-    search_file_spec.ClearDirectory();
-  SourceLocationSpec search_location_spec(
-      search_file_spec, m_location_spec.GetLine().value_or(0),
-      m_location_spec.GetColumn(), m_location_spec.GetCheckInlines(),
-      m_location_spec.GetExactMatch());
-
   const size_t num_comp_units = context.module_sp->GetNumCompileUnits();
   for (size_t i = 0; i < num_comp_units; i++) {
     CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i));
     if (cu_sp) {
       if (filter.CompUnitPasses(*cu_sp))
-        cu_sp->ResolveSymbolContext(search_location_spec,
-                                    eSymbolContextEverything, sc_list);
+        cu_sp->ResolveSymbolContext(m_location_spec, eSymbolContextEverything,
+                                    sc_list);
     }
   }
 
-  FilterContexts(sc_list, is_relative);
+  FilterContexts(sc_list);
 
   StreamString s;
   s.Printf("for %s:%d ",

diff  --git a/lldb/source/Core/FileSpecList.cpp b/lldb/source/Core/FileSpecList.cpp
index 8eec8f499352c..7fa511176e468 100644
--- a/lldb/source/Core/FileSpecList.cpp
+++ b/lldb/source/Core/FileSpecList.cpp
@@ -81,6 +81,58 @@ size_t FileSpecList::FindFileIndex(size_t start_idx, const FileSpec &file_spec,
   return UINT32_MAX;
 }
 
+size_t FileSpecList::FindCompatibleIndex(size_t start_idx,
+                                         const FileSpec &file_spec) const {
+  const size_t num_files = m_files.size();
+  if (start_idx >= num_files)
+    return UINT32_MAX;
+
+  const bool file_spec_relative = file_spec.IsRelative();
+  const bool file_spec_case_sensitive = file_spec.IsCaseSensitive();
+  // When looking for files, we will compare only the filename if the directory
+  // argument is empty in file_spec
+  const bool full = !file_spec.GetDirectory().IsEmpty();
+
+  for (size_t idx = start_idx; idx < num_files; ++idx) {
+    const FileSpec &curr_file = m_files[idx];
+
+    // Always start by matching the filename first
+    if (!curr_file.FileEquals(file_spec))
+      continue;
+
+    // Only compare the full name if the we were asked to and if the current
+    // file entry has the a directory. If it doesn't have a directory then we
+    // only compare the filename.
+    if (FileSpec::Equal(curr_file, file_spec, full)) {
+      return idx;
+    } else if (curr_file.IsRelative() || file_spec_relative) {
+      llvm::StringRef curr_file_dir = curr_file.GetDirectory().GetStringRef();
+      if (curr_file_dir.empty())
+        return idx; // Basename match only for this file in the list
+
+      // Check if we have a relative path in our file list, or if "file_spec" is
+      // relative, if so, check if either ends with the other.
+      llvm::StringRef file_spec_dir = file_spec.GetDirectory().GetStringRef();
+      // We have a relative path in our file list, it matches if the
+      // specified path ends with this path, but we must ensure the full
+      // component matches (we don't want "foo/bar.cpp" to match "oo/bar.cpp").
+      auto is_suffix = [](llvm::StringRef a, llvm::StringRef b,
+                          bool case_sensitive) -> bool {
+        if (case_sensitive ? a.consume_back(b) : a.consume_back_insensitive(b))
+          return a.empty() || a.endswith("/");
+        return false;
+      };
+      const bool case_sensitive =
+          file_spec_case_sensitive || curr_file.IsCaseSensitive();
+      if (is_suffix(curr_file_dir, file_spec_dir, case_sensitive) ||
+          is_suffix(file_spec_dir, curr_file_dir, case_sensitive))
+        return idx;
+    }
+  }
+
+  // We didn't find the file, return an invalid index
+  return UINT32_MAX;
+}
 // Returns the FileSpec object at index "idx". If "idx" is out of range, then
 // an empty FileSpec object will be returned.
 const FileSpec &FileSpecList::GetFileSpecAtIndex(size_t idx) const {

diff  --git a/lldb/source/Symbol/CompileUnit.cpp b/lldb/source/Symbol/CompileUnit.cpp
index 2bae08c8b930c..da9dc95d61fb6 100644
--- a/lldb/source/Symbol/CompileUnit.cpp
+++ b/lldb/source/Symbol/CompileUnit.cpp
@@ -216,10 +216,11 @@ VariableListSP CompileUnit::GetVariableList(bool can_create) {
   return m_variables;
 }
 
-std::vector<uint32_t> FindFileIndexes(const FileSpecList &files, const FileSpec &file) {
+std::vector<uint32_t> FindFileIndexes(const FileSpecList &files,
+                                      const FileSpec &file) {
   std::vector<uint32_t> result;
   uint32_t idx = -1;
-  while ((idx = files.FindFileIndex(idx + 1, file, /*full=*/true)) !=
+  while ((idx = files.FindCompatibleIndex(idx + 1, file)) !=
          UINT32_MAX)
     result.push_back(idx);
   return result;
@@ -230,7 +231,8 @@ uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line,
                                     LineEntry *line_entry_ptr) {
   if (!file_spec_ptr)
     file_spec_ptr = &GetPrimaryFile();
-  std::vector<uint32_t> file_indexes = FindFileIndexes(GetSupportFiles(), *file_spec_ptr);
+  std::vector<uint32_t> file_indexes = FindFileIndexes(GetSupportFiles(),
+                                                       *file_spec_ptr);
   if (file_indexes.empty())
     return UINT32_MAX;
 
@@ -255,7 +257,6 @@ void CompileUnit::ResolveSymbolContext(
   // First find all of the file indexes that match our "file_spec". If
   // "file_spec" has an empty directory, then only compare the basenames when
   // finding file indexes
-  std::vector<uint32_t> file_indexes;
   bool file_spec_matches_cu_file_spec =
       FileSpec::Match(file_spec, this->GetPrimaryFile());
 
@@ -276,13 +277,8 @@ void CompileUnit::ResolveSymbolContext(
     return;
   }
 
-  uint32_t file_idx =
-      GetSupportFiles().FindFileIndex(0, file_spec, true);
-  while (file_idx != UINT32_MAX) {
-    file_indexes.push_back(file_idx);
-    file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true);
-  }
-
+  std::vector<uint32_t> file_indexes = FindFileIndexes(GetSupportFiles(),
+                                                       file_spec);
   const size_t num_file_indexes = file_indexes.size();
   if (num_file_indexes == 0)
     return;

diff  --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
index 5bd7b43130ebc..6212ba6c98646 100644
--- a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
@@ -8,9 +8,11 @@
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
+import os
 import side_effect
 
 
+
 class BreakpointCommandTestCase(TestBase):
 
     NO_DEBUG_INFO_TESTCASE = True
@@ -31,6 +33,77 @@ def test_commands_on_creation(self):
         self.build()
         self.breakpoint_commands_on_creation()
 
+    @skipIf(oslist=["windows"])
+    @no_debug_info_test
+    def test_breakpoints_with_relative_path_line_tables(self):
+        """
+            Test that we can set breakpoints using a full or partial path when
+            line tables in the debug information has relative paths where the
+            relative path is either fully contained in the specified path, or if
+            the specified path also a relative path that is shorter than the
+            path in the debug info.
+
+            The "relative.yaml" contains a line table that is:
+
+            Line table for a/b/c/main.cpp in `a.out
+            0x0000000100003f94: a/b/c/main.cpp:1
+            0x0000000100003fb0: a/b/c/main.cpp:2:3
+            0x0000000100003fb8: a/b/c/main.cpp:2:3
+
+            So the line table contains relative paths. We should be able to set
+            breakpoints with any source path that matches this path which
+            includes paths that are longer than "a/b/c/main.cpp", but also any
+            relative path that is shorter than this while all specified relative
+            path components still match.
+        """
+        src_dir = self.getSourceDir()
+        yaml_path = os.path.join(src_dir, "relative.yaml")
+        yaml_base, ext = os.path.splitext(yaml_path)
+        obj_path = self.getBuildArtifact("a.out")
+        self.yaml2obj(yaml_path, obj_path)
+
+        # Create a target with the object file we just created from YAML
+        target = self.dbg.CreateTarget(obj_path)
+        # We now have debug information with line table paths that start are
+        # "./a/b/c/main.cpp".
+
+        # Make sure that all of the following paths create a breakpoint
+        # successfully. We have paths that are longer than our path, and also
+        # that are shorter where all relative directories still match.
+        valid_paths = [
+            "/x/a/b/c/main.cpp",
+            "/x/y/a/b/c/main.cpp",
+            "./x/y/a/b/c/main.cpp",
+            "x/y/a/b/c/main.cpp",
+            "./y/a/b/c/main.cpp",
+            "y/a/b/c/main.cpp",
+            "./a/b/c/main.cpp",
+            "a/b/c/main.cpp",
+            "./b/c/main.cpp",
+            "b/c/main.cpp",
+            "./c/main.cpp",
+            "c/main.cpp",
+            "./main.cpp",
+            "main.cpp",
+        ]
+        for path in valid_paths:
+            bkpt = target.BreakpointCreateByLocation(path, 2)
+            self.assertTrue(bkpt.GetNumLocations() > 0,
+                'Couldn\'t resolve breakpoint using full path "%s" in executate "%s" with '
+                'debug info that has relative path with matching suffix' % (path, self.getBuildArtifact("a.out")))
+        invalid_paths = [
+            "/x/b/c/main.cpp",
+            "/x/c/main.cpp",
+            "/x/main.cpp",
+            "./x/y/a/d/c/main.cpp",
+        ]
+        for path in invalid_paths:
+            bkpt = target.BreakpointCreateByLocation(path, 2)
+            self.assertTrue(bkpt.GetNumLocations() == 0,
+                'Incorrectly resolved a breakpoint using full path "%s" with '
+                'debug info that has relative path with matching suffix' % (path))
+
+
     def setUp(self):
         # Call super's setUp().
         TestBase.setUp(self)
@@ -307,8 +380,8 @@ def test_add_commands_by_breakpoint_name(self):
         for id in bp_ids:
             self.expect("breakpoint command list {0}".format(id),
                         patterns=["bktptcmd.function"])
-        
-        
+
+
 
     def test_breakpoint_delete_disabled(self):
         """Test 'break delete --disabled' works"""

diff  --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command/relative.yaml b/lldb/test/API/functionalities/breakpoint/breakpoint_command/relative.yaml
new file mode 100644
index 0000000000000..d67d8b0012251
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command/relative.yaml
@@ -0,0 +1,445 @@
+--- !mach-o
+FileHeader:
+  magic:           0xFEEDFACF
+  cputype:         0x100000C
+  cpusubtype:      0x0
+  filetype:        0xA
+  ncmds:           7
+  sizeofcmds:      1240
+  flags:           0x0
+  reserved:        0x0
+LoadCommands:
+  - cmd:             LC_UUID
+    cmdsize:         24
+    uuid:            030921EA-6D76-3A68-B515-386B9AF6D568
+  - cmd:             LC_BUILD_VERSION
+    cmdsize:         24
+    platform:        1
+    minos:           786432
+    sdk:             787200
+    ntools:          0
+  - cmd:             LC_SYMTAB
+    cmdsize:         24
+    symoff:          4096
+    nsyms:           2
+    stroff:          4128
+    strsize:         28
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         72
+    segname:         __PAGEZERO
+    vmaddr:          0
+    vmsize:          4294967296
+    fileoff:         0
+    filesize:        0
+    maxprot:         0
+    initprot:        0
+    nsects:          0
+    flags:           0
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         232
+    segname:         __TEXT
+    vmaddr:          4294967296
+    vmsize:          16384
+    fileoff:         0
+    filesize:        0
+    maxprot:         5
+    initprot:        5
+    nsects:          2
+    flags:           0
+    Sections:
+      - sectname:        __text
+        segname:         __TEXT
+        addr:            0x100003F94
+        size:            36
+        offset:          0x0
+        align:           2
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x80000400
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         CFFAEDFE0C000001000000000A00000007000000D804000000000000000000001B000000
+      - sectname:        __unwind_info
+        segname:         __TEXT
+        addr:            0x100003FB8
+        size:            72
+        offset:          0x0
+        align:           2
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         CFFAEDFE0C000001000000000A00000007000000D804000000000000000000001B00000018000000030921EA6D763A68B515386B9AF6D56832000000180000000100000000000C00
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         72
+    segname:         __LINKEDIT
+    vmaddr:          4294983680
+    vmsize:          4096
+    fileoff:         4096
+    filesize:        60
+    maxprot:         1
+    initprot:        1
+    nsects:          0
+    flags:           0
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         792
+    segname:         __DWARF
+    vmaddr:          4294987776
+    vmsize:          4096
+    fileoff:         8192
+    filesize:        796
+    maxprot:         7
+    initprot:        3
+    nsects:          9
+    flags:           0
+    Sections:
+      - sectname:        __debug_line
+        segname:         __DWARF
+        addr:            0x100005000
+        size:            64
+        offset:          0x2000
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+      - sectname:        __debug_aranges
+        segname:         __DWARF
+        addr:            0x100005040
+        size:            48
+        offset:          0x2040
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+      - sectname:        __debug_info
+        segname:         __DWARF
+        addr:            0x100005070
+        size:            148
+        offset:          0x2070
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+      - sectname:        __debug_abbrev
+        segname:         __DWARF
+        addr:            0x100005104
+        size:            90
+        offset:          0x2104
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+      - sectname:        __debug_str
+        segname:         __DWARF
+        addr:            0x10000515E
+        size:            200
+        offset:          0x215E
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+      - sectname:        __apple_names
+        segname:         __DWARF
+        addr:            0x100005226
+        size:            60
+        offset:          0x2226
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         485341480100000001000000010000000C000000000000000100000001000600000000006A7F9A7C2C000000AB000000010000003200000000000000
+      - sectname:        __apple_namespac
+        segname:         __DWARF
+        addr:            0x100005262
+        size:            36
+        offset:          0x2262
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         485341480100000001000000000000000C000000000000000100000001000600FFFFFFFF
+      - sectname:        __apple_types
+        segname:         __DWARF
+        addr:            0x100005286
+        size:            114
+        offset:          0x2286
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         48534148010000000200000002000000180000000000000004000000010006000300050005000B000600060000000000010000003080880B6320957C440000005B000000BF0000000100000076000000240000A4283A0C00000000C3000000010000008C00000024000057D77B9300000000
+      - sectname:        __apple_objc
+        segname:         __DWARF
+        addr:            0x1000052F8
+        size:            36
+        offset:          0x22F8
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         485341480100000001000000000000000C000000000000000100000001000600FFFFFFFF
+LinkEditData:
+  NameList:
+    - n_strx:          2
+      n_type:          0xF
+      n_sect:          1
+      n_desc:          16
+      n_value:         4294967296
+    - n_strx:          22
+      n_type:          0xF
+      n_sect:          1
+      n_desc:          0
+      n_value:         4294983572
+  StringTable:
+    - ''
+    - ''
+    - __mh_execute_header
+    - _main
+DWARF:
+  debug_str:
+    - ''
+    - 'Apple clang version 13.1.6 (clang-1316.0.21.2)'
+    - main.cpp
+    - '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk'
+    - MacOSX.sdk
+    - './a/b/c'
+    - main
+    - argc
+    - argv
+    - envp
+    - int
+    - char
+  debug_abbrev:
+    - ID:              0
+      Table:
+        - Code:            0x1
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_producer
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_language
+              Form:            DW_FORM_data2
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_LLVM_sysroot
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_APPLE_sdk
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_stmt_list
+              Form:            DW_FORM_sec_offset
+            - Attribute:       DW_AT_comp_dir
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_low_pc
+              Form:            DW_FORM_addr
+            - Attribute:       DW_AT_high_pc
+              Form:            DW_FORM_data4
+        - Code:            0x2
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_low_pc
+              Form:            DW_FORM_addr
+            - Attribute:       DW_AT_high_pc
+              Form:            DW_FORM_data4
+            - Attribute:       DW_AT_APPLE_omit_frame_ptr
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_frame_base
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref_addr
+            - Attribute:       DW_AT_external
+              Form:            DW_FORM_flag_present
+        - Code:            0x3
+          Tag:             DW_TAG_formal_parameter
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_location
+              Form:            DW_FORM_exprloc
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_decl_file
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_decl_line
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref_addr
+        - Code:            0x4
+          Tag:             DW_TAG_base_type
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_encoding
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_byte_size
+              Form:            DW_FORM_data1
+        - Code:            0x5
+          Tag:             DW_TAG_pointer_type
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref_addr
+        - Code:            0x6
+          Tag:             DW_TAG_const_type
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref_addr
+  debug_aranges:
+    - Length:          0x2C
+      Version:         2
+      CuOffset:        0x0
+      AddressSize:     0x8
+      Descriptors:
+        - Address:         0x100003F94
+          Length:          0x24
+  debug_info:
+    - Length:          0x90
+      Version:         4
+      AbbrevTableID:   0
+      AbbrOffset:      0x0
+      AddrSize:        8
+      Entries:
+        - AbbrCode:        0x1
+          Values:
+            - Value:           0x1
+            - Value:           0x1A
+            - Value:           0x30
+            - Value:           0x39
+            - Value:           0x98
+            - Value:           0x0
+            - Value:           0xA3
+            - Value:           0x100003F94
+            - Value:           0x24
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0x100003F94
+            - Value:           0x24
+            - Value:           0x1
+            - Value:           0x1
+              BlockData:       [ 0x6F ]
+            - Value:           0xAB
+            - Value:           0x1
+            - Value:           0x1
+            - Value:           0x76
+            - Value:           0x1
+        - AbbrCode:        0x3
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0x18 ]
+            - Value:           0xB0
+            - Value:           0x1
+            - Value:           0x1
+            - Value:           0x76
+        - AbbrCode:        0x3
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0x10 ]
+            - Value:           0xB5
+            - Value:           0x1
+            - Value:           0x1
+            - Value:           0x7D
+        - AbbrCode:        0x3
+          Values:
+            - Value:           0x2
+              BlockData:       [ 0x91, 0x8 ]
+            - Value:           0xBA
+            - Value:           0x1
+            - Value:           0x1
+            - Value:           0x7D
+        - AbbrCode:        0x0
+        - AbbrCode:        0x4
+          Values:
+            - Value:           0xBF
+            - Value:           0x5
+            - Value:           0x4
+        - AbbrCode:        0x5
+          Values:
+            - Value:           0x82
+        - AbbrCode:        0x5
+          Values:
+            - Value:           0x87
+        - AbbrCode:        0x6
+          Values:
+            - Value:           0x8C
+        - AbbrCode:        0x4
+          Values:
+            - Value:           0xC3
+            - Value:           0x6
+            - Value:           0x1
+        - AbbrCode:        0x0
+  debug_line:
+    - Length:          60
+      Version:         4
+      PrologueLength:  32
+      MinInstLength:   1
+      MaxOpsPerInst:   1
+      DefaultIsStmt:   1
+      LineBase:        251
+      LineRange:       14
+      OpcodeBase:      13
+      StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
+      Files:
+        - Name:            main.cpp
+          DirIdx:          0
+          ModTime:         0
+          Length:          0
+      Opcodes:
+        - Opcode:          DW_LNS_extended_op
+          ExtLen:          9
+          SubOpcode:       DW_LNE_set_address
+          Data:            4294983572
+        - Opcode:          DW_LNS_copy
+          Data:            0
+        - Opcode:          DW_LNS_set_column
+          Data:            3
+        - Opcode:          DW_LNS_set_prologue_end
+          Data:            0
+        - Opcode:          DW_LNS_const_add_pc
+          Data:            0
+        - Opcode:          0xAD
+          Data:            0
+        - Opcode:          DW_LNS_advance_pc
+          Data:            8
+        - Opcode:          DW_LNS_extended_op
+          ExtLen:          1
+          SubOpcode:       DW_LNE_end_sequence
+          Data:            0
+...

diff  --git a/lldb/unittests/Core/CMakeLists.txt b/lldb/unittests/Core/CMakeLists.txt
index 6e2dd03536d7a..ea3f652203e75 100644
--- a/lldb/unittests/Core/CMakeLists.txt
+++ b/lldb/unittests/Core/CMakeLists.txt
@@ -2,6 +2,7 @@ add_lldb_unittest(LLDBCoreTests
   CommunicationTest.cpp
   DiagnosticEventTest.cpp
   DumpDataExtractorTest.cpp
+  FileSpecListTest.cpp
   FormatEntityTest.cpp
   MangledTest.cpp
   ModuleSpecTest.cpp

diff  --git a/lldb/unittests/Core/FileSpecListTest.cpp b/lldb/unittests/Core/FileSpecListTest.cpp
new file mode 100644
index 0000000000000..d1a8eadde3bfa
--- /dev/null
+++ b/lldb/unittests/Core/FileSpecListTest.cpp
@@ -0,0 +1,125 @@
+//===-- FileSpecListTest.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+#include "lldb/Core/FileSpecList.h"
+
+using namespace lldb_private;
+
+static FileSpec PosixSpec(llvm::StringRef path) {
+  return FileSpec(path, FileSpec::Style::posix);
+}
+
+static FileSpec WindowsSpec(llvm::StringRef path) {
+  return FileSpec(path, FileSpec::Style::windows);
+}
+
+TEST(FileSpecListTest, RelativePathMatchesPosix) {
+
+  const FileSpec fullpath = PosixSpec("/build/src/main.cpp");
+  const FileSpec relative = PosixSpec("./src/main.cpp");
+  const FileSpec basename = PosixSpec("./main.cpp");
+  const FileSpec full_wrong = PosixSpec("/other/wrong/main.cpp");
+  const FileSpec rel_wrong = PosixSpec("./wrong/main.cpp");
+  // Make sure these don't match "src/main.cpp" as we want to match full
+  // directories only
+  const FileSpec rel2_wrong = PosixSpec("asrc/main.cpp");
+  const FileSpec rel3_wrong = PosixSpec("rc/main.cpp");
+
+  FileSpecList files;
+  files.Append(fullpath);
+  files.Append(relative);
+  files.Append(basename);
+  files.Append(full_wrong);
+  files.Append(rel_wrong);
+  files.Append(rel2_wrong);
+  files.Append(rel3_wrong);
+
+  // Make sure the full path only matches the first entry
+  EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, fullpath));
+  EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, fullpath));
+  EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, fullpath));
+  EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, fullpath));
+  // Make sure the relative path matches the all of the entries that contain
+  // the relative path
+  EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, relative));
+  EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, relative));
+  EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, relative));
+  EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, relative));
+
+  // Make sure looking file a file using the basename matches all entries
+  EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, basename));
+  EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, basename));
+  EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, basename));
+  EXPECT_EQ((size_t)3, files.FindCompatibleIndex(3, basename));
+  EXPECT_EQ((size_t)4, files.FindCompatibleIndex(4, basename));
+  EXPECT_EQ((size_t)5, files.FindCompatibleIndex(5, basename));
+  EXPECT_EQ((size_t)6, files.FindCompatibleIndex(6, basename));
+
+  // Make sure that paths that have a common suffix don't return values that
+  // don't match on directory delimiters.
+  EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel2_wrong));
+  EXPECT_EQ((size_t)5, files.FindCompatibleIndex(3, rel2_wrong));
+  EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(6, rel2_wrong));
+
+  EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel3_wrong));
+  EXPECT_EQ((size_t)6, files.FindCompatibleIndex(3, rel3_wrong));
+}
+
+TEST(FileSpecListTest, RelativePathMatchesWindows) {
+
+  const FileSpec fullpath = WindowsSpec(R"(C:\build\src\main.cpp)");
+  const FileSpec relative = WindowsSpec(R"(.\src\main.cpp)");
+  const FileSpec basename = WindowsSpec(R"(.\main.cpp)");
+  const FileSpec full_wrong = WindowsSpec(R"(\other\wrong\main.cpp)");
+  const FileSpec rel_wrong = WindowsSpec(R"(.\wrong\main.cpp)");
+  // Make sure these don't match "src\main.cpp" as we want to match full
+  // directories only
+  const FileSpec rel2_wrong = WindowsSpec(R"(asrc\main.cpp)");
+  const FileSpec rel3_wrong = WindowsSpec(R"("rc\main.cpp)");
+
+  FileSpecList files;
+  files.Append(fullpath);
+  files.Append(relative);
+  files.Append(basename);
+  files.Append(full_wrong);
+  files.Append(rel_wrong);
+  files.Append(rel2_wrong);
+  files.Append(rel3_wrong);
+
+  // Make sure the full path only matches the first entry
+  EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, fullpath));
+  EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, fullpath));
+  EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, fullpath));
+  EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, fullpath));
+  // Make sure the relative path matches the all of the entries that contain
+  // the relative path
+  EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, relative));
+  EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, relative));
+  EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, relative));
+  EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(3, relative));
+
+  // Make sure looking file a file using the basename matches all entries
+  EXPECT_EQ((size_t)0, files.FindCompatibleIndex(0, basename));
+  EXPECT_EQ((size_t)1, files.FindCompatibleIndex(1, basename));
+  EXPECT_EQ((size_t)2, files.FindCompatibleIndex(2, basename));
+  EXPECT_EQ((size_t)3, files.FindCompatibleIndex(3, basename));
+  EXPECT_EQ((size_t)4, files.FindCompatibleIndex(4, basename));
+  EXPECT_EQ((size_t)5, files.FindCompatibleIndex(5, basename));
+  EXPECT_EQ((size_t)6, files.FindCompatibleIndex(6, basename));
+
+  // Make sure that paths that have a common suffix don't return values that
+  // don't match on directory delimiters.
+  EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel2_wrong));
+  EXPECT_EQ((size_t)5, files.FindCompatibleIndex(3, rel2_wrong));
+  EXPECT_EQ((size_t)UINT32_MAX, files.FindCompatibleIndex(6, rel2_wrong));
+
+  EXPECT_EQ((size_t)2, files.FindCompatibleIndex(0, rel3_wrong));
+  EXPECT_EQ((size_t)6, files.FindCompatibleIndex(3, rel3_wrong));
+}


        


More information about the lldb-commits mailing list