sgraenitz updated this revision to Diff 167593.
sgraenitz marked 8 inline comments as done.
sgraenitz added a comment.

Address feedback from Adrian and Duncan


https://reviews.llvm.org/D52375

Files:
  include/lldb/Symbol/Symtab.h
  packages/Python/lldbsuite/test/macosx/debug_map/Makefile
  packages/Python/lldbsuite/test/macosx/debug_map/TestDebugMap.py
  packages/Python/lldbsuite/test/macosx/debug_map/cu1.c
  packages/Python/lldbsuite/test/macosx/debug_map/cu1.h
  packages/Python/lldbsuite/test/macosx/debug_map/cu2.c
  packages/Python/lldbsuite/test/macosx/debug_map/cu2.h
  packages/Python/lldbsuite/test/macosx/debug_map/cu3.c
  packages/Python/lldbsuite/test/macosx/debug_map/cu3.h
  packages/Python/lldbsuite/test/macosx/debug_map/main.c
  source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
  source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
  source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
  source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
  source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
  source/Symbol/Symtab.cpp

Index: source/Symbol/Symtab.cpp
===================================================================
--- source/Symbol/Symtab.cpp
+++ source/Symbol/Symtab.cpp
@@ -509,6 +509,16 @@
   return indexes.size() - prev_size;
 }
 
+bool Symtab::HasSymbolWithTypeAndFlags(lldb::SymbolType symbol_type,
+                                       uint32_t flags_value) const {
+  std::lock_guard<std::recursive_mutex> guard(m_mutex);
+  for (const Symbol &symbol : m_symbols)
+    if (symbol.GetType() == symbol_type && symbol.GetFlags() == flags_value)
+      return true;
+
+  return false;
+}
+
 uint32_t Symtab::AppendSymbolIndexesWithType(SymbolType symbol_type,
                                              Debug symbol_debug_type,
                                              Visibility symbol_visibility,
Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
===================================================================
--- source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -131,7 +131,11 @@
   uint32_t GetPluginVersion() override;
 
 protected:
-  enum { kHaveInitializedOSOs = (1 << 0), kNumFlags };
+  enum {
+    kHaveInitializedOSOs = (1 << 0),
+    kHaveCheckedCUInfos = (1 << 1),
+    kNumFlags
+  };
 
   friend class DebugMapModule;
   friend struct DIERef;
@@ -172,13 +176,17 @@
           first_symbol_id(UINT32_MAX), last_symbol_id(UINT32_MAX),
           file_range_map(), file_range_map_valid(false) {}
 
+    bool Init(lldb_private::Symbol *so, lldb_private::Symbol *oso);
     const FileRangeMap &GetFileRangeMap(SymbolFileDWARFDebugMap *exe_symfile);
   };
 
   //------------------------------------------------------------------
   // Protected Member Functions
   //------------------------------------------------------------------
   void InitOSO();
+  bool HasCompileUnits();
+  void ReportErrorInitOSO(lldb_private::Symbol *so_symbol,
+                          lldb_private::Symbol *oso_symbol, uint32_t oso_idx);
 
   static uint32_t GetOSOIndexFromUserID(lldb::user_id_t uid) {
     return (uint32_t)((uid >> 32ull) - 1ull);
@@ -247,6 +255,11 @@
 
   lldb::CompUnitSP GetCompileUnit(SymbolFileDWARF *oso_dwarf);
 
+  lldb::CompUnitSP GetCompileUnitByIndex(SymbolFileDWARF *oso_dwarf,
+                                         uint32_t cu_idx);
+  lldb::CompUnitSP GetCompileUnitByOffset(SymbolFileDWARF *oso_dwarf,
+                                          dw_offset_t cu_offset);
+
   CompileUnitInfo *GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf);
 
   lldb::TypeSP
@@ -297,6 +310,8 @@
   // Member Variables
   //------------------------------------------------------------------
   std::bitset<kNumFlags> m_flags;
+  bool m_has_compile_unit_infos;
+  std::vector<lldb::offset_t> m_oso_compile_unit_offset;
   std::vector<CompileUnitInfo> m_compile_unit_infos;
   std::vector<uint32_t> m_func_indexes; // Sorted by address
   std::vector<uint32_t> m_glob_indexes;
@@ -373,7 +388,7 @@
   LinkOSOLineTable(SymbolFileDWARF *oso_symfile,
                    lldb_private::LineTable *line_table);
 
