diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index 9901c2c57557b..5501410aa49c1 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -131,6 +131,9 @@ class SymbolFile : public PluginInterface { Symtab *GetSymtab(); + virtual llvm::VersionTuple GetProducerVersion(CompileUnit &comp_unit) { + return {}; + } virtual lldb::LanguageType ParseLanguage(CompileUnit &comp_unit) = 0; /// Return the Xcode SDK comp_unit was compiled against. virtual XcodeSDK ParseXcodeSDK(CompileUnit &comp_unit) { return {}; } diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index a6a5d6abf9001..c06467a1ceb37 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -93,6 +93,9 @@ class ProcessProperties : public Properties { void SetDetachKeepsStopped(bool keep_stopped); bool GetWarningsOptimization() const; bool GetWarningsUnsupportedLanguage() const; +#ifdef LLDB_ENABLE_SWIFT + bool GetWarningsToolchainMismatch() const; +#endif bool GetStopOnExec() const; std::chrono::seconds GetUtilityExpressionTimeout() const; std::chrono::seconds GetInterruptTimeout() const; @@ -372,7 +375,8 @@ class Process : public std::enable_shared_from_this, enum Warnings { eWarningsOptimization = 1, eWarningsUnsupportedLanguage = 2, - eWarningsSwiftImport + eWarningsSwiftImport, + eWarningsToolchainMismatch }; typedef Range LoadRange; @@ -1311,6 +1315,7 @@ class Process : public std::enable_shared_from_this, /// pre-computed. void PrintWarningOptimization(const SymbolContext &sc); +#ifdef LLDB_ENABLE_SWIFT /// Prints a async warning message to the user one time per Process /// for a Module whose Swift AST sections couldn't be loaded because /// they aren't buildable on the current machine. @@ -1320,6 +1325,11 @@ class Process : public std::enable_shared_from_this, void PrintWarningCantLoadSwiftModule(const Module &module, std::string details); + /// Print a user-visible warning about Swift CUs compiled with a + /// different Swift compiler than the one embedded in LLDB. + void PrintWarningToolchainMismatch(const SymbolContext &sc); +#endif + /// Print a user-visible warning about a function written in a /// language that this version of LLDB doesn't support. /// diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp index 824e43872269b..fdc5e1c1efd2f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -609,52 +609,45 @@ bool DWARFUnit::DW_AT_decl_file_attributes_are_invalid() { } bool DWARFUnit::Supports_unnamed_objc_bitfields() { - if (GetProducer() == eProducerClang) { - const uint32_t major_version = GetProducerVersionMajor(); - return major_version > 425 || - (major_version == 425 && GetProducerVersionUpdate() >= 13); - } - return true; // Assume all other compilers didn't have incorrect ObjC bitfield - // info + if (GetProducer() == eProducerClang) + return GetProducerVersion() >= llvm::VersionTuple(425, 0, 13); + // Assume all other compilers didn't have incorrect ObjC bitfield info. + return true; } void DWARFUnit::ParseProducerInfo() { - m_producer_version_major = UINT32_MAX; - m_producer_version_minor = UINT32_MAX; - m_producer_version_update = UINT32_MAX; - + m_producer = eProducerOther; const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); - if (die) { - - const char *producer_cstr = - die->GetAttributeValueAsString(this, DW_AT_producer, nullptr); - if (producer_cstr) { - RegularExpression llvm_gcc_regex( - llvm::StringRef("^4\\.[012]\\.[01] \\(Based on Apple " - "Inc\\. build [0-9]+\\) \\(LLVM build " - "[\\.0-9]+\\)$")); - if (llvm_gcc_regex.Execute(llvm::StringRef(producer_cstr))) { - m_producer = eProducerLLVMGCC; - } else if (strstr(producer_cstr, "clang")) { - static RegularExpression g_clang_version_regex( - llvm::StringRef("clang-([0-9]+)\\.([0-9]+)\\.([0-9]+)")); - llvm::SmallVector matches; - if (g_clang_version_regex.Execute(llvm::StringRef(producer_cstr), - &matches)) { - m_producer_version_major = - StringConvert::ToUInt32(matches[1].str().c_str(), UINT32_MAX, 10); - m_producer_version_minor = - StringConvert::ToUInt32(matches[2].str().c_str(), UINT32_MAX, 10); - m_producer_version_update = - StringConvert::ToUInt32(matches[3].str().c_str(), UINT32_MAX, 10); - } - m_producer = eProducerClang; - } else if (strstr(producer_cstr, "GNU")) - m_producer = eProducerGCC; - } + if (!die) + return; + + llvm::StringRef producer( + die->GetAttributeValueAsString(this, DW_AT_producer, nullptr)); + if (producer.empty()) + return; + + static RegularExpression g_swiftlang_version_regex( + llvm::StringRef(R"(swiftlang-([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?))")); + static RegularExpression g_clang_version_regex( + llvm::StringRef(R"(clang-([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?))")); + static RegularExpression g_llvm_gcc_regex( + llvm::StringRef(R"(4\.[012]\.[01] )" + R"(\(Based on Apple Inc\. build [0-9]+\) )" + R"(\(LLVM build [\.0-9]+\)$)")); + + llvm::SmallVector matches; + if (g_swiftlang_version_regex.Execute(producer, &matches)) { + m_producer_version.tryParse(matches[1]); + m_producer = eProducerSwift; + } else if (producer.contains("clang")) { + if (g_clang_version_regex.Execute(producer, &matches)) + m_producer_version.tryParse(matches[1]); + m_producer = eProducerClang; + } else if (producer.contains("GNU")) { + m_producer = eProducerGCC; + } else if (g_llvm_gcc_regex.Execute(producer)) { + m_producer = eProducerLLVMGCC; } - if (m_producer == eProducerInvalid) - m_producer = eProcucerOther; } DWARFProducer DWARFUnit::GetProducer() { @@ -663,22 +656,10 @@ DWARFProducer DWARFUnit::GetProducer() { return m_producer; } -uint32_t DWARFUnit::GetProducerVersionMajor() { - if (m_producer_version_major == 0) - ParseProducerInfo(); - return m_producer_version_major; -} - -uint32_t DWARFUnit::GetProducerVersionMinor() { - if (m_producer_version_minor == 0) - ParseProducerInfo(); - return m_producer_version_minor; -} - -uint32_t DWARFUnit::GetProducerVersionUpdate() { - if (m_producer_version_update == 0) +llvm::VersionTuple DWARFUnit::GetProducerVersion() { + if (m_producer_version.empty()) ParseProducerInfo(); - return m_producer_version_update; + return m_producer_version; } uint64_t DWARFUnit::GetDWARFLanguageType() { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h index da79a6aaf64e5..315c77a78f110 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -29,7 +29,8 @@ enum DWARFProducer { eProducerClang, eProducerGCC, eProducerLLVMGCC, - eProcucerOther + eProducerSwift, + eProducerOther }; /// Base class describing the header of any kind of "unit." Some information @@ -194,11 +195,7 @@ class DWARFUnit : public lldb_private::UserID { DWARFProducer GetProducer(); - uint32_t GetProducerVersionMajor(); - - uint32_t GetProducerVersionMinor(); - - uint32_t GetProducerVersionUpdate(); + llvm::VersionTuple GetProducerVersion(); uint64_t GetDWARFLanguageType(); @@ -308,9 +305,7 @@ class DWARFUnit : public lldb_private::UserID { std::unique_ptr m_func_aranges_up; dw_addr_t m_base_addr = 0; DWARFProducer m_producer = eProducerInvalid; - uint32_t m_producer_version_major = 0; - uint32_t m_producer_version_minor = 0; - uint32_t m_producer_version_update = 0; + llvm::VersionTuple m_producer_version; llvm::Optional m_language_type; lldb_private::LazyBool m_is_optimized = lldb_private::eLazyBoolCalculate; llvm::Optional m_comp_dir; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index af6cf08dab100..18be95eaf6ec0 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -804,6 +804,16 @@ bool SymbolFileDWARF::FixupAddress(Address &addr) { // This is a normal DWARF file, no address fixups need to happen return true; } + +llvm::VersionTuple SymbolFileDWARF::GetProducerVersion(CompileUnit &comp_unit) { + std::lock_guard guard(GetModuleMutex()); + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (dwarf_cu) + return dwarf_cu->GetProducerVersion(); + else + return {}; +} + lldb::LanguageType SymbolFileDWARF::ParseLanguage(CompileUnit &comp_unit) { std::lock_guard guard(GetModuleMutex()); DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 424656899e13f..0fb507fbeb595 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -108,6 +108,8 @@ class SymbolFileDWARF : public lldb_private::SymbolFile, void InitializeObject() override; // Compile Unit function calls + llvm::VersionTuple + GetProducerVersion(lldb_private::CompileUnit &comp_unit) override; lldb::LanguageType ParseLanguage(lldb_private::CompileUnit &comp_unit) override; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index 492c282c685d8..1ecc871bb27e2 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -623,6 +623,15 @@ size_t SymbolFileDWARFDebugMap::GetCompUnitInfosForModule( return cu_infos.size(); } +llvm::VersionTuple +SymbolFileDWARFDebugMap::GetProducerVersion(CompileUnit &comp_unit) { + std::lock_guard guard(GetModuleMutex()); + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->GetProducerVersion(comp_unit); + return {}; +} + lldb::LanguageType SymbolFileDWARFDebugMap::ParseLanguage(CompileUnit &comp_unit) { std::lock_guard guard(GetModuleMutex()); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h index 2f9b5086699c2..b2d564043655b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -56,6 +56,8 @@ class SymbolFileDWARFDebugMap : public lldb_private::SymbolFile { void InitializeObject() override; // Compile Unit function calls + llvm::VersionTuple + GetProducerVersion(lldb_private::CompileUnit &comp_unit) override; lldb::LanguageType ParseLanguage(lldb_private::CompileUnit &comp_unit) override; lldb_private::XcodeSDK diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index fc02b21e7cbc4..06ddb998cb9b0 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -38,6 +38,7 @@ #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolFile.h" #include "lldb/Target/ABI.h" #include "lldb/Target/AssertFrameRecognizer.h" #include "lldb/Target/DynamicLoader.h" @@ -71,6 +72,10 @@ #include "lldb/Utility/State.h" #include "lldb/Utility/Timer.h" +#ifdef LLDB_ENABLE_SWIFT +#include "swift/Basic/Version.h" +#endif + using namespace lldb; using namespace lldb_private; using namespace std::chrono; @@ -285,6 +290,14 @@ bool ProcessProperties::GetWarningsUnsupportedLanguage() const { nullptr, idx, g_process_properties[idx].default_uint_value != 0); } +#ifdef LLDB_ENABLE_SWIFT +bool ProcessProperties::GetWarningsToolchainMismatch() const { + const uint32_t idx = ePropertyWarningToolchainMismatch; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); +} +#endif + bool ProcessProperties::GetStopOnExec() const { const uint32_t idx = ePropertyStopOnExec; return m_collection_sp->GetPropertyAtIndexAsBoolean( @@ -5869,6 +5882,7 @@ void Process::PrintWarningOptimization(const SymbolContext &sc) { } } +#ifdef LLDB_ENABLE_SWIFT void Process::PrintWarningCantLoadSwiftModule(const Module &module, std::string details) { PrintWarning(Process::Warnings::eWarningsSwiftImport, (void *)&module, @@ -5876,6 +5890,24 @@ void Process::PrintWarningCantLoadSwiftModule(const Module &module, module.GetFileSpec().GetCString(), details.c_str()); } +void Process::PrintWarningToolchainMismatch(const SymbolContext &sc) { + if (!GetWarningsToolchainMismatch()) + return; + if (!sc.module_sp || !sc.comp_unit) + return; + if (sc.GetLanguage() != eLanguageTypeSwift) + return; + if (SymbolFile *sym_file = sc.module_sp->GetSymbolFile()) + if (sym_file->GetProducerVersion(*sc.comp_unit) != + swift::version::Version::getCurrentCompilerVersion()) + PrintWarning(Process::Warnings::eWarningsToolchainMismatch, + sc.module_sp.get(), + "%s was compiled with a Swift compiler from a different " + "toolchain. Swift expression evaluation may not work.\n", + sc.module_sp->GetFileSpec().GetFilename().GetCString()); +} +#endif + void Process::PrintWarningUnsupportedLanguage(const SymbolContext &sc) { if (!GetWarningsUnsupportedLanguage()) return; diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td index f238bdfc84d4f..92353e220a10b 100644 --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -241,6 +241,9 @@ let Definition = "process" in { def WarningUnsupportedLanguage: Property<"unsupported-language-warnings", "Boolean">, DefaultTrue, Desc<"If true, warn when stopped in code that is written in a source language that LLDB does not support.">; + def WarningToolchainMismatch: Property<"toolchain-mismatch-warnings", "Boolean">, + DefaultTrue, + Desc<"If true, warn when stopped in code that was compiled by a Swift compiler different from the one embedded in LLDB.">; def StopOnExec: Property<"stop-on-exec", "Boolean">, Global, DefaultTrue, diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 8e1aac73bf293..955eab775b7d3 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -322,11 +322,18 @@ void Thread::FrameSelectedCallback(StackFrame *frame) { if (frame->HasDebugInformation() && (GetProcess()->GetWarningsOptimization() || - GetProcess()->GetWarningsUnsupportedLanguage())) { + GetProcess()->GetWarningsUnsupportedLanguage() +#ifdef LLDB_ENABLE_SWIFT + || GetProcess()->GetWarningsToolchainMismatch()) +#endif + ) { SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextModule); GetProcess()->PrintWarningOptimization(sc); GetProcess()->PrintWarningUnsupportedLanguage(sc); +#ifdef LLDB_ENABLE_SWIFT + GetProcess()->PrintWarningToolchainMismatch(sc); +#endif } SymbolContext msc = frame->GetSymbolContext(eSymbolContextModule); if (msc.module_sp) diff --git a/lldb/test/Shell/Swift/ToolchainMismatch.test b/lldb/test/Shell/Swift/ToolchainMismatch.test new file mode 100644 index 0000000000000..59d5b50d8f0a0 --- /dev/null +++ b/lldb/test/Shell/Swift/ToolchainMismatch.test @@ -0,0 +1,38 @@ +# REQUIRES: swift + +# Tests that a warning is printed when stopped in a Swift frame +# compiled by a different Swift compiler than the one embedded in LLDB. + +# RUN: rm -rf %t && mkdir %t && cd %t +# RUN: %target-swiftc -g \ +# RUN: -module-cache-path %t/cache %S/Inputs/main.swift \ +# RUN: -module-name main -o %t/a.ll -emit-ir +# RUN: sed -i -e 's/producer: "[^"]*Swift [^"]*"/producer: "Future Swift (swiftlang-9999.8.7.6)"/g' %t/a.ll +# RUN: %clang_host -c %t/a.ll -o %t/a.o +# RUN: llvm-dwarfdump -r 0 %t/a.o | grep -q swiftlang-9999 +# RUN: %target-swiftc \ +# RUN: -module-cache-path %t/cache \ +# RUN: %t/a.o -o %t/a.out +# RUN: %lldb %t/a.out -s %s 2>&1 | FileCheck %s + +# Sanity check: Swift +# RUN: %target-swiftc -g \ +# RUN: -module-cache-path %t/cache %S/Inputs/main.swift \ +# RUN: -module-name main -o %t/good.out +# RUN: %lldb %t/good.out -s %s 2>&1 | FileCheck %s --check-prefix=SANITY + +# Sanity check: Clang +# RUN: %clang_host -g \ +# RUN: %S/../Driver/Inputs/hello.cpp \ +# RUN: -o %t/clang.out +# RUN: %lldb %t/clang.out -s %s 2>&1 | FileCheck %s --check-prefix=SANITY + +b main +run +quit + +# The {{ }} avoids accidentally matching the input script! +# CHECK: {{a\.out}} was compiled with a Swift compiler from a different toolchain. +# CHECK: stop reason{{ = }}breakpoint +# SANITY-NOT: {{a\.out}} was compiled with a Swift compiler from a different toolchain. +# SANITY: stop reason{{ = }}breakpoint diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFUnitTest.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFUnitTest.cpp index f5cfd1e61120b..123acb6ea8e3f 100644 --- a/lldb/unittests/SymbolFile/DWARF/DWARFUnitTest.cpp +++ b/lldb/unittests/SymbolFile/DWARF/DWARFUnitTest.cpp @@ -84,3 +84,110 @@ TEST(DWARFUnitTest, MissingSentinel) { EXPECT_EQ(die_first->GetFirstChild(), nullptr); EXPECT_EQ(die_first->GetSibling(), nullptr); } + +TEST(DWARFUnitTest, ClangProducer) { + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +DWARF: + debug_str: + - 'Apple clang version 13.0.0 (clang-1300.0.29.3)' + debug_abbrev: + - Table: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_strp + debug_info: + - Version: 4 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x0 + - AbbrCode: 0x0 +)"; + + YAMLModuleTester t(yamldata); + DWARFUnit *unit = t.GetDwarfUnit(); + ASSERT_TRUE((bool)unit); + EXPECT_EQ(unit->GetProducer(), eProducerClang); + EXPECT_EQ(unit->GetProducerVersion(), llvm::VersionTuple(1300, 0, 29, 3)); +} + +TEST(DWARFUnitTest, LLVMGCCProducer) { + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +DWARF: + debug_str: + - 'i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)' + debug_abbrev: + - Table: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_strp + debug_info: + - Version: 4 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x0 + - AbbrCode: 0x0 +)"; + + YAMLModuleTester t(yamldata); + DWARFUnit *unit = t.GetDwarfUnit(); + ASSERT_TRUE((bool)unit); + EXPECT_EQ(unit->GetProducer(), eProducerLLVMGCC); +} + +TEST(DWARFUnitTest, SwiftProducer) { + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +DWARF: + debug_str: + - 'Apple Swift version 5.5 (swiftlang-1300.0.31.1 clang-1300.0.29.1)' + debug_abbrev: + - Table: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_strp + debug_info: + - Version: 4 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x0 + - AbbrCode: 0x0 +)"; + + YAMLModuleTester t(yamldata); + DWARFUnit *unit = t.GetDwarfUnit(); + ASSERT_TRUE((bool)unit); + EXPECT_EQ(unit->GetProducer(), eProducerSwift); + EXPECT_EQ(unit->GetProducerVersion(), llvm::VersionTuple(1300, 0, 31, 1)); +}