diff --git a/include/lldb/Symbol/SwiftASTContext.h b/include/lldb/Symbol/SwiftASTContext.h index 9198dba6a0ac..90b1111d17bf 100644 --- a/include/lldb/Symbol/SwiftASTContext.h +++ b/include/lldb/Symbol/SwiftASTContext.h @@ -822,6 +822,8 @@ class SwiftASTContext : public TypeSystem { bool TargetHasNoSDK(); + std::vector &GetASTVectorForModule(const Module *module); + std::unique_ptr m_source_manager_ap; std::unique_ptr m_diagnostic_engine_ap; std::unique_ptr m_ast_context_ap; @@ -850,7 +852,7 @@ class SwiftASTContext : public TypeSystem { // target's process pointer be filled in std::string m_platform_sdk_path; std::string m_resource_dir; - typedef std::map ASTFileDataMap; + typedef std::map> ASTFileDataMap; ASTFileDataMap m_ast_file_data_map; /// FIXME: this vector is needed because the LLDBNameLookup debugger clients /// are being put into diff --git a/include/lldb/Symbol/SymbolFile.h b/include/lldb/Symbol/SymbolFile.h index c53a32693687..22ed447d8eac 100644 --- a/include/lldb/Symbol/SymbolFile.h +++ b/include/lldb/Symbol/SymbolFile.h @@ -10,6 +10,8 @@ #ifndef liblldb_SymbolFile_h_ #define liblldb_SymbolFile_h_ +#include + #include "lldb/Core/PluginInterface.h" #include "lldb/Symbol/CompilerDecl.h" #include "lldb/Symbol/CompilerDeclContext.h" @@ -223,15 +225,26 @@ class SymbolFile : public PluginInterface { virtual bool ForceInlineSourceFileCheck(); //------------------------------------------------------------------ - // Symbol files can store AST data for any language that wants to - // store the native AST format supported by the current compiler. - // This information is often only usable by a compiler that is in - // sync with the compiler sources that were used to build LLDB so - // any data should be versioned appropriately so the compiler can - // try to load the data and know if the data will be able to be - // used. + /// Retrieve all the AST data blobs from the SymbolFile. + /// + /// Symbol files can store AST data for any language that wants to + /// store the native AST format supported by the current compiler. + /// This information is often only usable by a compiler that is in + /// sync with the compiler sources that were used to build LLDB so + /// any data should be versioned appropriately so the compiler can + /// try to load the data and know if the data will be able to be + /// used. + /// + /// @param[in] language + /// The language for which AST data is being requested. + /// A given file can contain ASTs for more than one language. + /// + /// @return + /// Zero or more buffers, each of which contain the raw data + /// of an AST in the requested language. //------------------------------------------------------------------ - virtual lldb::DataBufferSP GetASTData(lldb::LanguageType language); + virtual std::vector + GetASTData(lldb::LanguageType language); // Used for the REPL to limit source file ranges that are valid within "file". // Since diff --git a/include/lldb/Symbol/SymbolVendor.h b/include/lldb/Symbol/SymbolVendor.h index caa82b2d80df..6ca6cd3cf2e6 100644 --- a/include/lldb/Symbol/SymbolVendor.h +++ b/include/lldb/Symbol/SymbolVendor.h @@ -163,7 +163,8 @@ class SymbolVendor : public ModuleChild, public PluginInterface { virtual bool SymbolContextShouldBeExcluded(const SymbolContext &sc, uint32_t actual_line); - virtual lldb::DataBufferSP GetASTData(lldb::LanguageType language); + virtual std::vector + GetASTData(lldb::LanguageType language); virtual bool ForceInlineSourceFileCheck(); diff --git a/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/A.swift b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/A.swift new file mode 100644 index 000000000000..2b1cd5ae94b6 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/A.swift @@ -0,0 +1,7 @@ +import Foundation + +@objc public class A: NSObject { + public func foo() -> Int { + return 4 // Set breakpoint here + } +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/B.swift b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/B.swift new file mode 100644 index 000000000000..ea6d6e80340e --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/B.swift @@ -0,0 +1,7 @@ +import Foundation + +@objc public class B: NSObject { + public func bar() -> Int { + return 8 // Set breakpoint here + } +} \ No newline at end of file diff --git a/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/Makefile b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/Makefile new file mode 100755 index 000000000000..9964302b9196 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/Makefile @@ -0,0 +1,29 @@ +# Build swift modules with debug info + +LEVEL=../../../../make + +# Don't use 'all' target. There is a default build rule that will kick in that +# will be wrong. WE use 'first' so that the normal 'make' command (without +# a target) selects the first (but not 'all') target so we avoid the undesired +# default behavior. +first: main + +include $(LEVEL)/Makefile.rules + +# To use the path commented out below, which is what we'd really want to do, +# we'd also need to require that the Swift standard library be built along +# with the compiler. I'd like to avoid that requirement. +# SWIFT_LIB_DIR=$(dir $(SWIFTCC))../lib +SWIFT_LIB_DIR="$(shell xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx" + +main: A.o B.o objc_main.m + $(CC) objc_main.m -fobjc-arc -o main A.o B.o -L$(SWIFT_LIB_DIR) -Xlinker -add_ast_path -Xlinker A.swiftmodule -Xlinker -add_ast_path -Xlinker B.swiftmodule -Xlinker -rpath -Xlinker $(SWIFT_LIB_DIR) + +A.o: A.swift + $(SWIFTCC) -c -g -Onone -sdk "$(SWIFTSDKROOT)" -parse-as-library -module-name A -emit-module-path A.swiftmodule -emit-objc-header-path A-Swift.h -output-file-map output_map A.swift + +B.o: B.swift + $(SWIFTCC) -c -g -Onone -sdk "$(SWIFTSDKROOT)" -parse-as-library -module-name B -emit-module-path B.swiftmodule -emit-objc-header-path B-Swift.h -output-file-map output_map B.swift -o B.o + +clean:: + rm -f *.o main *-Swift.h *.swiftmodule *.swiftdoc diff --git a/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/TestSwiftStaticLinkingMacOS.py b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/TestSwiftStaticLinkingMacOS.py new file mode 100644 index 000000000000..7e8f723e5f88 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/TestSwiftStaticLinkingMacOS.py @@ -0,0 +1,114 @@ +# TestSwiftStaticLinkingMacOS.py +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +# +# ------------------------------------------------------------------------------ +""" +Test that macOS can statically link two separately-compiled Swift modules +with one Objective-C module, link them through the clang driver, and still +access debug info for each of the Swift modules. +""" +from __future__ import print_function + + +# System imports +import os +import commands + +# Third-party imports + +# LLDB imports +import lldb +from lldbsuite.test.lldbtest import TestBase +from lldbsuite.test import decorators, lldbtest, lldbutil + + +class SwiftStaticLinkingMacOSTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + NO_DEBUG_INFO_TESTCASE = True + + def expect_self_var_available_at_breakpoint( + self, process, breakpoint, module_name): + # Frame #0 should be at the given breakpoint + threads = lldbutil.get_threads_stopped_at_breakpoint( + process, breakpoint) + + self.assertEquals(1, len(threads)) + self.thread = threads[0] + self.frame = self.thread.frames[0] + self.assertTrue(self.frame, "Frame 0 is valid.") + + patterns = [ + # Ensure we report a self with an address. + r"self\s*=\s*0x[0-9a-fA-F]+", + # Ensure we think it is an NSObject. + r"ObjectiveC.NSObject"] + substrs = [ + "(%s.%s)" % (module_name, module_name) + ] + self.expect("frame variable self", patterns=patterns, + substrs=substrs) + + @decorators.skipUnlessDarwin + def test_variables_print_from_both_swift_modules(self): + """Test that variables from two modules can be accessed.""" + self.build() + + # I could not find a reasonable way to say "skipUnless(archs=[])". + # That would probably be worth adding. + if self.getArchitecture() != 'x86_64': + self.skipTest("This test requires x86_64 as the architecture " + "for the inferior") + + exe_name = "main" + src_a = lldb.SBFileSpec("A.swift") + line_a = 5 + src_b = lldb.SBFileSpec("B.swift") + line_b = 5 + exe = os.path.join(os.getcwd(), exe_name) + + # Create the target + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, lldbtest.VALID_TARGET) + + # Set the breakpoints + # breakpoint_a = target.BreakpointCreateBySourceRegex( + # 'Set breakpoint here', src_a) + breakpoint_a = target.BreakpointCreateByLocation( + src_a, line_a) + self.assertTrue(breakpoint_a.GetNumLocations() > 0, + lldbtest.VALID_BREAKPOINT) + + # breakpoint_b = target.BreakpointCreateBySourceRegex( + # 'Set breakpoint here', src_b) + breakpoint_b = target.BreakpointCreateByLocation( + src_b, line_b) + self.assertTrue(breakpoint_b.GetNumLocations() > 0, + lldbtest.VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + envp = ['DYLD_FRAMEWORK_PATH=.'] + process = target.LaunchSimple(None, envp, os.getcwd()) + + self.assertTrue(process, lldbtest.PROCESS_IS_VALID) + + # We should be at breakpoint in module A. + self.expect_self_var_available_at_breakpoint( + process, breakpoint_a, "A") + + # Jump to the next breakpoint + process.Continue() + + # We should be at breakpoint in module B. + self.expect_self_var_available_at_breakpoint( + process, breakpoint_b, "B") + + return diff --git a/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/objc_main.m b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/objc_main.m new file mode 100644 index 000000000000..98f98b404a70 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/objc_main.m @@ -0,0 +1,13 @@ +#import + +#import "A-Swift.h" +#import "B-Swift.h" + +int main(int argc, const char * argv[]) { + @autoreleasepool { + NSLog(@"Hello, World!"); + NSLog(@"A = %ld", [[[A alloc] init] foo]); + NSLog(@"B = %ld", [[[B alloc] init] bar]); + } + return 0; +} diff --git a/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/output_map b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/output_map new file mode 100644 index 000000000000..e6c3a615a1f2 --- /dev/null +++ b/packages/Python/lldbsuite/test/lang/swift/static_linking/macOS/output_map @@ -0,0 +1,3 @@ +{'A.swift': {'object': 'A.o'}, + 'B.swift': {'object': 'B.o'}, +} diff --git a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index 6657995caa58..fdfef1dbeed9 100644 --- a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -1441,20 +1441,65 @@ SymbolFileDWARFDebugMap::AddOSOARanges(SymbolFileDWARF *dwarf2Data, return num_line_entries_added; } -DataBufferSP SymbolFileDWARFDebugMap::GetASTData(lldb::LanguageType language) { - if (language == eLanguageTypeSwift) { - Symtab *symtab = m_obj_file->GetSymtab(); - if (symtab) { - uint32_t start_idx = 0; - Symbol *symbol = - symtab->FindSymbolWithType(eSymbolTypeASTFile, Symtab::eDebugAny, - Symtab::eVisibilityAny, start_idx); - if (symbol) { - FileSpec file_spec(symbol->GetName().GetCString(), false); - if (file_spec.Exists()) - return file_spec.ReadFileContents(); +std::vector +SymbolFileDWARFDebugMap::GetASTData(lldb::LanguageType language) { + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP)); + + std::vector ast_datas; + if (language != eLanguageTypeSwift) { + if (log) + log->Printf("SymbolFileDWARFDebugMap::%s() - ignoring because not Swift", + __FUNCTION__); + return ast_datas; + } + + Symtab *symtab = m_obj_file->GetSymtab(); + if (!symtab) { + if (log) + log->Printf("SymbolFileDWARFDebugMap::%s() - ignoring because the obj " + "file has no symbol table", + __FUNCTION__); + return ast_datas; + } + + uint32_t next_idx = 0; + bool done = false; + do { + Symbol *symbol = + symtab->FindSymbolWithType(eSymbolTypeASTFile, Symtab::eDebugAny, + Symtab::eVisibilityAny, next_idx); + if (symbol == nullptr) { + // We didn't find any more symbols of type eSymbolTypeASTFile. We are + // done looping for them. + done = true; + } else { + // Try to load the specified file. + FileSpec file_spec(symbol->GetName().GetCString(), false); + if (file_spec.Exists()) { + // We found the source data for the AST data blob. + // Read it in and add it to our return vector. + ast_datas.push_back(file_spec.ReadFileContents()); + if (log) + log->Printf("SymbolFileDWARFDebugMap::%s() - found and loaded AST " + "data from file %s", + __FUNCTION__, file_spec.GetPath().c_str()); + } else { + if (log) + log->Printf("SymbolFileDWARFDebugMap::%s() - found reference to AST " + "file %s, but could not find the file, ignoring", + __FUNCTION__, file_spec.GetPath().c_str()); } + + // Regardless of whether we could find the specified file, start the next + // symbol search at the index past the one we just found. + ++next_idx; } - } - return DataBufferSP(); + } while (!done); + + // Return the vector of AST data blobs + if (log) + log->Printf("SymbolFileDWARFDebugMap::%s() - returning %d AST data blobs", + __FUNCTION__, (int)ast_datas.size()); + + return ast_datas; } diff --git a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h index 130cb0e34317..25cceb415738 100644 --- a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -122,7 +122,8 @@ class SymbolFileDWARFDebugMap : public lldb_private::SymbolFile { uint32_t type_mask, lldb_private::TypeList &type_list) override; - lldb::DataBufferSP GetASTData(lldb::LanguageType language) override; + std::vector + GetASTData(lldb::LanguageType language) override; //------------------------------------------------------------------ // PluginInterface protocol diff --git a/source/Symbol/SwiftASTContext.cpp b/source/Symbol/SwiftASTContext.cpp index af3ff7f3dec1..855f532461c6 100644 --- a/source/Symbol/SwiftASTContext.cpp +++ b/source/Symbol/SwiftASTContext.cpp @@ -1345,19 +1345,23 @@ lldb::TypeSystemSP SwiftASTContext::CreateInstance(lldb::LanguageType language, if (sym_vendor) { // Use the new loadFromSerializedAST if possible: - DataBufferSP ast_file_data_sp = - sym_vendor->GetASTData(eLanguageTypeSwift); + auto ast_file_datas = sym_vendor->GetASTData(eLanguageTypeSwift); bool got_serialized_options = false; - if (ast_file_data_sp) { + DataBufferSP ast_file_data_sp; + if (!ast_file_datas.empty() && + ((ast_file_data_sp = ast_file_datas.front()) != nullptr)) { if (log) - log->Printf("Found AST file data for library: %s.", + log->Printf("Found %d AST file data entries for library: %s.", + (int)ast_file_datas.size(), module->GetSpecificationDescription().c_str()); + + // Retrieve the first serialized AST data blob and initialize + // the compiler invocation with it. llvm::StringRef section_data_ref( (const char *)ast_file_data_sp->GetBytes(), ast_file_data_sp->GetByteSize()); - - swift::serialization::Status result = + auto result = swift_ast_sp->GetCompilerInvocation().loadFromSerializedAST( section_data_ref); @@ -1702,8 +1706,13 @@ lldb::TypeSystemSP SwiftASTContext::CreateInstance(lldb::LanguageType language, if (exe_module_sp) { SymbolVendor *sym_vendor = exe_module_sp->GetSymbolVendor(); if (sym_vendor) { - DataBufferSP ast_data_sp = sym_vendor->GetASTData(eLanguageTypeSwift); - if (ast_data_sp) { + // Retrieve the Swift ASTs from the symbol vendor. + auto ast_datas = sym_vendor->GetASTData(eLanguageTypeSwift); + if (!ast_datas.empty()) { + // We only initialize the compiler invocation with the first + // AST since it initializes some data that must remain static, like + // the SDK path and the triple for the produced output. + auto ast_data_sp = ast_datas.front(); llvm::StringRef section_data_ref( (const char *)ast_data_sp->GetBytes(), ast_data_sp->GetByteSize()); @@ -1716,8 +1725,8 @@ lldb::TypeSystemSP SwiftASTContext::CreateInstance(lldb::LanguageType language, Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES)); if (log) log->Printf("Attempt to load compiler options from Serialized " - "AST failed: %d.", - result); + "AST failed: %d (%zu AST data blobs total).", + result, ast_datas.size()); } } } @@ -3914,6 +3923,8 @@ bool SwiftASTContext::RegisterSectionModules( Module &module, std::vector &module_names) { VALID_OR_RETURN(false); + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES)); + swift::SerializedModuleLoader *sml = GetSerializeModuleLoader(); if (sml) { SectionList *section_list = module.GetSectionList(); @@ -3940,20 +3951,54 @@ bool SwiftASTContext::RegisterSectionModules( SymbolVendor *sym_vendor = module.GetSymbolVendor(); if (sym_vendor) { - DataBufferSP ast_file_data_sp = - sym_vendor->GetASTData(eLanguageTypeSwift); - if (ast_file_data_sp) { - m_ast_file_data_map[&module] = ast_file_data_sp; + // Grab all the AST blobs from the symbol vendor. + auto ast_file_datas = sym_vendor->GetASTData(eLanguageTypeSwift); + if (log) + log->Printf("SwiftASTContext::%s() retrieved %zu AST Data blobs " + "from the symbol vendor.", + __FUNCTION__, ast_file_datas.size()); + + // Add each of the AST blobs to the vector of AST blobs for the + // module. + auto &ast_vector = GetASTVectorForModule(&module); + ast_vector.insert(ast_vector.end(), ast_file_datas.begin(), + ast_file_datas.end()); + + // Retrieve the module names from the AST blobs retrieved from the + // symbol vendor. + size_t parse_fail_count = 0; + size_t ast_number = 0; + for (auto ast_file_data_sp : ast_file_datas) { + // Parse the AST section info from the AST blob. + ++ast_number; llvm::StringRef section_data_ref( (const char *)ast_file_data_sp->GetBytes(), ast_file_data_sp->GetByteSize()); llvm::SmallVector llvm_modules; if (swift::parseASTSection(sml, section_data_ref, llvm_modules)) { + // Collect the LLVM module names referenced by the AST. for (auto module_name : llvm_modules) module_names.push_back(module_name); - return true; + if (log) + log->Printf("SwiftASTContext::%s() - parsed %zu llvm modules " + "from Swift AST section %zu of %zu.", + __FUNCTION__, llvm_modules.size(), ast_number, + ast_file_datas.size()); + } else { + // Keep track of the fact that we failed to parse the AST + // section info. + if (log) + log->Printf("SwiftASTContext::%s() - failed to parse AST " + "section %zu of %zu.", + __FUNCTION__, ast_number, ast_file_datas.size()); + ++parse_fail_count; } } + if (!ast_file_datas.empty() && (parse_fail_count == 0)) { + // We found AST data entries and we successfully parsed all of + // them. + return true; + } } } } @@ -9161,6 +9206,11 @@ DWARFASTParser *SwiftASTContext::GetDWARFParser() { return m_dwarf_ast_parser_ap.get(); } +std::vector & +SwiftASTContext::GetASTVectorForModule(const Module *module) { + return m_ast_file_data_map[const_cast(module)]; +} + SwiftASTContextForExpressions::SwiftASTContextForExpressions(Target &target) : SwiftASTContext(target.GetArchitecture().GetTriple().getTriple().c_str(), &target), diff --git a/source/Symbol/SymbolFile.cpp b/source/Symbol/SymbolFile.cpp index a30d9a19159c..9beb0a3b8f67 100644 --- a/source/Symbol/SymbolFile.cpp +++ b/source/Symbol/SymbolFile.cpp @@ -129,9 +129,10 @@ bool SymbolFile::SymbolContextShouldBeExcluded(const SymbolContext &sc, return false; } -lldb::DataBufferSP SymbolFile::GetASTData(lldb::LanguageType language) { +std::vector +SymbolFile::GetASTData(lldb::LanguageType language) { // SymbolFile subclasses must add this functionality - return lldb::DataBufferSP(); + return std::vector(); } uint32_t SymbolFile::ResolveSymbolContext(const FileSpec &file_spec, diff --git a/source/Symbol/SymbolVendor.cpp b/source/Symbol/SymbolVendor.cpp index 0579ba0cf8b4..6ed988111f3c 100644 --- a/source/Symbol/SymbolVendor.cpp +++ b/source/Symbol/SymbolVendor.cpp @@ -537,12 +537,16 @@ bool SymbolVendor::SymbolContextShouldBeExcluded(const SymbolContext &sc, return false; } -DataBufferSP SymbolVendor::GetASTData(lldb::LanguageType language) { +std::vector +SymbolVendor::GetASTData(lldb::LanguageType language) { + std::vector ast_datas; + + if (language != eLanguageTypeSwift) + return ast_datas; + // Sometimes the AST Section data is found from the module, so look there // first: SectionList *section_list = GetModule()->GetSectionList(); - if (language != eLanguageTypeSwift) - return DataBufferSP(); if (section_list) { SectionSP section_sp( @@ -551,20 +555,20 @@ DataBufferSP SymbolVendor::GetASTData(lldb::LanguageType language) { DataExtractor section_data; if (section_sp->GetSectionData(section_data)) { - return DataBufferSP( + ast_datas.push_back(DataBufferSP( new DataBufferHeap((const char *)section_data.GetDataStart(), - section_data.GetByteSize())); + section_data.GetByteSize()))); + return ast_datas; } } } // If we couldn't find it in the Module, then look for it in the SymbolFile: SymbolFile *sym_file = GetSymbolFile(); - if (sym_file) - return sym_file->GetASTData(language); + ast_datas = sym_file->GetASTData(language); - return DataBufferSP(); + return ast_datas; } bool SymbolVendor::ForceInlineSourceFileCheck() {