-  size_t AddOSOARanges(SymbolFileDWARF *dwarf2Data,
+  size_t AddOSOARanges(SymbolFileDWARF *dwarf2Data, dw_offset_t cu_offset,
                        DWARFDebugAranges *debug_aranges);
 };
 
Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
===================================================================
--- source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -37,6 +37,7 @@
 #include "lldb/Symbol/VariableList.h"
 #include "llvm/Support/ScopedPrinter.h"
 
+#include "DWARFCompileUnit.h"
 #include "LogChannelDWARF.h"
 #include "SymbolFileDWARF.h"
 
@@ -169,6 +170,23 @@
   return file_range_map;
 }
 
+bool SymbolFileDWARFDebugMap::CompileUnitInfo::Init(Symbol *so, Symbol *oso) {
+  if (!so || so->GetType() != eSymbolTypeSourceFile)
+    return false;
+
+  if (!oso || oso->GetType() != eSymbolTypeObjectFile)
+    return false;
+
+  if (so->GetSiblingIndex() == UINT32_MAX)
+    return false;
+
+  llvm::StringRef so_file_name = so->GetName().GetStringRef();
+  so_file.SetFile(so_file_name, false, FileSpec::Style::native);
+  oso_mod_time = llvm::sys::toTimePoint(oso->GetIntegerValue(0));
+  oso_path = oso->GetName();
+  return true;
+}
+
 class DebugMapModule : public Module {
 public:
   DebugMapModule(const ModuleSP &exe_module_sp, uint32_t cu_idx,
@@ -251,61 +269,63 @@
 }
 
 SymbolFileDWARFDebugMap::SymbolFileDWARFDebugMap(ObjectFile *ofile)
-    : SymbolFile(ofile), m_flags(), m_compile_unit_infos(), m_func_indexes(),
-      m_glob_indexes(),
+    : SymbolFile(ofile), m_flags(), m_has_compile_unit_infos(false),
+      m_compile_unit_infos(), m_func_indexes(), m_glob_indexes(),
       m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate) {}
 
 SymbolFileDWARFDebugMap::~SymbolFileDWARFDebugMap() {}
 
 void SymbolFileDWARFDebugMap::InitializeObject() {}
 
