[Lldb-commits] [lldb] Add support for inline DWARF source files. (PR #75880)

Adrian Prantl via lldb-commits lldb-commits at lists.llvm.org
Wed Jan 3 16:39:54 PST 2024


https://github.com/adrian-prantl updated https://github.com/llvm/llvm-project/pull/75880

>From bce832068582908b521b6ded591eb7a0240f647e Mon Sep 17 00:00:00 2001
From: Adrian Prantl <aprantl at apple.com>
Date: Mon, 18 Dec 2023 15:59:00 -0800
Subject: [PATCH] Add support for inline DWARF source files.

LLVM supports DWARF 5 linetable extension to store source files inline
in DWARF. This is particularly useful for compiler-generated source
code. This implementation tries to materialize them as temporary files
lazily, so SBAPI clients don't need to be aware of them.

As an implementation detail, this patch separate SupportFileList from
FileSpecList and makes SupportFileList uncopyable.
---
 lldb/include/lldb/Symbol/CompileUnit.h        |  23 ++--
 lldb/include/lldb/Symbol/SymbolFile.h         |   2 +-
 lldb/include/lldb/Symbol/SymbolFileOnDemand.h |   2 +-
 lldb/include/lldb/Utility/FileSpecList.h      | 109 +++++++++++++-----
 lldb/source/API/SBCompileUnit.cpp             |   2 +-
 lldb/source/Commands/CommandObjectSource.cpp  |   2 +-
 lldb/source/Core/ModuleList.cpp               |   2 +-
 .../Clang/ClangUserExpression.cpp             |  12 +-
 .../Clang/CppModuleConfiguration.cpp          |   6 +-
 .../Breakpad/SymbolFileBreakpad.cpp           |   5 +-
 .../SymbolFile/Breakpad/SymbolFileBreakpad.h  |   3 +-
 .../Plugins/SymbolFile/CTF/SymbolFileCTF.h    |   2 +-
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      | 107 ++++++++++++-----
 .../SymbolFile/DWARF/SymbolFileDWARF.h        |   9 +-
 .../DWARF/SymbolFileDWARFDebugMap.cpp         |   4 +-
 .../DWARF/SymbolFileDWARFDebugMap.h           |   2 +-
 .../Plugins/SymbolFile/JSON/SymbolFileJSON.h  |   2 +-
 .../NativePDB/SymbolFileNativePDB.cpp         |   4 +-
 .../NativePDB/SymbolFileNativePDB.h           |   2 +-
 .../Plugins/SymbolFile/PDB/SymbolFilePDB.cpp  |   2 +-
 .../Plugins/SymbolFile/PDB/SymbolFilePDB.h    |   2 +-
 .../SymbolFile/Symtab/SymbolFileSymtab.cpp    |   2 +-
 .../SymbolFile/Symtab/SymbolFileSymtab.h      |   2 +-
 lldb/source/Symbol/CompileUnit.cpp            |  13 +--
 lldb/source/Symbol/SymbolFileOnDemand.cpp     |   2 +-
 lldb/source/Utility/FileSpecList.cpp          |  58 ++++++++--
 .../inline-sourcefile/Makefile                |  11 ++
 .../TestInlineSourceFiles.py                  |  17 +++
 .../inline-sourcefile/inline.ll               |  39 +++++++
 .../functionalities/inline-sourcefile/main.c  |   7 ++
 lldb/unittests/Core/FileSpecListTest.cpp      |   8 +-
 .../Expression/CppModuleConfigurationTest.cpp |  48 +++++---
 32 files changed, 371 insertions(+), 140 deletions(-)
 create mode 100644 lldb/test/API/functionalities/inline-sourcefile/Makefile
 create mode 100644 lldb/test/API/functionalities/inline-sourcefile/TestInlineSourceFiles.py
 create mode 100644 lldb/test/API/functionalities/inline-sourcefile/inline.ll
 create mode 100644 lldb/test/API/functionalities/inline-sourcefile/main.c

diff --git a/lldb/include/lldb/Symbol/CompileUnit.h b/lldb/include/lldb/Symbol/CompileUnit.h
index 93f191b4998584..89e853ab599d0f 100644
--- a/lldb/include/lldb/Symbol/CompileUnit.h
+++ b/lldb/include/lldb/Symbol/CompileUnit.h
@@ -112,10 +112,13 @@ class CompileUnit : public std::enable_shared_from_this<CompileUnit>,
   ///     the compile unit is optimized will be made when
   ///     CompileUnit::GetIsOptimized() is called.
   ///
+  /// \param[in] support_files
+  ///     An rvalue list of already parsed support files.
   /// \see lldb::LanguageType
   CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
               const FileSpec &file_spec, lldb::user_id_t uid,
-              lldb::LanguageType language, lldb_private::LazyBool is_optimized);
+              lldb::LanguageType language, lldb_private::LazyBool is_optimized,
+              SupportFileList &&support_files = {});
 
   /// Add a function to this compile unit.
   ///
@@ -226,6 +229,9 @@ class CompileUnit : public std::enable_shared_from_this<CompileUnit>,
   /// Return the primary source file associated with this compile unit.
   const FileSpec &GetPrimaryFile() const { return m_file_spec; }
 
+  /// Return the primary source file associated with this compile unit.
+  void SetPrimaryFile(const FileSpec &fs) { m_file_spec = fs; }
+
   /// Get the line table for the compile unit.
   ///
   /// Called by clients and the SymbolFile plug-in. The SymbolFile plug-ins
@@ -265,7 +271,13 @@ class CompileUnit : public std::enable_shared_from_this<CompileUnit>,
   ///
   /// \return
   ///     A support file list object.
-  const FileSpecList &GetSupportFiles();
+  const SupportFileList &GetSupportFiles();
+
+  /// Used by plugins that parse the support file list.
+  SupportFileList &GetSupportFileList() {
+    m_flags.Set(flagsParsedSupportFiles);
+    return m_support_files;
+  }
 
   /// Get the compile unit's imported module list.
   ///
@@ -331,8 +343,6 @@ class CompileUnit : public std::enable_shared_from_this<CompileUnit>,
   ///     A line table object pointer that this object now owns.
   void SetLineTable(LineTable *line_table);
 
-  void SetSupportFiles(FileSpecList support_files);
-
   void SetDebugMacros(const DebugMacrosSP &debug_macros);
 
   /// Set accessor for the variable list.
@@ -410,9 +420,8 @@ class CompileUnit : public std::enable_shared_from_this<CompileUnit>,
   std::vector<SourceModule> m_imported_modules;
   /// The primary file associated with this compile unit.
   FileSpec m_file_spec;
