diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp index 8e258ee0d7bdb..46204ba369ebc 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -76,6 +76,7 @@ void DWARFUnit::ExtractUnitDIEIfNeeded() { return; m_has_parsed_non_skeleton_unit = true; + m_dwo_error.Clear(); if (!m_dwo_id) return; // No DWO file. @@ -87,13 +88,24 @@ void DWARFUnit::ExtractUnitDIEIfNeeded() { DWARFUnit *dwo_cu = dwo_symbol_file->GetDWOCompileUnitForHash(*m_dwo_id); - if (!dwo_cu) + if (!dwo_cu) { + SetDwoError( + Status("unable to load .dwo file from \"%s\" due to ID (0x%16.16" PRIx64 + ") mismatch for skeleton DIE at 0x%8.8" PRIx32, + dwo_symbol_file->GetObjectFile()->GetFileSpec().GetPath().c_str(), + *m_dwo_id, m_first_die.GetOffset())); return; // Can't fetch the compile unit from the dwo file. + } dwo_cu->SetUserData(this); DWARFBaseDIE dwo_cu_die = dwo_cu->GetUnitDIEOnly(); - if (!dwo_cu_die.IsValid()) - return; // Can't fetch the compile unit DIE from the dwo file. + if (!dwo_cu_die.IsValid()) { + // Can't fetch the compile unit DIE from the dwo file. + SetDwoError( + Status("unable to extract compile unit DIE from .dwo file for skeleton " + "DIE at 0x%8.8" PRIx32, m_first_die.GetOffset())); + return; + } // Here for DWO CU we want to use the address base set in the skeleton unit // (DW_AT_addr_base) if it is available and use the DW_AT_GNU_addr_base diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h index c28fee9222caa..26e322059743b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -266,6 +266,25 @@ class DWARFUnit : public lldb_private::UserID { /// True if any DIEs match any tag in \a tags, false otherwise. bool HasAny(llvm::ArrayRef tags); + + /// Get the fission .dwo file specific error for this compile unit. + /// + /// The skeleton compile unit only can have a DWO error. Any other type + /// of DWARFUnit will not have a valid DWO error. + /// + /// \returns + /// A valid DWO error if there is a problem with anything in the + /// locating or parsing inforamtion in the .dwo file + const lldb_private::Status &GetDwoError() const { return m_dwo_error; } + + /// Set the fission .dwo file specific error for this compile unit. + /// + /// This helps tracks issues that arise when trying to locate or parse a + /// .dwo file. Things like a missing .dwo file, DWO ID mismatch, and other + /// .dwo errors can be stored in each compile unit so the issues can be + /// communicated to the user. + void SetDwoError(const lldb_private::Status &error) { m_dwo_error = error; } + protected: DWARFUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid, const DWARFUnitHeader &header, @@ -346,6 +365,10 @@ class DWARFUnit : public lldb_private::UserID { bool m_has_parsed_non_skeleton_unit; /// Value of DW_AT_GNU_dwo_id (v4) or dwo_id from CU header (v5). llvm::Optional m_dwo_id; + /// If we get an error when trying to load a .dwo file, save that error here. + /// Errors include .dwo/.dwp file not found, or the .dwp/.dwp file was found + /// but DWO ID doesn't match, etc. + lldb_private::Status m_dwo_error; private: void ParseProducerInfo(); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 237b3fea716a8..68d86f978da03 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -1732,24 +1732,34 @@ SymbolFileDWARF::GetDwoSymbolFileForCompileUnit( return nullptr; DWARFCompileUnit *dwarf_cu = llvm::dyn_cast(&unit); - // Only compile units can be split into two parts. - if (!dwarf_cu) + // Only compile units can be split into two parts and we should only + // look for a DWO file if there is a valid DWO ID. + if (!dwarf_cu || !dwarf_cu->GetDWOId().has_value()) return nullptr; const char *dwo_name = GetDWOName(*dwarf_cu, cu_die); - if (!dwo_name) + if (!dwo_name) { + unit.SetDwoError(Status("missing DWO name in skeleton DIE 0x%8.8" PRIx32, + cu_die.GetOffset())); return nullptr; + } if (std::shared_ptr dwp_sp = GetDwpSymbolFile()) return dwp_sp; + const char *comp_dir = nullptr; FileSpec dwo_file(dwo_name); FileSystem::Instance().Resolve(dwo_file); if (dwo_file.IsRelative()) { - const char *comp_dir = - cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr); - if (!comp_dir) + comp_dir = cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, + nullptr); + if (!comp_dir) { + unit.SetDwoError( + Status("unable to locate relative .dwo debug file \"%s\" for " + "skeleton DIE 0x%8.8" PRIx32 " without valid DW_AT_comp_dir " + "attribute", dwo_name, cu_die.GetOffset())); return nullptr; + } dwo_file.SetFile(comp_dir, FileSpec::Style::native); if (dwo_file.IsRelative()) { @@ -1764,6 +1774,11 @@ SymbolFileDWARF::GetDwoSymbolFileForCompileUnit( } if (!FileSystem::Instance().Exists(dwo_file)) { + unit.SetDwoError( + Status("unable to locate .dwo debug file \"%s\" for skeleton DIE " + "0x%8.8" PRIx32, dwo_file.GetPath().c_str(), + cu_die.GetOffset())); + if (m_dwo_warning_issued.test_and_set(std::memory_order_relaxed) == false) { GetObjectFile()->GetModule()->ReportWarning( "unable to locate separate debug file (dwo, dwp). Debugging will be " @@ -1779,8 +1794,12 @@ SymbolFileDWARF::GetDwoSymbolFileForCompileUnit( GetObjectFile()->GetModule(), &dwo_file, file_offset, FileSystem::Instance().GetByteSize(dwo_file), dwo_file_data_sp, dwo_file_data_offset); - if (dwo_obj_file == nullptr) + if (dwo_obj_file == nullptr) { + unit.SetDwoError( + Status("unable to load object file for .dwo debug file \"%s\" for " + "unit DIE 0x%8.8" PRIx32, dwo_name, cu_die.GetOffset())); return nullptr; + } return std::make_shared(*this, dwo_obj_file, dwarf_cu->GetID()); @@ -4132,6 +4151,14 @@ Status SymbolFileDWARF::GetFrameVariableError(StackFrame &frame) { if (!dwarf_cu) return Status(); + // Check if we have a skeleton compile unit that had issues trying to load + // its .dwo/.dwp file. First pares the Unit DIE to make sure we see any .dwo + // related errors. + dwarf_cu->ExtractUnitDIEIfNeeded(); + const Status &dwo_error = dwarf_cu->GetDwoError(); + if (dwo_error.Fail()) + return dwo_error; + // Don't return an error for assembly files as they typically don't have // varaible information. if (dwarf_cu->GetDWARFLanguageType() == DW_LANG_Mips_Assembler) diff --git a/lldb/test/API/commands/frame/var/TestFrameVar.py b/lldb/test/API/commands/frame/var/TestFrameVar.py index 4b3674859a283..9e0fb2c2d48b2 100644 --- a/lldb/test/API/commands/frame/var/TestFrameVar.py +++ b/lldb/test/API/commands/frame/var/TestFrameVar.py @@ -9,6 +9,7 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * import os +import shutil import time class TestFrameVar(TestBase): @@ -182,3 +183,59 @@ def test_gline_tables_only(self): 'no variable information is available in debug info for this compile unit' ] self.check_frame_variable_errors(thread, error_strings) + + @skipUnlessPlatform(["linux", "freebsd"]) + @add_test_categories(["dwo"]) + def test_fission_missing_dwo(self): + ''' + Test that if we build a binary with "-gsplit-dwarf" that we can + set a file and line breakpoint successfully, and get an error + letting us know we were unable to load the .dwo file. + ''' + self.build(dictionary={'CFLAGS_EXTRAS': '-gsplit-dwarf'}) + exe = self.getBuildArtifact("a.out") + main_dwo = self.getBuildArtifact("main.dwo") + + self.assertTrue(os.path.exists(main_dwo), 'Make sure "%s" file exists' % (main_dwo)) + # Delete the main.dwo file that contains the debug info so we force an + # error when we run to main and try to get variables. + os.unlink(main_dwo) + + # We have to set a named breakpoint because we don't have any debug info + # because we deleted the main.o file since the mod times don't match + # and debug info won't be loaded + (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, 'main') + error_strings = [ + 'unable to locate .dwo debug file "', + 'main.dwo" for skeleton DIE 0x' + ] + self.check_frame_variable_errors(thread, error_strings) + + @skipUnlessPlatform(["linux", "freebsd"]) + @add_test_categories(["dwo"]) + def test_fission_invalid_dwo_objectfile(self): + ''' + Test that if we build a binary with "-gsplit-dwarf" that we can + set a file and line breakpoint successfully, and get an error + letting us know we were unable to load the .dwo file because it + existed, but it wasn't a valid object file. + ''' + self.build(dictionary={'CFLAGS_EXTRAS': '-gsplit-dwarf'}) + exe = self.getBuildArtifact("a.out") + main_dwo = self.getBuildArtifact("main.dwo") + + self.assertTrue(os.path.exists(main_dwo), 'Make sure "%s" file exists' % (main_dwo)) + # Overwrite the main.dwo with the main.c source file so that the .dwo + # file exists, but it isn't a valid object file as there is an error + # for this case. + shutil.copyfile(self.getSourcePath('main.c'), main_dwo) + + # We have to set a named breakpoint because we don't have any debug info + # because we deleted the main.o file since the mod times don't match + # and debug info won't be loaded + (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, 'main') + error_strings = [ + 'unable to load object file for .dwo debug file "' + 'main.dwo" for unit DIE 0x', + ] + self.check_frame_variable_errors(thread, error_strings)