-void SymbolFileDWARFDebugMap::InitOSO() {
-  if (m_flags.test(kHaveInitializedOSOs))
-    return;
-
-  m_flags.set(kHaveInitializedOSOs);
-
-  // If the object file has been stripped, there is no sense in looking further
-  // as all of the debug symbols for the debug map will not be available
-  if (m_obj_file->IsStripped())
-    return;
-
-  // Also make sure the file type is some sort of executable. Core files, debug
-  // info files (dSYM), object files (.o files), and stub libraries all can
-  switch (m_obj_file->GetType()) {
+static bool IsExecutableType(ObjectFile::Type type) {
+  switch (type) {
   case ObjectFile::eTypeInvalid:
   case ObjectFile::eTypeCoreFile:
   case ObjectFile::eTypeDebugInfo:
   case ObjectFile::eTypeObjectFile:
   case ObjectFile::eTypeStubLibrary:
   case ObjectFile::eTypeUnknown:
   case ObjectFile::eTypeJIT:
-    return;
+    return false;
 
   case ObjectFile::eTypeExecutable:
   case ObjectFile::eTypeDynamicLinker:
   case ObjectFile::eTypeSharedLibrary:
-    break;
+    return true;
   }
+}
 
-  // In order to get the abilities of this plug-in, we look at the list of
-  // N_OSO entries (object files) from the symbol table and make sure that
-  // these files exist and also contain valid DWARF. If we get any of that then
-  // we return the abilities of the first N_OSO's DWARF.
+static uint32_t GetOSOSymbolFlags() {
+  // When a mach-o symbol is encoded, the n_type field is encoded in bits
+  // 23:16, and the n_desc field is encoded in bits 15:0.
+  //
+  // N_OSO object file symbols have a flags value as follows:
+  // bits 23:16 == 0x66 (N_OSO)
+  // bits 15: 0 == 0x0001 (specifies this is a debug map object file)
+  return 0x660001u;
+}
+
+void SymbolFileDWARFDebugMap::InitOSO() {
+  if (m_flags.test(kHaveInitializedOSOs))
+    return;
+
+  m_flags.set(kHaveInitializedOSOs);
+
+  // If the object file has been stripped, there is no sense in looking further
+  // as all of the debug symbols for the debug map will not be available
+  if (m_obj_file->IsStripped())
+    return;
+
+  // Also make sure the file type is some sort of executable.
+  if (!IsExecutableType(m_obj_file->GetType()))
+    return;
 
   Symtab *symtab = m_obj_file->GetSymtab();
   if (symtab) {
     Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP));
 
     std::vector<uint32_t> oso_indexes;
-    // When a mach-o symbol is encoded, the n_type field is encoded in bits
-    // 23:16, and the n_desc field is encoded in bits 15:0.
-    //
-    // To find all N_OSO entries that are part of the DWARF + debug map we find
-    // only object file symbols with the flags value as follows: bits 23:16 ==
-    // 0x66 (N_OSO) bits 15: 0 == 0x0001 (specifies this is a debug map object
-    // file)
-    const uint32_t k_oso_symbol_flags_value = 0x660001u;
+    const uint32_t k_oso_symbol_flags_value = GetOSOSymbolFlags();
 
     const uint32_t oso_index_count =
         symtab->AppendSymbolIndexesWithTypeAndFlagsValue(
@@ -340,67 +360,97 @@
       }
       m_debug_map.Sort();
 
-      m_compile_unit_infos.resize(oso_index_count);
+      assert(m_compile_unit_infos.empty());
+      assert(m_oso_compile_unit_offset.empty());
 
       for (uint32_t i = 0; i < oso_index_count; ++i) {
         const uint32_t so_idx = oso_indexes[i] - 1;
         const uint32_t oso_idx = oso_indexes[i];
-        const Symbol *so_symbol = symtab->SymbolAtIndex(so_idx);
-        const Symbol *oso_symbol = symtab->SymbolAtIndex(oso_idx);
-        if (so_symbol && oso_symbol &&
-            so_symbol->GetType() == eSymbolTypeSourceFile &&
-            oso_symbol->GetType() == eSymbolTypeObjectFile) {
-          m_compile_unit_infos[i].so_file.SetFile(
-              so_symbol->GetName().AsCString(), false, FileSpec::Style::native);
-          m_compile_unit_infos[i].oso_path = oso_symbol->GetName();
-          m_compile_unit_infos[i].oso_mod_time =
-              llvm::sys::toTimePoint(oso_symbol->GetIntegerValue(0));
+        Symbol *so_symbol = symtab->SymbolAtIndex(so_idx);
+        Symbol *oso_symbol = symtab->SymbolAtIndex(oso_idx);
+
+        // oso_info is the basis for the individual cu_infos below.
+        CompileUnitInfo oso_info;
+        if (!oso_info.Init(so_symbol, oso_symbol)) {
+          ReportErrorInitOSO(so_symbol, oso_symbol, oso_idx);
+          continue;
+        }
+
+        SymbolFileDWARF *sym_file = GetSymbolFileByCompUnitInfo(&oso_info);
+        if (!sym_file)
+          continue;
+
+        const auto &debug_info_data = sym_file->get_debug_info_data();
+        auto ExtractNext = [sym_file, &debug_info_data](lldb::offset_t offs) {
+          return DWARFCompileUnit::Extract(sym_file, debug_info_data, &offs);
+        };
+
+        for (DWARFUnitSP cu_sp = ExtractNext(0); cu_sp != nullptr;
+             cu_sp = ExtractNext(cu_sp->GetNextCompileUnitOffset())) {
+          // FIXME: Multiple OSO entries with multiple CUs may be a rare, but
+          // while I am here, I should store CU OFFSETS BY OSO ENTRY! It may
+          // move to m_oso_entry_info.
+          m_oso_compile_unit_offset.push_back(cu_sp->GetOffset());
+
+          // Make a copy of the oso_info and add compile unit specific data.
+          //
+          // FIXME: There is no CU-specific data currently, because symbol
+          // ranges are the same for all CUs and the offset is stored
+          // separately. It may be turned into a m_oso_entry_info.
+          m_compile_unit_infos.push_back(oso_info);
+          CompileUnitInfo &cu_info = m_compile_unit_infos.back();
+
           uint32_t sibling_idx = so_symbol->GetSiblingIndex();
-          // The sibling index can't be less that or equal to the current index
-          // "i"
-          if (sibling_idx == UINT32_MAX) {
-            m_obj_file->GetModule()->ReportError(
-                "N_SO in symbol with UID %u has invalid sibling in debug map, "
-                "please file a bug and attach the binary listed in this error",
-                so_symbol->GetID());
-          } else {
-            const Symbol *last_symbol = symtab->SymbolAtIndex(sibling_idx - 1);
-            m_compile_unit_infos[i].first_symbol_index = so_idx;
-            m_compile_unit_infos[i].last_symbol_index = sibling_idx - 1;
-            m_compile_unit_infos[i].first_symbol_id = so_symbol->GetID();
-            m_compile_unit_infos[i].last_symbol_id = last_symbol->GetID();
-
-            if (log)
-              log->Printf("Initialized OSO 0x%8.8x: file=%s", i,
-                          oso_symbol->GetName().GetCString());
-          }
-        } else {
-          if (oso_symbol == NULL)
-            m_obj_file->GetModule()->ReportError(
-                "N_OSO symbol[%u] can't be found, please file a bug and attach "
-                "the binary listed in this error",
-                oso_idx);
-          else if (so_symbol == NULL)
-            m_obj_file->GetModule()->ReportError(
-                "N_SO not found for N_OSO symbol[%u], please file a bug and "
-                "attach the binary listed in this error",
-                oso_idx);
-          else if (so_symbol->GetType() != eSymbolTypeSourceFile)
-            m_obj_file->GetModule()->ReportError(
-                "N_SO has incorrect symbol type (%u) for N_OSO symbol[%u], "
-                "please file a bug and attach the binary listed in this error",
-                so_symbol->GetType(), oso_idx);
-          else if (oso_symbol->GetType() != eSymbolTypeSourceFile)
-            m_obj_file->GetModule()->ReportError(
-                "N_OSO has incorrect symbol type (%u) for N_OSO symbol[%u], "
-                "please file a bug and attach the binary listed in this error",
-                oso_symbol->GetType(), oso_idx);
+          const Symbol *last_symbol = symtab->SymbolAtIndex(sibling_idx - 1);
+
+          // For (regular) objects with only one compile unit, there is a
+          // bidirectional mapping between a symtab slice and a compile unit.
+          // LTO objects do not allow such a mapping and so we assign the full
+          // symtab range to all compile units of the same oso_entry.
+          cu_info.first_symbol_index = so_idx;
+          cu_info.last_symbol_index = sibling_idx - 1;
+          cu_info.first_symbol_id = so_symbol->GetID();
+          cu_info.last_symbol_id = last_symbol->GetID();
         }
+
+        if (log)
+          log->Printf("Initialized OSO 0x%8.8x: file=%s", i,
+                      oso_symbol->GetName().GetCString());
       }
     }
   }
 }
 