-  /// Files associated with this compile unit's line table and
-  /// declarations.
-  FileSpecList m_support_files;
+  /// Files associated with this compile unit's line table and declarations.
+  SupportFileList m_support_files;
   /// Line table that will get parsed on demand.
   std::unique_ptr<LineTable> m_line_table_up;
   /// Debug macros that will get parsed on demand.
diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index c9a2a647a039dc..f356f7b789fa38 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -197,7 +197,7 @@ class SymbolFile : public PluginInterface {
     return false;
   }
   virtual bool ParseSupportFiles(CompileUnit &comp_unit,
-                                 FileSpecList &support_files) = 0;
+                                 SupportFileList &support_files) = 0;
   virtual size_t ParseTypes(CompileUnit &comp_unit) = 0;
   virtual bool ParseIsOptimized(CompileUnit &comp_unit) { return false; }
 
diff --git a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
index cde9f3c3b8ce1f..4e3009941aa7d6 100644
--- a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
+++ b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
@@ -81,7 +81,7 @@ class SymbolFileOnDemand : public lldb_private::SymbolFile {
       llvm::function_ref<bool(lldb_private::Module &)>) override;
 
   bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
-                         lldb_private::FileSpecList &support_files) override;
+                         lldb_private::SupportFileList &support_files) override;
 
   bool ParseIsOptimized(lldb_private::CompileUnit &comp_unit) override;
 
