Skip to content
This repository has been archived by the owner on Apr 2, 2020. It is now read-only.

support debugging statically-linked Swift binaries on macOS #53

Merged
merged 3 commits into from
Sep 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion include/lldb/Symbol/SwiftASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,8 @@ class SwiftASTContext : public TypeSystem {

bool TargetHasNoSDK();

std::vector<lldb::DataBufferSP> &GetASTVectorForModule(const Module *module);

std::unique_ptr<swift::SourceManager> m_source_manager_ap;
std::unique_ptr<swift::DiagnosticEngine> m_diagnostic_engine_ap;
std::unique_ptr<swift::ASTContext> m_ast_context_ap;
Expand Down Expand Up @@ -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<Module *, lldb::DataBufferSP> ASTFileDataMap;
typedef std::map<Module *, std::vector<lldb::DataBufferSP>> ASTFileDataMap;
ASTFileDataMap m_ast_file_data_map;
/// FIXME: this vector is needed because the LLDBNameLookup debugger clients
/// are being put into
Expand Down
29 changes: 21 additions & 8 deletions include/lldb/Symbol/SymbolFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#ifndef liblldb_SymbolFile_h_
#define liblldb_SymbolFile_h_

#include <vector>

#include "lldb/Core/PluginInterface.h"
#include "lldb/Symbol/CompilerDecl.h"
#include "lldb/Symbol/CompilerDeclContext.h"
Expand Down Expand Up @@ -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<lldb::DataBufferSP>
GetASTData(lldb::LanguageType language);

// Used for the REPL to limit source file ranges that are valid within "file".
// Since
Expand Down
3 changes: 2 additions & 1 deletion include/lldb/Symbol/SymbolVendor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<lldb::DataBufferSP>
GetASTData(lldb::LanguageType language);

virtual bool ForceInlineSourceFileCheck();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

@objc public class A: NSObject {
public func foo() -> Int {
return 4 // Set breakpoint here
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

@objc public class B: NSObject {
public func bar() -> Int {
return 8 // Set breakpoint here
}
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#import <Foundation/Foundation.h>

#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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{'A.swift': {'object': 'A.o'},
'B.swift': {'object': 'B.o'},
}
73 changes: 59 additions & 14 deletions source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<DataBufferSP>
SymbolFileDWARFDebugMap::GetASTData(lldb::LanguageType language) {
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP));

std::vector<DataBufferSP> 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;
}
3 changes: 2 additions & 1 deletion source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<lldb::DataBufferSP>
GetASTData(lldb::LanguageType language) override;

//------------------------------------------------------------------
// PluginInterface protocol
Expand Down