+void SymbolFileDWARFDebugMap::ReportErrorInitOSO(Symbol *so_symbol,
+                                                 Symbol *oso_symbol,
+                                                 uint32_t oso_idx) {
+  if (oso_symbol == NULL)
+    m_obj_file->GetModule()->ReportError(
+        "N_OSO symbol[%u] can't be found, please file a bug and attach "
+        "the binary listed in this error",
+        oso_idx);
+  else if (so_symbol == NULL)
+    m_obj_file->GetModule()->ReportError(
+        "N_SO not found for N_OSO symbol[%u], please file a bug and "
+        "attach the binary listed in this error",
+        oso_idx);
+  else if (so_symbol->GetType() != eSymbolTypeSourceFile)
+    m_obj_file->GetModule()->ReportError(
+        "N_SO has incorrect symbol type (%u) for N_OSO symbol[%u], "
+        "please file a bug and attach the binary listed in this error",
+        so_symbol->GetType(), oso_idx);
+  else if (oso_symbol->GetType() != eSymbolTypeSourceFile)
+    m_obj_file->GetModule()->ReportError(
+        "N_OSO has incorrect symbol type (%u) for N_OSO symbol[%u], "
+        "please file a bug and attach the binary listed in this error",
+        oso_symbol->GetType(), oso_idx);
+  else if (so_symbol->GetSiblingIndex() == UINT32_MAX)
+    m_obj_file->GetModule()->ReportError(
+        "N_SO in symbol with UID %u has invalid sibling in debug map, "
+        "please file a bug and attach the binary listed in this error",
+        so_symbol->GetID());
+}
+
 Module *SymbolFileDWARFDebugMap::GetModuleByOSOIndex(uint32_t oso_idx) {
   const uint32_t cu_count = GetNumCompileUnits();
   if (oso_idx < cu_count)
@@ -547,20 +597,40 @@
   // N_OSO entries (object files) from the symbol table and make sure that
   // these files exist and also contain valid DWARF. If we get any of that then
   // we return the abilities of the first N_OSO's DWARF.
+  if (HasCompileUnits())
+    return SymbolFile::CompileUnits | SymbolFile::Functions |
+           SymbolFile::Blocks | SymbolFile::GlobalVariables |
+           SymbolFile::LocalVariables | SymbolFile::VariableTypes |
+           SymbolFile::LineTables;
 
-  const uint32_t oso_index_count = GetNumCompileUnits();
-  if (oso_index_count > 0) {
-    InitOSO();
-    if (!m_compile_unit_infos.empty()) {
-      return SymbolFile::CompileUnits | SymbolFile::Functions |
-             SymbolFile::Blocks | SymbolFile::GlobalVariables |
-             SymbolFile::LocalVariables | SymbolFile::VariableTypes |
-             SymbolFile::LineTables;
-    }
-  }
   return 0;
 }
 