diff --git a/lldb/include/lldb/Utility/FileSpecList.h b/lldb/include/lldb/Utility/FileSpecList.h
index 77587aa917916b..90a6f089d37668 100644
--- a/lldb/include/lldb/Utility/FileSpecList.h
+++ b/lldb/include/lldb/Utility/FileSpecList.h
@@ -17,6 +17,89 @@
 namespace lldb_private {
 class Stream;
 
+/// Wraps either a FileSpec that represents a local file or a source
+/// file whose contents is known (for example because it can be
+/// reconstructed from debug info), but that hasn't been written to a
+/// file yet.
+class SupportFile {
+protected:
+  FileSpec m_file_spec;
+
+public:
+  SupportFile(const FileSpec &spec) : m_file_spec(spec) {}
+  SupportFile(const SupportFile &other) = delete;
+  SupportFile(SupportFile &&other) = default;
+  virtual ~SupportFile() = default;
+  bool operator==(const SupportFile &other) {
+    return m_file_spec == other.m_file_spec;
+  }
+  /// Return the file name only. Useful for resolving breakpoints by file name.
+  const FileSpec &GetSpecOnly() const { return m_file_spec; };
+  /// Materialize the file to disk and return the path to that temporary file.
+  virtual const FileSpec &Materialize() {
+    return m_file_spec;
+  }
+};
+
+/// A list of support files for a CompileUnit.
+class SupportFileList {
+public:
+  SupportFileList(){};
+  SupportFileList(const SupportFileList &) = delete;
+  SupportFileList(SupportFileList &&other)
+      : m_files(std::move(other.m_files)) {}
+
+  typedef std::vector<std::unique_ptr<SupportFile>> collection;
+  typedef collection::const_iterator const_iterator;
+  const_iterator begin() const { return m_files.begin(); }
+  const_iterator end() const { return m_files.end(); }
+
+  void Append(const FileSpec &file) {
+    return Append(std::make_unique<SupportFile>(file));
+  }
+  void Append(std::unique_ptr<SupportFile> &&file) {
+    m_files.push_back(std::move(file));
+  }
+  // FIXME: Only used by SymbolFilePDB. Replace with a DenseSet at call site.
+  bool AppendIfUnique(const FileSpec &file);
+  size_t GetSize() const { return m_files.size(); }
+  const FileSpec &GetFileSpecAtIndex(size_t idx) const;
+  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;
+
+  template <class... Args> void EmplaceBack(Args &&...args) {
+    m_files.push_back(
+        std::make_unique<SupportFile>(FileSpec(std::forward<Args>(args)...)));
+  }
+
+protected:
+  collection m_files; ///< A collection of FileSpec objects.
+};
+
 /// \class FileSpecList FileSpecList.h "lldb/Utility/FileSpecList.h"
 /// A file collection class.
 ///
@@ -114,32 +197,6 @@ 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/API/SBCompileUnit.cpp b/lldb/source/API/SBCompileUnit.cpp
index 3aa65e225d7aba..65fdb11032b9c0 100644
--- a/lldb/source/API/SBCompileUnit.cpp
+++ b/lldb/source/API/SBCompileUnit.cpp
@@ -171,7 +171,7 @@ uint32_t SBCompileUnit::FindSupportFileIndex(uint32_t start_idx,
   LLDB_INSTRUMENT_VA(this, start_idx, sb_file, full);
 
   if (m_opaque_ptr) {
-    const FileSpecList &support_files = m_opaque_ptr->GetSupportFiles();
+    const SupportFileList &support_files = m_opaque_ptr->GetSupportFiles();
     return support_files.FindFileIndex(start_idx, sb_file.ref(), full);
   }
   return 0;
diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp
index db158a7f526305..cabf6f0436f176 100644
--- a/lldb/source/Commands/CommandObjectSource.cpp
+++ b/lldb/source/Commands/CommandObjectSource.cpp
@@ -204,7 +204,7 @@ class CommandObjectSourceInfo : public CommandObjectParsed {
     if (cu) {
       assert(file_spec.GetFilename().AsCString());
       bool has_path = (file_spec.GetDirectory().AsCString() != nullptr);
-      const FileSpecList &cu_file_list = cu->GetSupportFiles();
+      const SupportFileList &cu_file_list = cu->GetSupportFiles();
       size_t file_idx = cu_file_list.FindFileIndex(0, file_spec, has_path);
       if (file_idx != UINT32_MAX) {
         // Update the file to how it appears in the CU.
diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp
index aa89c93c8d0521..2180f29f369427 100644
--- a/lldb/source/Core/ModuleList.cpp
+++ b/lldb/source/Core/ModuleList.cpp
@@ -164,7 +164,7 @@ void ModuleListProperties::UpdateSymlinkMappings() {
   llvm::sys::ScopedWriter lock(m_symlink_paths_mutex);
   const bool notify = false;
   m_symlink_paths.Clear(notify);
-  for (FileSpec symlink : list) {
+  for (auto symlink : list) {
     FileSpec resolved;
     Status status = FileSystem::Instance().Readlink(symlink, resolved);
     if (status.Success())
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
index 68bdd96e8adb03..30bc81c9ed8c19 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -488,18 +488,18 @@ CppModuleConfiguration GetModuleConfig(lldb::LanguageType language,
 
   // Build a list of files we need to analyze to build the configuration.
   FileSpecList files;
-  for (const FileSpec &f : sc.comp_unit->GetSupportFiles())
-    files.AppendIfUnique(f);
+  for (auto &f : sc.comp_unit->GetSupportFiles())
+    files.AppendIfUnique(f->Materialize());
   // We also need to look at external modules in the case of -gmodules as they
   // contain the support files for libc++ and the C library.
   llvm::DenseSet<SymbolFile *> visited_symbol_files;
   sc.comp_unit->ForEachExternalModule(
       visited_symbol_files, [&files](Module &module) {
         for (std::size_t i = 0; i < module.GetNumCompileUnits(); ++i) {
-          const FileSpecList &support_files =
+          const SupportFileList &support_files =
               module.GetCompileUnitAtIndex(i)->GetSupportFiles();
-          for (const FileSpec &f : support_files) {
-            files.AppendIfUnique(f);
+          for (auto &f : support_files) {
+            files.AppendIfUnique(f->Materialize());
           }
         }
         return false;
@@ -508,7 +508,7 @@ CppModuleConfiguration GetModuleConfig(lldb::LanguageType language,
   LLDB_LOG(log, "[C++ module config] Found {0} support files to analyze",
            files.GetSize());
   if (log && log->GetVerbose()) {
-    for (const FileSpec &f : files)
+    for (auto &f : files)
       LLDB_LOGV(log, "[C++ module config] Analyzing support file: {0}",
                 f.GetPath());
   }
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp b/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp
index 62443d1290dc72..f43a04488230f0 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp
@@ -134,9 +134,9 @@ bool CppModuleConfiguration::hasValidConfig() {
 CppModuleConfiguration::CppModuleConfiguration(
     const FileSpecList &support_files, const llvm::Triple &triple) {
   // Analyze all files we were given to build the configuration.
-  bool error = !llvm::all_of(support_files,
-                             std::bind(&CppModuleConfiguration::analyzeFile,
-                                       this, std::placeholders::_1, triple));
+  bool error = !llvm::all_of(support_files, [&](auto &file) {
+    return CppModuleConfiguration::analyzeFile(file, triple);
+  });
   // If we have a valid configuration at this point, set the
   // include directories and module list that should be used.
   if (!error && hasValidConfig()) {
diff --git a/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp b/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
index 729d6af02402d8..47c8074adc5b7c 100644
--- a/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
+++ b/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
@@ -278,13 +278,14 @@ bool SymbolFileBreakpad::ParseLineTable(CompileUnit &comp_unit) {
 }
 
 bool SymbolFileBreakpad::ParseSupportFiles(CompileUnit &comp_unit,
-                                           FileSpecList &support_files) {
+                                           SupportFileList &support_files) {
   std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
   CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data;
   if (!data.support_files)
     ParseLineTableAndSupportFiles(comp_unit, data);
 
-  support_files = std::move(*data.support_files);
+  for (auto &fs : *data.support_files)
+    support_files.Append(fs);
   return true;
 }
 
diff --git a/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h b/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
index 214fbdd3ff3aa0..41e4e3b258014c 100644
--- a/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
+++ b/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
@@ -73,7 +73,7 @@ class SymbolFileBreakpad : public SymbolFileCommon {
   bool ParseDebugMacros(CompileUnit &comp_unit) override { return false; }
 
   bool ParseSupportFiles(CompileUnit &comp_unit,
-                         FileSpecList &support_files) override;
+                         SupportFileList &support_files) override;
   size_t ParseTypes(CompileUnit &cu) override { return 0; }
 
   bool ParseImportedModules(
@@ -195,7 +195,6 @@ class SymbolFileBreakpad : public SymbolFileCommon {
     Bookmark bookmark;
     std::optional<FileSpecList> support_files;
     std::unique_ptr<LineTable> line_table_up;
-
   };
 
   uint32_t CalculateNumCompileUnits() override;
diff --git a/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h b/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h
index f111937dbd6ef0..3a80138fffbc37 100644
--- a/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h
+++ b/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h
@@ -66,7 +66,7 @@ class SymbolFileCTF : public lldb_private::SymbolFileCommon {
   bool ParseDebugMacros(CompileUnit &comp_unit) override { return false; }
 
   bool ParseSupportFiles(CompileUnit &comp_unit,
-                         FileSpecList &support_files) override {
+                         SupportFileList &support_files) override {
     return false;
   }
 
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 505ea29ca4d4f5..447930ffe07b3f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -10,6 +10,7 @@
 
 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/Threading.h"
 
@@ -209,17 +210,14 @@ GetFileByIndex(const llvm::DWARFDebugLine::Prologue &prologue, size_t idx,
   return std::move(rel_path);
 }
 
-static FileSpecList
-ParseSupportFilesFromPrologue(const lldb::ModuleSP &module,
-                              const llvm::DWARFDebugLine::Prologue &prologue,
-                              FileSpec::Style style,
-                              llvm::StringRef compile_dir = {}) {
-  FileSpecList support_files;
-
+static void ParseSupportFilesFromPrologue(
+    SupportFileList &support_files, const lldb::ModuleSP &module,
+    const llvm::DWARFDebugLine::Prologue &prologue, FileSpec::Style style,
+    llvm::StringRef compile_dir = {}) {
   // Handle the case where there are no files first to avoid having to special
   // case this later.
   if (prologue.FileNames.empty())
-    return support_files;
+    return;
 
   // Before DWARF v5, the line table indexes were one based.
   const bool is_one_based = prologue.getVersion() < 5;
@@ -235,6 +233,53 @@ ParseSupportFilesFromPrologue(const lldb::ModuleSP &module,
   for (size_t idx = first_file_idx; idx <= last_file_idx; ++idx) {
     std::string remapped_file;
     if (auto file_path = GetFileByIndex(prologue, idx, compile_dir, style)) {
+      auto entry = prologue.getFileNameEntry(idx);
+      auto source = entry.Source.getAsCString();
+      if (!source)
+        consumeError(source.takeError());
+      else {
+        llvm::StringRef source_ref(*source);
+        if (!source_ref.empty()) {
+          /// Wrap a path for an in-DWARF source file. Lazily write it
+          /// to disk when Materialize() is called.
+          struct LazyDWARFSourceFile : public SupportFile {
+            LazyDWARFSourceFile(const FileSpec &fs, llvm::StringRef source,
+                                FileSpec::Style style)
+                : SupportFile(fs), source(source), style(style) {}
+            FileSpec tmp_file;
+            /// The file contents buffer.
+            llvm::StringRef source;
+            /// Deletes the temporary file at the end.
+            std::unique_ptr<llvm::FileRemover> remover;
+            FileSpec::Style style;
+
+            /// Write the file contents to a temporary file.
+            const FileSpec &Materialize() override {
+              if (tmp_file)
+                return tmp_file;
+              llvm::SmallString<0> name;
+              int fd;
+              auto orig_name = m_file_spec.GetFilename().GetStringRef();
+              auto ec = llvm::sys::fs::createTemporaryFile(
+                  "", llvm::sys::path::filename(orig_name, style), fd, name);
+              if (ec || fd <= 0) {
+                LLDB_LOG(GetLog(DWARFLog::DebugInfo),
+                         "Could not create temporary file");
+                return tmp_file;
+              }
+              remover = std::make_unique<llvm::FileRemover>(name);
+              NativeFile file(fd, File::eOpenOptionWriteOnly, true);
+              size_t num_bytes = source.size();
+              file.Write(source.data(), num_bytes);
+              tmp_file.SetPath(name);
+              return tmp_file;
+            }
+          };
+          support_files.Append(std::make_unique<LazyDWARFSourceFile>(
+              FileSpec(*file_path), *source, style));
+          continue;
+        }
+      }
       if (auto remapped = module->RemapSourceFile(llvm::StringRef(*file_path)))
         remapped_file = *remapped;
       else
@@ -251,8 +296,6 @@ ParseSupportFilesFromPrologue(const lldb::ModuleSP &module,
     // Unconditionally add an entry, so the indices match up.
     support_files.EmplaceBack(remapped_file, style, checksum);
   }
-
-  return support_files;
 }
 
 void SymbolFileDWARF::Initialize() {
@@ -744,12 +787,13 @@ lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) {
       ModuleSP module_sp(m_objfile_sp->GetModule());
       if (module_sp) {
         auto initialize_cu = [&](const FileSpec &file_spec,
-                                 LanguageType cu_language) {
+                                 LanguageType cu_language,
+                                 SupportFileList &&support_files = {}) {
           BuildCuTranslationTable();
           cu_sp = std::make_shared<CompileUnit>(
               module_sp, &dwarf_cu, file_spec,
               *GetDWARFUnitIndex(dwarf_cu.GetID()), cu_language,
-              eLazyBoolCalculate);
+              eLazyBoolCalculate, std::move(support_files));
 
           dwarf_cu.SetUserData(cu_sp.get());
 
@@ -775,15 +819,13 @@ lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) {
           // file is also the name of the compile unit. This
           // allows us to avoid loading the non-skeleton unit,
           // which may be in a separate DWO file.
-          FileSpecList support_files;
+          SupportFileList support_files;
           if (!ParseSupportFiles(dwarf_cu, module_sp, support_files))
             return false;
           if (support_files.GetSize() == 0)
             return false;
-
           initialize_cu(support_files.GetFileSpecAtIndex(0),
-                        eLanguageTypeUnknown);
-          cu_sp->SetSupportFiles(std::move(support_files));
+                        eLanguageTypeUnknown, std::move(support_files));
           return true;
         };
 
@@ -1029,7 +1071,7 @@ bool SymbolFileDWARF::ForEachExternalModule(
 }
 
 bool SymbolFileDWARF::ParseSupportFiles(CompileUnit &comp_unit,
-                                        FileSpecList &support_files) {
+                                        SupportFileList &support_files) {
   std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
   DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
   if (!dwarf_cu)
@@ -1038,13 +1080,12 @@ bool SymbolFileDWARF::ParseSupportFiles(CompileUnit &comp_unit,
   if (!ParseSupportFiles(*dwarf_cu, comp_unit.GetModule(), support_files))
     return false;
 
-  comp_unit.SetSupportFiles(support_files);
   return true;
 }
 
 bool SymbolFileDWARF::ParseSupportFiles(DWARFUnit &dwarf_cu,
                                         const ModuleSP &module,
-                                        FileSpecList &support_files) {
+                                        SupportFileList &support_files) {
 
   dw_offset_t offset = dwarf_cu.GetLineTableOffset();
   if (offset == DW_INVALID_OFFSET)
@@ -1057,8 +1098,8 @@ bool SymbolFileDWARF::ParseSupportFiles(DWARFUnit &dwarf_cu,
     return false;
 
   std::string comp_dir = dwarf_cu.GetCompilationDirectory().GetPath();
-  support_files = ParseSupportFilesFromPrologue(
-      module, prologue, dwarf_cu.GetPathStyle(), comp_dir);
+  ParseSupportFilesFromPrologue(support_files, module, prologue,
+                                dwarf_cu.GetPathStyle(), comp_dir);
   return true;
 }
 
@@ -1070,24 +1111,27 @@ FileSpec SymbolFileDWARF::GetFile(DWARFUnit &unit, size_t file_idx) {
   }
 
   auto &tu = llvm::cast<DWARFTypeUnit>(unit);
-  return GetTypeUnitSupportFiles(tu).GetFileSpecAtIndex(file_idx);
+  if (const SupportFileList *support_files = GetTypeUnitSupportFiles(tu))
+    return support_files->GetFileSpecAtIndex(file_idx);
+  return {};
 }
 
-const FileSpecList &
+const SupportFileList *
 SymbolFileDWARF::GetTypeUnitSupportFiles(DWARFTypeUnit &tu) {
-  static FileSpecList empty_list;
+  static SupportFileList empty_list;
 
   dw_offset_t offset = tu.GetLineTableOffset();
   if (offset == DW_INVALID_OFFSET ||
       offset == llvm::DenseMapInfo<dw_offset_t>::getEmptyKey() ||
       offset == llvm::DenseMapInfo<dw_offset_t>::getTombstoneKey())
-    return empty_list;
+    return nullptr;
 
   // Many type units can share a line table, so parse the support file list
   // once, and cache it based on the offset field.
   auto iter_bool = m_type_unit_support_files.try_emplace(offset);
-  FileSpecList &list = iter_bool.first->second;
+  std::unique_ptr<SupportFileList> &list = iter_bool.first->second;
   if (iter_bool.second) {
+    list = std::make_unique<SupportFileList>();
     uint64_t line_table_offset = offset;
     llvm::DWARFDataExtractor data =
         m_context.getOrLoadLineData().GetAsLLVMDWARF();
@@ -1101,14 +1145,13 @@ SymbolFileDWARF::GetTypeUnitSupportFiles(DWARFTypeUnit &tu) {
     };
     ElapsedTime elapsed(m_parse_time);
     llvm::Error error = prologue.parse(data, &line_table_offset, report, ctx);
-    if (error) {
+    if (error)
       report(std::move(error));
-    } else {
-      list = ParseSupportFilesFromPrologue(GetObjectFile()->GetModule(),
-                                           prologue, tu.GetPathStyle());
-    }
+    else
+      ParseSupportFilesFromPrologue(*list, GetObjectFile()->GetModule(),
+                                    prologue, tu.GetPathStyle());
   }
-  return list;
+  return list.get();
 }
 
 bool SymbolFileDWARF::ParseIsOptimized(CompileUnit &comp_unit) {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 78819edd0062bf..26a9502f90aa00 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -123,7 +123,7 @@ class SymbolFileDWARF : public SymbolFileCommon {
                              llvm::function_ref<bool(Module &)>) override;
 
   bool ParseSupportFiles(CompileUnit &comp_unit,
-                         FileSpecList &support_files) override;
+                         SupportFileList &support_files) override;
 
   bool ParseIsOptimized(CompileUnit &comp_unit) override;
 
@@ -396,7 +396,7 @@ class SymbolFileDWARF : public SymbolFileCommon {
                          bool *type_is_new);
 
   bool ParseSupportFiles(DWARFUnit &dwarf_cu, const lldb::ModuleSP &module,
-                         FileSpecList &support_files);
+                         SupportFileList &support_files);
 
   lldb::VariableSP ParseVariableDIE(const SymbolContext &sc,
                                     const DWARFDIE &die,
@@ -489,7 +489,7 @@ class SymbolFileDWARF : public SymbolFileCommon {
 
   void FindDwpSymbolFile();
 
-  const FileSpecList &GetTypeUnitSupportFiles(DWARFTypeUnit &tu);
+  const SupportFileList *GetTypeUnitSupportFiles(DWARFTypeUnit &tu);
 
   void InitializeFirstCodeAddressRecursive(const SectionList &section_list);
 
@@ -529,7 +529,8 @@ class SymbolFileDWARF : public SymbolFileCommon {
   DIEToVariableSP m_die_to_variable_sp;
   DIEToCompilerType m_forward_decl_die_to_compiler_type;
   CompilerTypeToDIE m_forward_decl_compiler_type_to_die;
-  llvm::DenseMap<dw_offset_t, FileSpecList> m_type_unit_support_files;
+  llvm::DenseMap<dw_offset_t, std::unique_ptr<SupportFileList>>
+      m_type_unit_support_files;
   std::vector<uint32_t> m_lldb_cu_to_dwarf_unit;
   /// DWARF does not provide a good way for traditional (concatenating) linkers
   /// to invalidate debug info describing dead-stripped code. These linkers will
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
index e5b59460cb85d5..9094a5e21e690c 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -725,8 +725,8 @@ bool SymbolFileDWARFDebugMap::ForEachExternalModule(
   return false;
 }
 
-bool SymbolFileDWARFDebugMap::ParseSupportFiles(CompileUnit &comp_unit,
-                                                FileSpecList &support_files) {
+bool SymbolFileDWARFDebugMap::ParseSupportFiles(
+    CompileUnit &comp_unit, SupportFileList &support_files) {
   std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
   SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
   if (oso_dwarf)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
index cd0a4bb6e41c2f..d639ee500080d5 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -74,7 +74,7 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon {
                              llvm::function_ref<bool(Module &)>) override;
 
   bool ParseSupportFiles(CompileUnit &comp_unit,
-                         FileSpecList &support_files) override;
+                         SupportFileList &support_files) override;
 
   bool ParseIsOptimized(CompileUnit &comp_unit) override;
 
diff --git a/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h b/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h
index 4dd0d65da46583..3dd33b3dc82fba 100644
--- a/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h
+++ b/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h
@@ -59,7 +59,7 @@ class SymbolFileJSON : public lldb_private::SymbolFileCommon {
   bool ParseDebugMacros(CompileUnit &comp_unit) override { return false; }
 
   bool ParseSupportFiles(CompileUnit &comp_unit,
-                         FileSpecList &support_files) override {
+                         SupportFileList &support_files) override {
     return false;
   }
 
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index ad08013399369e..8375010ae3dedd 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -1369,7 +1369,7 @@ SymbolFileNativePDB::GetFileIndex(const CompilandIndexItem &cii,
 }
 
 bool SymbolFileNativePDB::ParseSupportFiles(CompileUnit &comp_unit,
-                                            FileSpecList &support_files) {
+                                            SupportFileList &support_files) {
   std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
   PdbSymUid cu_id(comp_unit.GetID());
   lldbassert(cu_id.kind() == PdbSymUidKind::Compiland);
@@ -1416,7 +1416,7 @@ void SymbolFileNativePDB::ParseInlineSite(PdbCompilandSymId id,
     return;
   InlineeSourceLine inlinee_line = iter->second;
 
-  const FileSpecList &files = comp_unit->GetSupportFiles();
+  const SupportFileList &files = comp_unit->GetSupportFiles();
   FileSpec decl_file;
   llvm::Expected<uint32_t> file_index_or_err =
       GetFileIndex(*cii, inlinee_line.Header->FileID);
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
index 9d0458cf7ebfeb..82577771f355c8 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -94,7 +94,7 @@ class SymbolFileNativePDB : public SymbolFileCommon {
   bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
 
   bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
-                         FileSpecList &support_files) override;
+                         SupportFileList &support_files) override;
   size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
 
   bool ParseImportedModules(
diff --git a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
index 9e1cd836066021..b26beecc6d1263 100644
--- a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
@@ -365,7 +365,7 @@ bool SymbolFilePDB::ParseDebugMacros(CompileUnit &comp_unit) {
 }
 
 bool SymbolFilePDB::ParseSupportFiles(
-    CompileUnit &comp_unit, lldb_private::FileSpecList &support_files) {
+    CompileUnit &comp_unit, lldb_private::SupportFileList &support_files) {
 
   // In theory this is unnecessary work for us, because all of this information
   // is easily (and quickly) accessible from DebugInfoPDB, so caching it a
diff --git a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
index 01851f1418f3a8..ea495c575f1f1a 100644
--- a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
+++ b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
@@ -70,7 +70,7 @@ class SymbolFilePDB : public lldb_private::SymbolFileCommon {
   bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
 
   bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
-                         lldb_private::FileSpecList &support_files) override;
+                         lldb_private::SupportFileList &support_files) override;
 
   size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
 
diff --git a/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
index 6e4c6439974e95..8c17017442b1f8 100644
--- a/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
+++ b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
@@ -211,7 +211,7 @@ bool SymbolFileSymtab::ParseDebugMacros(CompileUnit &comp_unit) {
 }
 
 bool SymbolFileSymtab::ParseSupportFiles(CompileUnit &comp_unit,
-                                         FileSpecList &support_files) {
+                                         SupportFileList &support_files) {
   return false;
 }
 
diff --git a/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
index 1bbc4de9c9425b..a36311525334e4 100644
--- a/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
+++ b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
@@ -57,7 +57,7 @@ class SymbolFileSymtab : public lldb_private::SymbolFileCommon {
   bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
 
   bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
-                         lldb_private::FileSpecList &support_files) override;
+                         lldb_private::SupportFileList &support_files) override;
 
   size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
 
diff --git a/lldb/source/Symbol/CompileUnit.cpp b/lldb/source/Symbol/CompileUnit.cpp
index c9796973940a24..a6b6c8e57eec01 100644
--- a/lldb/source/Symbol/CompileUnit.cpp
+++ b/lldb/source/Symbol/CompileUnit.cpp
@@ -28,10 +28,11 @@ CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
 CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
                          const FileSpec &fspec, const lldb::user_id_t cu_sym_id,
                          lldb::LanguageType language,
-                         lldb_private::LazyBool is_optimized)
+                         lldb_private::LazyBool is_optimized,
+                         SupportFileList &&support_files)
     : ModuleChild(module_sp), UserID(cu_sym_id), m_user_data(user_data),
       m_language(language), m_flags(0), m_file_spec(fspec),
-      m_is_optimized(is_optimized) {
+      m_support_files(std::move(support_files)), m_is_optimized(is_optimized) {
   if (language != eLanguageTypeUnknown)
     m_flags.Set(flagsParsedLanguage);
   assert(module_sp);
@@ -178,10 +179,6 @@ void CompileUnit::SetLineTable(LineTable *line_table) {
   m_line_table_up.reset(line_table);
 }
 
-void CompileUnit::SetSupportFiles(FileSpecList support_files) {
-  m_support_files = std::move(support_files);
-}
-
 DebugMacros *CompileUnit::GetDebugMacros() {
   if (m_debug_macros_sp.get() == nullptr) {
     if (m_flags.IsClear(flagsParsedDebugMacros)) {
@@ -213,7 +210,7 @@ VariableListSP CompileUnit::GetVariableList(bool can_create) {
   return m_variables;
 }
 
-std::vector<uint32_t> FindFileIndexes(const FileSpecList &files,
+std::vector<uint32_t> FindFileIndexes(const SupportFileList &files,
                                       const FileSpec &file) {
   std::vector<uint32_t> result;
   uint32_t idx = -1;
@@ -411,7 +408,7 @@ bool CompileUnit::ForEachExternalModule(
   return false;
 }
 
-const FileSpecList &CompileUnit::GetSupportFiles() {
+const SupportFileList &CompileUnit::GetSupportFiles() {
   if (m_support_files.GetSize() == 0) {
     if (m_flags.IsClear(flagsParsedSupportFiles)) {
       m_flags.Set(flagsParsedSupportFiles);
diff --git a/lldb/source/Symbol/SymbolFileOnDemand.cpp b/lldb/source/Symbol/SymbolFileOnDemand.cpp
index 33995252bfe2c9..bdb1951d51259d 100644
--- a/lldb/source/Symbol/SymbolFileOnDemand.cpp
+++ b/lldb/source/Symbol/SymbolFileOnDemand.cpp
@@ -115,7 +115,7 @@ bool SymbolFileOnDemand::ForEachExternalModule(
 }
 
 bool SymbolFileOnDemand::ParseSupportFiles(CompileUnit &comp_unit,
-                                           FileSpecList &support_files) {
+                                           SupportFileList &support_files) {
   LLDB_LOG(GetLog(),
            "[{0}] {1} is not skipped: explicitly allowed to support breakpoint",
            GetSymbolFileName(), __FUNCTION__);
diff --git a/lldb/source/Utility/FileSpecList.cpp b/lldb/source/Utility/FileSpecList.cpp
index e3d8ea650c75dc..8d2cf81efe5b13 100644
--- a/lldb/source/Utility/FileSpecList.cpp
+++ b/lldb/source/Utility/FileSpecList.cpp
@@ -37,6 +37,19 @@ bool FileSpecList::AppendIfUnique(const FileSpec &file_spec) {
   return false;
 }
 
+// FIXME: Replace this with a DenseSet at the call site. It is inefficient.
+bool SupportFileList::AppendIfUnique(const FileSpec &file_spec) {
+  collection::iterator end = m_files.end();
+  if (find_if(m_files.begin(), end,
+              [&](const std::unique_ptr<SupportFile> &support_file) {
+                return support_file->GetSpecOnly() == file_spec;
+              }) == end) {
+    Append(file_spec);
+    return true;
+  }
+  return false;
+}
+
 // Clears the file list.
 void FileSpecList::Clear() { m_files.clear(); }
 
@@ -55,22 +68,22 @@ void FileSpecList::Dump(Stream *s, const char *separator_cstr) const {
 //
 // Returns the valid index of the file that matches "file_spec" if it is found,
 // else std::numeric_limits<uint32_t>::max() is returned.
-size_t FileSpecList::FindFileIndex(size_t start_idx, const FileSpec &file_spec,
-                                   bool full) const {
-  const size_t num_files = m_files.size();
-
+static size_t FindFileIndex(size_t start_idx, const FileSpec &file_spec,
+                            bool full, size_t num_files,
+                            std::function<const FileSpec &(size_t)> get_ith) {
   // When looking for files, we will compare only the filename if the FILE_SPEC
   // argument is empty
   bool compare_filename_only = file_spec.GetDirectory().IsEmpty();
 
   for (size_t idx = start_idx; idx < num_files; ++idx) {
+    const FileSpec &ith = get_ith(idx);
     if (compare_filename_only) {
-      if (ConstString::Equals(
-              m_files[idx].GetFilename(), file_spec.GetFilename(),
-              file_spec.IsCaseSensitive() || m_files[idx].IsCaseSensitive()))
+      if (ConstString::Equals(ith.GetFilename(), file_spec.GetFilename(),
+                              file_spec.IsCaseSensitive() ||
+                                  ith.IsCaseSensitive()))
         return idx;
     } else {
-      if (FileSpec::Equal(m_files[idx], file_spec, full))
+      if (FileSpec::Equal(ith, file_spec, full))
         return idx;
     }
   }
@@ -79,8 +92,24 @@ 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 {
+size_t FileSpecList::FindFileIndex(size_t start_idx, const FileSpec &file_spec,
+                                   bool full) const {
+  return ::FindFileIndex(
+      start_idx, file_spec, full, m_files.size(),
+      [&](size_t idx) -> const FileSpec & { return m_files[idx]; });
+}
+
+size_t SupportFileList::FindFileIndex(size_t start_idx,
+                                      const FileSpec &file_spec,
+                                      bool full) const {
+  return ::FindFileIndex(start_idx, file_spec, full, m_files.size(),
+                         [&](size_t idx) -> const FileSpec & {
+                           return m_files[idx]->GetSpecOnly();
+                         });
+}
+
+size_t SupportFileList::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;
@@ -92,7 +121,7 @@ size_t FileSpecList::FindCompatibleIndex(size_t start_idx,
   const bool full = !file_spec.GetDirectory().IsEmpty();
 
   for (size_t idx = start_idx; idx < num_files; ++idx) {
-    const FileSpec &curr_file = m_files[idx];
+    const FileSpec &curr_file = m_files[idx]->GetSpecOnly();
 
     // Always start by matching the filename first
     if (!curr_file.FileEquals(file_spec))
@@ -140,6 +169,13 @@ const FileSpec &FileSpecList::GetFileSpecAtIndex(size_t idx) const {
   return g_empty_file_spec;
 }
 
+const FileSpec &SupportFileList::GetFileSpecAtIndex(size_t idx) const {
+  if (idx < m_files.size())
+    return m_files[idx]->Materialize();
+  static FileSpec g_empty_file_spec;
+  return g_empty_file_spec;
+}
+
 // Return the size in bytes that this object takes in memory. This returns the
 // size in bytes of this object's member variables and any FileSpec objects its
 // member variables contain, the result doesn't not include the string values
diff --git a/lldb/test/API/functionalities/inline-sourcefile/Makefile b/lldb/test/API/functionalities/inline-sourcefile/Makefile
new file mode 100644
index 00000000000000..adb29d3a88e26c
--- /dev/null
+++ b/lldb/test/API/functionalities/inline-sourcefile/Makefile
@@ -0,0 +1,11 @@
+C_SOURCES := main.c
+CFLAGS_EXTRAS := -gdwarf-5
+
+include Makefile.rules
+
+OBJECTS += inline.o
+
+$(EXE): main.c inline.o
+
+%.o: %.ll
+	$(CC) $< -c -o $@
diff --git a/lldb/test/API/functionalities/inline-sourcefile/TestInlineSourceFiles.py b/lldb/test/API/functionalities/inline-sourcefile/TestInlineSourceFiles.py
new file mode 100644
index 00000000000000..6daf970b895b0f
--- /dev/null
+++ b/lldb/test/API/functionalities/inline-sourcefile/TestInlineSourceFiles.py
@@ -0,0 +1,17 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbplatform
+from lldbsuite.test import lldbutil
+
+
+class InlineSourceFilesTestCase(TestBase):
+    @skipIf(compiler="gcc")
+    @skipIf(compiler="clang", compiler_version=["<", "18.0"])
+    def test(self):
+        """Test DWARF inline source files."""
+        self.build()
+        #target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+        #    self, 'break here', lldb.SBFileSpec('inlined.c'))
+        target, process, thread, bkpt = lldbutil.run_to_name_breakpoint(
+            self, 'f')
diff --git a/lldb/test/API/functionalities/inline-sourcefile/inline.ll b/lldb/test/API/functionalities/inline-sourcefile/inline.ll
new file mode 100644
index 00000000000000..56194e45b81387
--- /dev/null
+++ b/lldb/test/API/functionalities/inline-sourcefile/inline.ll
@@ -0,0 +1,39 @@
+; ModuleID = '/tmp/t.c'
+source_filename = "/tmp/t.c"
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+
+; Function Attrs: noinline nounwind optnone ssp uwtable(sync)
+define void @f() #0 !dbg !9 {
+entry:
+  call void @stop(), !dbg !13
+  ret void, !dbg !14
+}
+
+declare void @stop(...) #1
+
+attributes #0 = { noinline nounwind optnone ssp uwtable(sync) }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
+!llvm.ident = !{!8}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 18.0.0git (git at github.com:llvm/llvm-project.git 29ee66f4a0967e43a035f147c960743c7b640f2f)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+!1 = !DIFile(filename: "/INLINE/inlined.c", directory: "/Volumes/Data/llvm-project", checksumkind: CSK_MD5, checksum: "3183154a5cb31debe9a8e27ca500bc3c")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"uwtable", i32 1}
+!7 = !{i32 7, !"frame-pointer", i32 1}
+!8 = !{!"clang version 18.0.0git (git at github.com:llvm/llvm-project.git 29ee66f4a0967e43a035f147c960743c7b640f2f)"}
+!9 = distinct !DISubprogram(name: "f", scope: !10, file: !10, line: 2, type: !11, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0)
+!10 = !DIFile(filename: "/INLINE/inlined.c", directory: "", source: "void stop();
+void f() {
+  // This is inline source code.
+  stop(); // break here
+}
+")
+!11 = !DISubroutineType(types: !12)
+!12 = !{null}
+!13 = !DILocation(line: 4, column: 3, scope: !9)
+!14 = !DILocation(line: 5, column: 1, scope: !9)
diff --git a/lldb/test/API/functionalities/inline-sourcefile/main.c b/lldb/test/API/functionalities/inline-sourcefile/main.c
new file mode 100644
index 00000000000000..c030d7773fa70a
--- /dev/null
+++ b/lldb/test/API/functionalities/inline-sourcefile/main.c
@@ -0,0 +1,7 @@
+void f();
+void stop() {}
+
+int main(int argc, char const *argv[]) {
+  f();
+  return 0;
+}
diff --git a/lldb/unittests/Core/FileSpecListTest.cpp b/lldb/unittests/Core/FileSpecListTest.cpp
index d65e7cd2d0586b..e63f4a00bc3a94 100644
--- a/lldb/unittests/Core/FileSpecListTest.cpp
+++ b/lldb/unittests/Core/FileSpecListTest.cpp
@@ -20,7 +20,7 @@ static FileSpec WindowsSpec(llvm::StringRef path) {
   return FileSpec(path, FileSpec::Style::windows);
 }
 
-TEST(FileSpecListTest, RelativePathMatchesPosix) {
+TEST(SupportFileListTest, RelativePathMatchesPosix) {
 
   const FileSpec fullpath = PosixSpec("/build/src/main.cpp");
   const FileSpec relative = PosixSpec("./src/main.cpp");
@@ -32,7 +32,7 @@ TEST(FileSpecListTest, RelativePathMatchesPosix) {
   const FileSpec rel2_wrong = PosixSpec("asrc/main.cpp");
   const FileSpec rel3_wrong = PosixSpec("rc/main.cpp");
 
-  FileSpecList files;
+  SupportFileList files;
   files.Append(fullpath);
   files.Append(relative);
   files.Append(basename);
@@ -72,7 +72,7 @@ TEST(FileSpecListTest, RelativePathMatchesPosix) {
   EXPECT_EQ((size_t)6, files.FindCompatibleIndex(3, rel3_wrong));
 }
 
-TEST(FileSpecListTest, RelativePathMatchesWindows) {
+TEST(SupportFileListTest, RelativePathMatchesWindows) {
 
   const FileSpec fullpath = WindowsSpec(R"(C:\build\src\main.cpp)");
   const FileSpec relative = WindowsSpec(R"(.\src\main.cpp)");
@@ -84,7 +84,7 @@ TEST(FileSpecListTest, RelativePathMatchesWindows) {
   const FileSpec rel2_wrong = WindowsSpec(R"(asrc\main.cpp)");
   const FileSpec rel3_wrong = WindowsSpec(R"("rc\main.cpp)");
 
-  FileSpecList files;
+  SupportFileList files;
   files.Append(fullpath);
   files.Append(relative);
   files.Append(basename);
diff --git a/lldb/unittests/Expression/CppModuleConfigurationTest.cpp b/lldb/unittests/Expression/CppModuleConfigurationTest.cpp
index 017a0748c2243c..c01e1c1c70d34d 100644
--- a/lldb/unittests/Expression/CppModuleConfigurationTest.cpp
+++ b/lldb/unittests/Expression/CppModuleConfigurationTest.cpp
@@ -37,10 +37,10 @@ struct CppModuleConfigurationTest : public testing::Test {
   }
 
   /// Utility function turning a list of paths into a FileSpecList.
-  FileSpecList makeFiles(llvm::ArrayRef<std::string> paths) {
-    FileSpecList result;
+  std::unique_ptr<SupportFileList> makeFiles(llvm::ArrayRef<std::string> paths) {
+    auto result = std::make_unique<SupportFileList>();
     for (const std::string &path : paths) {
-      result.Append(FileSpec(path, FileSpec::Style::posix));
+      result->Append(FileSpec(path, FileSpec::Style::posix));
       if (!m_fs->addFileNoOwn(path, static_cast<time_t>(0), m_empty_buffer))
         llvm_unreachable("Invalid test configuration?");
     }
@@ -67,7 +67,8 @@ TEST_F(CppModuleConfigurationTest, Linux) {
                                     // C++ library
                                     libcpp + "/vector",
                                     libcpp + "/module.modulemap"};
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
   EXPECT_THAT(config.GetIncludeDirs(),
               testing::ElementsAre(libcpp, ResourceInc(), usr));
@@ -85,7 +86,8 @@ TEST_F(CppModuleConfigurationTest, LinuxTargetSpecificInclude) {
       usr + "/stdio.h", usr_target + "/sys/cdefs.h",
       // C++ library
       libcpp + "/vector", libcpp + "/module.modulemap"};
-  CppModuleConfiguration config(makeFiles(files),
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files,
                                 llvm::Triple("x86_64-unknown-linux-gnu"));
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
   EXPECT_THAT(config.GetIncludeDirs(),
@@ -103,7 +105,8 @@ TEST_F(CppModuleConfigurationTest, Sysroot) {
                                     // C++ library
                                     libcpp + "/vector",
                                     libcpp + "/module.modulemap"};
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
   EXPECT_THAT(config.GetIncludeDirs(),
               testing::ElementsAre(libcpp, ResourceInc(), usr));
@@ -119,7 +122,8 @@ TEST_F(CppModuleConfigurationTest, LinuxLocalLibCpp) {
                                     // C++ library
                                     libcpp + "/vector",
                                     libcpp + "/module.modulemap"};
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
   EXPECT_THAT(config.GetIncludeDirs(),
               testing::ElementsAre(libcpp, ResourceInc(), usr));
@@ -137,7 +141,8 @@ TEST_F(CppModuleConfigurationTest, UnrelatedLibrary) {
                                     // C++ library
                                     libcpp + "/vector",
                                     libcpp + "/module.modulemap"};
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
   EXPECT_THAT(config.GetIncludeDirs(),
               testing::ElementsAre(libcpp, ResourceInc(), usr));
@@ -157,7 +162,8 @@ TEST_F(CppModuleConfigurationTest, UnrelatedLibraryWithTargetSpecificInclude) {
                                     // C++ library
                                     libcpp + "/vector",
                                     libcpp + "/module.modulemap"};
-  CppModuleConfiguration config(makeFiles(files),
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files,
                                 llvm::Triple("x86_64-unknown-linux-gnu"));
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
   EXPECT_THAT(config.GetIncludeDirs(),
@@ -178,7 +184,8 @@ TEST_F(CppModuleConfigurationTest, Xcode) {
       libcpp + "/vector",
       libcpp + "/module.modulemap",
   };
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
   EXPECT_THAT(config.GetIncludeDirs(),
               testing::ElementsAre(libcpp, ResourceInc(), usr));
@@ -193,7 +200,8 @@ TEST_F(CppModuleConfigurationTest, LibCppV2) {
                                     // C++ library
                                     libcpp + "/vector",
                                     libcpp + "/module.modulemap"};
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
   EXPECT_THAT(config.GetIncludeDirs(),
               testing::ElementsAre("/usr/include/c++/v2", ResourceInc(),
@@ -211,7 +219,8 @@ TEST_F(CppModuleConfigurationTest, UnknownLibCppFile) {
                                     libcpp + "/non_existing_file",
                                     libcpp + "/module.modulemap",
                                     libcpp + "/vector"};
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
   EXPECT_THAT(config.GetIncludeDirs(),
               testing::ElementsAre("/usr/include/c++/v1", ResourceInc(),
@@ -225,7 +234,8 @@ TEST_F(CppModuleConfigurationTest, MissingUsrInclude) {
   std::vector<std::string> files = {// C++ library
                                     libcpp + "/vector",
                                     libcpp + "/module.modulemap"};
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
 }
@@ -238,7 +248,8 @@ TEST_F(CppModuleConfigurationTest, MissingLibCpp) {
       // C library
       usr + "/stdio.h",
   };
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
 }
@@ -253,7 +264,8 @@ TEST_F(CppModuleConfigurationTest, IgnoreLibStdCpp) {
       // C++ library
       usr + "/c++/8.0.1/vector",
   };
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
 }
@@ -274,7 +286,8 @@ TEST_F(CppModuleConfigurationTest, AmbiguousCLib) {
       libcpp + "/vector",
       libcpp + "/module.modulemap",
   };
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
 }
@@ -296,7 +309,8 @@ TEST_F(CppModuleConfigurationTest, AmbiguousLibCpp) {
       libcpp2 + "/vector",
       libcpp2 + "/module.modulemap",
   };
-  CppModuleConfiguration config(makeFiles(files), llvm::Triple());
+  auto support_files = makeFiles(files);
+  CppModuleConfiguration config(*support_files, llvm::Triple());
   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
 }



More information about the lldb-commits mailing list