+bool SymbolFileDWARFDebugMap::HasCompileUnits() {
+  if (m_flags.test(kHaveCheckedCUInfos))
+    return m_has_compile_unit_infos;
+
+  m_flags.set(kHaveCheckedCUInfos);
+
+  // If the object file has been stripped, there is no sense in looking further
+  // as all of the debug symbols for the debug map will not be available.
+  //
+  // FIXME: This should be renamed to IsStrippedOrMissing()
+  if (m_obj_file->IsStripped())
+    return false;
+
+  if (!IsExecutableType(m_obj_file->GetType()))
+    return false;
+
+  assert(m_has_compile_unit_infos == false && "Arrive here only once");
+
+  if (Symtab *symtab = m_obj_file->GetSymtab())
+    m_has_compile_unit_infos = symtab->HasSymbolWithTypeAndFlags(
+        eSymbolTypeObjectFile, GetOSOSymbolFlags());
+
+  return m_has_compile_unit_infos;
+}
+
 uint32_t SymbolFileDWARFDebugMap::GetNumCompileUnits() {
   InitOSO();
   return m_compile_unit_infos.size();
@@ -575,12 +645,10 @@
     if (oso_module) {
       FileSpec so_file_spec;
       if (GetFileSpecForSO(cu_idx, so_file_spec)) {
-        // User zero as the ID to match the compile unit at offset zero in each
-        // .o file since each .o file can only have one compile unit for now.
-        lldb::user_id_t cu_id = 0;
-        m_compile_unit_infos[cu_idx].compile_unit_sp.reset(
-            new CompileUnit(m_obj_file->GetModule(), NULL, so_file_spec, cu_id,
-                            eLanguageTypeUnknown, eLazyBoolCalculate));
+        lldb::user_id_t cu_offset = m_oso_compile_unit_offset[cu_idx];
+        m_compile_unit_infos[cu_idx].compile_unit_sp.reset(new CompileUnit(
+            m_obj_file->GetModule(), NULL, so_file_spec, cu_offset,
+            eLanguageTypeUnknown, eLazyBoolCalculate));
 
         if (m_compile_unit_infos[cu_idx].compile_unit_sp) {
           // Let our symbol vendor know about this compile unit
@@ -734,6 +802,15 @@
       if (sc.symbol != NULL) {
         resolved_flags |= eSymbolContextSymbol;
 
+        // FIXME: Search by symbol Index/ID will NOT return the correct info for
+        // objects with multiple compile units. We cannot safely match slices of
+        // the symtab with individual units (it's very likely that optimization
+        // mixed it up), and so all compile units from such objects share the
+        // full symbol range.
+        //
+        // For the moment this is not a problem here, because we only use the
+        // compile unit info to find the module, which is invariant across all
+        // compile units of one object file.
         uint32_t oso_idx = 0;
         CompileUnitInfo *comp_unit_info =
             GetCompileUnitInfoForSymbolWithID(sc.symbol->GetID(), &oso_idx);
@@ -1244,6 +1321,32 @@
   llvm_unreachable("this shouldn't happen");
 }
 
+lldb::CompUnitSP
+SymbolFileDWARFDebugMap::GetCompileUnitByIndex(SymbolFileDWARF *oso_dwarf,
+                                               uint32_t cu_idx) {
+  assert(cu_idx < GetNumCompileUnits() && "CU index out of range");
+  auto &cu_info = m_compile_unit_infos[cu_idx];
+  if (!cu_info.compile_unit_sp)
+    cu_info.compile_unit_sp = ParseCompileUnitAtIndex(cu_idx);
+
+  return cu_info.compile_unit_sp;
+}
+
+lldb::CompUnitSP
+SymbolFileDWARFDebugMap::GetCompileUnitByOffset(SymbolFileDWARF *oso_dwarf,
+                                                dw_offset_t cu_offset) {
+  auto begin = m_oso_compile_unit_offset.begin();
+  auto end = m_oso_compile_unit_offset.end();
+  auto it = std::lower_bound(begin, end, cu_offset);
+
+  if (it != end) {
+    uint32_t cu_idx = std::distance(begin, it);
+    return GetCompileUnitByIndex(oso_dwarf, cu_idx);
+  }
+
+  return nullptr;
+}
+
 SymbolFileDWARFDebugMap::CompileUnitInfo *
 SymbolFileDWARFDebugMap::GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf) {
   if (oso_dwarf) {
@@ -1380,6 +1483,16 @@
       addr_module->GetSymbolVendor()->GetSymbolFile()));
   if (cu_info) {
     const lldb::addr_t oso_file_addr = addr.GetFileAddress();
+
+    // FIXME: Search by symbol Index/ID will NOT return the correct info for
+    // objects with multiple compile units. We cannot safely match slices of
+    // the symtab with individual units (it's very likely that optimization
+    // mixed it up), and so all compile units from such objects share the
+    // full symbol range.
+    //
+    // For the moment this is not a problem here, because we only use the
+    // compile unit info to find the module, which is invariant across all
+    // compile units of one object file.
     const FileRangeMap::Entry *oso_range_entry =
         cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr);
     if (oso_range_entry) {
@@ -1407,7 +1520,16 @@
 
 size_t
 SymbolFileDWARFDebugMap::AddOSOARanges(SymbolFileDWARF *dwarf2Data,
+                                       dw_offset_t cu_offset,
                                        DWARFDebugAranges *debug_aranges) {
+  // For OSO debug map entries with multiple CUs, we can't map between symtab
+  // entries and CUs. Thus, each CU's FileRangeMap will be populated from the
+  // entire symtab range of that OSO debug map entry. The loop below would add
+  // invalid entries (from other CUs of this OSO debug map entry) and we had to
+  // test each of them in SymbolFileDWARF::ResolveSymbolContext().
+  if (m_compile_unit_infos.size() > 1)
+    return 0;
+
   size_t num_line_entries_added = 0;
   if (debug_aranges && dwarf2Data) {
     CompileUnitInfo *compile_unit_info = GetCompileUnitInfo(dwarf2Data);
@@ -1417,7 +1539,7 @@
       for (size_t idx = 0; idx < file_range_map.GetSize(); idx++) {
         const FileRangeMap::Entry *entry = file_range_map.GetEntryAtIndex(idx);
         if (entry) {
-          debug_aranges->AppendRange(dwarf2Data->GetID(), entry->GetRangeBase(),
+          debug_aranges->AppendRange(cu_offset, entry->GetRangeBase(),
                                      entry->GetRangeEnd());
           num_line_entries_added++;
         }
Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
===================================================================
--- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -275,7 +275,7 @@
 
   lldb_private::CompileUnit *
   GetCompUnitForDWARFCompUnit(DWARFUnit *dwarf_cu,
-                              uint32_t cu_idx = UINT32_MAX);
+                              uint32_t cu_idx = DW_INVALID_INDEX);
 
   virtual size_t GetObjCMethodDIEOffsets(lldb_private::ConstString class_name,
                                          DIEArray &method_die_offsets);
Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
===================================================================
--- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -778,9 +778,25 @@
       if (dwarf_cu->GetSymbolFileDWARF() != this) {
         return dwarf_cu->GetSymbolFileDWARF()->ParseCompileUnit(dwarf_cu,
                                                                 cu_idx);
-      } else if (dwarf_cu->GetOffset() == 0 && GetDebugMapSymfile()) {
+      } else if (SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile()) {
+        assert(debug_map == m_debug_map_symfile &&
+               "debug_map is a nested instance derived from SymbolFile, "
+               "NOT SymbolFileDWARF");
+
         // Let the debug map create the compile unit
-        cu_sp = m_debug_map_symfile->GetCompileUnit(this);
+        if (debug_map->GetNumCompileUnits() == 1) {
+          cu_sp = debug_map->GetCompileUnit(this);
+        } else if (cu_idx != UINT32_MAX) {
+          cu_sp = debug_map->GetCompileUnitByIndex(this, cu_idx);
+        } else {
+          lldb::offset_t cu_offset = dwarf_cu->GetOffset();
+          if (cu_offset != DW_INVALID_OFFSET) {
+            cu_sp = debug_map->GetCompileUnitByOffset(this, cu_offset);
+          } else {
+            llvm_unreachable("Need more info to find the correct CU");
+          }
+        }
+
         dwarf_cu->SetUserData(cu_sp.get());
       } else {
         ModuleSP module_sp(m_obj_file->GetModule());
Index: source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
===================================================================
--- source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
+++ source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
@@ -426,8 +426,17 @@
   const dw_offset_t cu_offset = GetOffset();
   if (die) {
     DWARFRangeList ranges;
+
+    // DW_AT_low/high_pc attributes of DW_TAG_compile_unit are no suitable
+    // fallback for deducing code address ranges. It can have outdated data or
+    // result in overlapping ranges, e.g. if the CUs actual code is interleaved.
+    // Instead BuildAddressRangeTable() below will index the DWARF and generate
+    // the address ranges table manually, based on DW_AT_low/high_pc attributes
+    // of nested DW_TAG_subprogram tags.
+    static constexpr bool check_hi_lo_pc = false;
     const size_t num_ranges =
-        die->GetAttributeAddressRanges(dwarf, this, ranges, false);
+        die->GetAttributeAddressRanges(dwarf, this, ranges, check_hi_lo_pc);
+
     if (num_ranges > 0) {
       // This compile unit has DW_AT_ranges, assume this is correct if it is
       // present since clang no longer makes .debug_aranges by default and it
@@ -477,7 +486,7 @@
           }
         }
       } else
-        debug_map_sym_file->AddOSOARanges(dwarf, debug_aranges);
+        debug_map_sym_file->AddOSOARanges(dwarf, cu_offset, debug_aranges);
     }
   }
 
Index: packages/Python/lldbsuite/test/macosx/debug_map/main.c
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/macosx/debug_map/main.c
@@ -0,0 +1,10 @@
+#include "cu1.h"
+#include "cu2.h"
+#include "cu3.h"
+
+int main(int argc, char **argv) {
+  int cu1_res = cu1_main(argc - 1); // +1
+  int cu2_res = cu2_main(cu1_res); //  +2
+  int cu3_res = cu3_main(cu2_res); //  -3
+  return cu3_res;
+}
Index: packages/Python/lldbsuite/test/macosx/debug_map/cu3.h
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/macosx/debug_map/cu3.h
@@ -0,0 +1 @@
+int cu3_main(int n);
Index: packages/Python/lldbsuite/test/macosx/debug_map/cu3.c
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/macosx/debug_map/cu3.c
@@ -0,0 +1,5 @@
+#include "cu3.h"
+
+int cu3_main(int n) {
+  return n - 3; // Set a breakpoint here
+}
Index: packages/Python/lldbsuite/test/macosx/debug_map/cu2.h
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/macosx/debug_map/cu2.h
@@ -0,0 +1 @@
+int cu2_main(int n);
Index: packages/Python/lldbsuite/test/macosx/debug_map/cu2.c
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/macosx/debug_map/cu2.c
@@ -0,0 +1,5 @@
+#include "cu2.h"
+
+int cu2_main(int n) {
+  return n + 2; // Set a breakpoint here
+}
Index: packages/Python/lldbsuite/test/macosx/debug_map/cu1.h
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/macosx/debug_map/cu1.h
@@ -0,0 +1 @@
+int cu1_main(int n);
Index: packages/Python/lldbsuite/test/macosx/debug_map/cu1.c
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/macosx/debug_map/cu1.c
@@ -0,0 +1,5 @@
+#include "cu1.h"
+
+int cu1_main(int n) {
+  return n + 1; // Set a breakpoint here
+}
Index: packages/Python/lldbsuite/test/macosx/debug_map/TestDebugMap.py
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/macosx/debug_map/TestDebugMap.py
@@ -0,0 +1,118 @@
+"""
+Test breakpoint resolution in DWARF debug map for different LTO and line-tables-only combinations
+"""
+
+from __future__ import print_function
+
+import os
+import time
+import re
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+
+class DebugMapTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    # If your test case doesn't stress debug info, set this to true.
+    # That way it won't be run once for each debug info format.
+    NO_DEBUG_INFO_TESTCASE = False
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+
+    def run_to_bp(self, file_name):
+        file = lldb.SBFileSpec(file_name)
+        line_hint = "Set a breakpoint here"
+        return lldbutil.run_to_source_breakpoint(self, line_hint, file)
+
+    def assertFunctionIs(self, halt, name):
+        # halt = (target, process, thread, bkpt)
+        self.assertTrue(halt[2].GetFrameAtIndex(0).GetFunctionName(), name)
+        self.runCmd("continue")
+
+    def check_all_breakpoints(self):
+        halt1 = self.run_to_bp("cu1.c")
+        self.assertFunctionIs(halt1, "cu1_main")
+
+        halt2 = self.run_to_bp("cu2.c")
+        self.assertFunctionIs(halt2, "cu2_main")
+
+        halt3 = self.run_to_bp("cu3.c")
+        self.assertFunctionIs(halt3, "cu3_main")
+
+    def test_bc(self):
+        """Debug map with full debug info and all CUs in one entry"""
+        self.build(dictionary={ 
+            'CFLAGS': '-flto',
+            'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_bc_lto.o'
+        })
+        self.check_all_breakpoints()
+
+    def test_lt(self):
+        """Debug map with line tables only and one CU per entry"""
+        self.build(dictionary={ 
+            'CFLAGS': '-gline-tables-only',
+            'LD_EXTRAS': '-gline-tables-only'
+        })
+        self.check_all_breakpoints()
+
+    def test_bc12(self):
+        """Debug map with full debug info and order of entries: main.o, test_lt12_lto.o, cu3.o"""
+        # Note: actual order of entries is expected to become: main.o, cu3.o, test_lt12_lto.o
+        self.build(dictionary={ 
+            'CFLAGS_CU1': '-flto',
+            'CFLAGS_CU2': '-flto',
+            'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_lt12_lto.o'
+        })
+        self.check_all_breakpoints()
+
+    def test_bc23(self):
+        """Debug map with full debug info and order of entries: test_lt01_lto.o, cu2.o, cu3.o"""
+        # Note: actual order of entries is expected to become: cu2.o, cu3.o, test_lt01_lto.o
+        self.build(dictionary={ 
+            'CFLAGS_CU1': '-flto',
+            'CFLAGS_MAIN': '-flto',
+            'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_lt01_lto.o'
+        })
+        self.check_all_breakpoints()
+
+    def test_bc12_lt12(self):
+        """Debug map with line tables only in LTO sources"""
+        self.build(dictionary={
+            'CFLAGS_CU1': '-flto -gline-tables-only',
+            'CFLAGS_CU2': '-flto -gline-tables-only',
+            'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_bc12_lt12_lto.o'
+        })
+        self.check_all_breakpoints()
+
+    def test_bc12_lt2(self):
+        """Debug map with line tables only in one of the LTO sources"""
+        self.build(dictionary={
+            'CFLAGS_CU1': '-flto',
+            'CFLAGS_CU2': '-flto -gline-tables-only',
+            'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_bc12_lt2_lto.o'
+        })
+        self.check_all_breakpoints()
+
+    def test_bc12_lt3(self):
+        """Debug map with line tables only in a non-LTO entry"""
+        self.build(dictionary={
+            'CFLAGS_CU1': '-flto',
+            'CFLAGS_CU2': '-flto',
+            'CFLAGS_CU3': '-gline-tables-only',
+            'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_bc12_lt3_lto.o'
+        })
+        self.check_all_breakpoints()
+
+    def test_bc12_stripped(self):
+        """Debug map with missing LTO sources"""
+        self.build(dictionary={
+            'CFLAGS_CU1': '-flto',
+            'CFLAGS_CU2': '-flto',
+            'LD_EXTRAS': '-flto'
+        })
+        only_halt = self.run_to_bp("cu3.c")
+        self.assertFunctionIs(only_halt, "cu3_main")
Index: packages/Python/lldbsuite/test/macosx/debug_map/Makefile
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/macosx/debug_map/Makefile
@@ -0,0 +1,27 @@
+LEVEL = ../../make
+
+# Python script can pass parameters CFLAGS, CFLAGS_CUx, CFLAGS_MAIN and 
+# LD_EXTRAS. For example:
+#
+#     self.build(dictionary={ 
+#       'CFLAGS_CU1': '-flto',
+#       'CFLAGS_CU2': '-flto',
+#       'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_lt12_lto.o'
+#     })
+
+include $(LEVEL)/Makefile.rules
+
+main.o: main.c
+	$(CC) -c $(CFLAGS) $(CFLAGS_MAIN) -o main.o $(SRCDIR)/main.c
+
+cu1.o: cu1.c
+	$(CC) -c $(CFLAGS) $(CFLAGS_CU1) -o cu1.o $(SRCDIR)/cu1.c
+
+cu2.o: cu2.c
+	$(CC) -c $(CFLAGS) $(CFLAGS_CU2) -o cu2.o $(SRCDIR)/cu2.c
+
+cu3.o: cu3.c
+	$(CC) -c $(CFLAGS) $(CFLAGS_CU3) -o cu3.o $(SRCDIR)/cu3.c
+
+a.out: main.o cu1.o cu2.o cu3.o
+	$(LD) main.o cu1.o cu2.o cu3.o -L. $(LDFLAGS) -o a.out
Index: include/lldb/Symbol/Symtab.h
===================================================================
--- include/lldb/Symbol/Symtab.h
+++ include/lldb/Symbol/Symtab.h
@@ -56,6 +56,10 @@
   Symbol *FindSymbolWithType(lldb::SymbolType symbol_type,
                              Debug symbol_debug_type,
                              Visibility symbol_visibility, uint32_t &start_idx);
+
+  bool HasSymbolWithTypeAndFlags(lldb::SymbolType symbol_type,
+                                 uint32_t flags_value) const;
+
   //----------------------------------------------------------------------
   /// Get the parent symbol for the given symbol.
   ///
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to