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

Commit 88e778c

Browse files
authored
Merge pull request #53 from tfiala/SR-2637-v2
support debugging statically-linked Swift binaries on macOS
2 parents 169bfb8 + a646342 commit 88e778c

File tree

14 files changed

+340
-50
lines changed

14 files changed

+340
-50
lines changed

include/lldb/Symbol/SwiftASTContext.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,8 @@ class SwiftASTContext : public TypeSystem {
822822

823823
bool TargetHasNoSDK();
824824

825+
std::vector<lldb::DataBufferSP> &GetASTVectorForModule(const Module *module);
826+
825827
std::unique_ptr<swift::SourceManager> m_source_manager_ap;
826828
std::unique_ptr<swift::DiagnosticEngine> m_diagnostic_engine_ap;
827829
std::unique_ptr<swift::ASTContext> m_ast_context_ap;
@@ -850,7 +852,7 @@ class SwiftASTContext : public TypeSystem {
850852
// target's process pointer be filled in
851853
std::string m_platform_sdk_path;
852854
std::string m_resource_dir;
853-
typedef std::map<Module *, lldb::DataBufferSP> ASTFileDataMap;
855+
typedef std::map<Module *, std::vector<lldb::DataBufferSP>> ASTFileDataMap;
854856
ASTFileDataMap m_ast_file_data_map;
855857
/// FIXME: this vector is needed because the LLDBNameLookup debugger clients
856858
/// are being put into

include/lldb/Symbol/SymbolFile.h

+21-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#ifndef liblldb_SymbolFile_h_
1111
#define liblldb_SymbolFile_h_
1212

13+
#include <vector>
14+
1315
#include "lldb/Core/PluginInterface.h"
1416
#include "lldb/Symbol/CompilerDecl.h"
1517
#include "lldb/Symbol/CompilerDeclContext.h"
@@ -223,15 +225,26 @@ class SymbolFile : public PluginInterface {
223225
virtual bool ForceInlineSourceFileCheck();
224226

225227
//------------------------------------------------------------------
226-
// Symbol files can store AST data for any language that wants to
227-
// store the native AST format supported by the current compiler.
228-
// This information is often only usable by a compiler that is in
229-
// sync with the compiler sources that were used to build LLDB so
230-
// any data should be versioned appropriately so the compiler can
231-
// try to load the data and know if the data will be able to be
232-
// used.
228+
/// Retrieve all the AST data blobs from the SymbolFile.
229+
///
230+
/// Symbol files can store AST data for any language that wants to
231+
/// store the native AST format supported by the current compiler.
232+
/// This information is often only usable by a compiler that is in
233+
/// sync with the compiler sources that were used to build LLDB so
234+
/// any data should be versioned appropriately so the compiler can
235+
/// try to load the data and know if the data will be able to be
236+
/// used.
237+
///
238+
/// @param[in] language
239+
/// The language for which AST data is being requested.
240+
/// A given file can contain ASTs for more than one language.
241+
///
242+
/// @return
243+
/// Zero or more buffers, each of which contain the raw data
244+
/// of an AST in the requested language.
233245
//------------------------------------------------------------------
234-
virtual lldb::DataBufferSP GetASTData(lldb::LanguageType language);
246+
virtual std::vector<lldb::DataBufferSP>
247+
GetASTData(lldb::LanguageType language);
235248

236249
// Used for the REPL to limit source file ranges that are valid within "file".
237250
// Since

include/lldb/Symbol/SymbolVendor.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ class SymbolVendor : public ModuleChild, public PluginInterface {
163163
virtual bool SymbolContextShouldBeExcluded(const SymbolContext &sc,
164164
uint32_t actual_line);
165165

166-
virtual lldb::DataBufferSP GetASTData(lldb::LanguageType language);
166+
virtual std::vector<lldb::DataBufferSP>
167+
GetASTData(lldb::LanguageType language);
167168

168169
virtual bool ForceInlineSourceFileCheck();
169170

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Foundation
2+
3+
@objc public class A: NSObject {
4+
public func foo() -> Int {
5+
return 4 // Set breakpoint here
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Foundation
2+
3+
@objc public class B: NSObject {
4+
public func bar() -> Int {
5+
return 8 // Set breakpoint here
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Build swift modules with debug info
2+
3+
LEVEL=../../../../make
4+
5+
# Don't use 'all' target. There is a default build rule that will kick in that
6+
# will be wrong. WE use 'first' so that the normal 'make' command (without
7+
# a target) selects the first (but not 'all') target so we avoid the undesired
8+
# default behavior.
9+
first: main
10+
11+
include $(LEVEL)/Makefile.rules
12+
13+
# To use the path commented out below, which is what we'd really want to do,
14+
# we'd also need to require that the Swift standard library be built along
15+
# with the compiler. I'd like to avoid that requirement.
16+
# SWIFT_LIB_DIR=$(dir $(SWIFTCC))../lib
17+
SWIFT_LIB_DIR="$(shell xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx"
18+
19+
main: A.o B.o objc_main.m
20+
$(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)
21+
22+
A.o: A.swift
23+
$(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
24+
25+
B.o: B.swift
26+
$(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
27+
28+
clean::
29+
rm -f *.o main *-Swift.h *.swiftmodule *.swiftdoc
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# TestSwiftStaticLinkingMacOS.py
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ------------------------------------------------------------------------------
12+
"""
13+
Test that macOS can statically link two separately-compiled Swift modules
14+
with one Objective-C module, link them through the clang driver, and still
15+
access debug info for each of the Swift modules.
16+
"""
17+
from __future__ import print_function
18+
19+
20+
# System imports
21+
import os
22+
import commands
23+
24+
# Third-party imports
25+
26+
# LLDB imports
27+
import lldb
28+
from lldbsuite.test.lldbtest import TestBase
29+
from lldbsuite.test import decorators, lldbtest, lldbutil
30+
31+
32+
class SwiftStaticLinkingMacOSTestCase(TestBase):
33+
34+
mydir = TestBase.compute_mydir(__file__)
35+
36+
NO_DEBUG_INFO_TESTCASE = True
37+
38+
def expect_self_var_available_at_breakpoint(
39+
self, process, breakpoint, module_name):
40+
# Frame #0 should be at the given breakpoint
41+
threads = lldbutil.get_threads_stopped_at_breakpoint(
42+
process, breakpoint)
43+
44+
self.assertEquals(1, len(threads))
45+
self.thread = threads[0]
46+
self.frame = self.thread.frames[0]
47+
self.assertTrue(self.frame, "Frame 0 is valid.")
48+
49+
patterns = [
50+
# Ensure we report a self with an address.
51+
r"self\s*=\s*0x[0-9a-fA-F]+",
52+
# Ensure we think it is an NSObject.
53+
r"ObjectiveC.NSObject"]
54+
substrs = [
55+
"(%s.%s)" % (module_name, module_name)
56+
]
57+
self.expect("frame variable self", patterns=patterns,
58+
substrs=substrs)
59+
60+
@decorators.skipUnlessDarwin
61+
def test_variables_print_from_both_swift_modules(self):
62+
"""Test that variables from two modules can be accessed."""
63+
self.build()
64+
65+
# I could not find a reasonable way to say "skipUnless(archs=[])".
66+
# That would probably be worth adding.
67+
if self.getArchitecture() != 'x86_64':
68+
self.skipTest("This test requires x86_64 as the architecture "
69+
"for the inferior")
70+
71+
exe_name = "main"
72+
src_a = lldb.SBFileSpec("A.swift")
73+
line_a = 5
74+
src_b = lldb.SBFileSpec("B.swift")
75+
line_b = 5
76+
exe = os.path.join(os.getcwd(), exe_name)
77+
78+
# Create the target
79+
target = self.dbg.CreateTarget(exe)
80+
self.assertTrue(target, lldbtest.VALID_TARGET)
81+
82+
# Set the breakpoints
83+
# breakpoint_a = target.BreakpointCreateBySourceRegex(
84+
# 'Set breakpoint here', src_a)
85+
breakpoint_a = target.BreakpointCreateByLocation(
86+
src_a, line_a)
87+
self.assertTrue(breakpoint_a.GetNumLocations() > 0,
88+
lldbtest.VALID_BREAKPOINT)
89+
90+
# breakpoint_b = target.BreakpointCreateBySourceRegex(
91+
# 'Set breakpoint here', src_b)
92+
breakpoint_b = target.BreakpointCreateByLocation(
93+
src_b, line_b)
94+
self.assertTrue(breakpoint_b.GetNumLocations() > 0,
95+
lldbtest.VALID_BREAKPOINT)
96+
97+
# Launch the process, and do not stop at the entry point.
98+
envp = ['DYLD_FRAMEWORK_PATH=.']
99+
process = target.LaunchSimple(None, envp, os.getcwd())
100+
101+
self.assertTrue(process, lldbtest.PROCESS_IS_VALID)
102+
103+
# We should be at breakpoint in module A.
104+
self.expect_self_var_available_at_breakpoint(
105+
process, breakpoint_a, "A")
106+
107+
# Jump to the next breakpoint
108+
process.Continue()
109+
110+
# We should be at breakpoint in module B.
111+
self.expect_self_var_available_at_breakpoint(
112+
process, breakpoint_b, "B")
113+
114+
return
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#import <Foundation/Foundation.h>
2+
3+
#import "A-Swift.h"
4+
#import "B-Swift.h"
5+
6+
int main(int argc, const char * argv[]) {
7+
@autoreleasepool {
8+
NSLog(@"Hello, World!");
9+
NSLog(@"A = %ld", [[[A alloc] init] foo]);
10+
NSLog(@"B = %ld", [[[B alloc] init] bar]);
11+
}
12+
return 0;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{'A.swift': {'object': 'A.o'},
2+
'B.swift': {'object': 'B.o'},
3+
}

source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp

+59-14
Original file line numberDiff line numberDiff line change
@@ -1441,20 +1441,65 @@ SymbolFileDWARFDebugMap::AddOSOARanges(SymbolFileDWARF *dwarf2Data,
14411441
return num_line_entries_added;
14421442
}
14431443

1444-
DataBufferSP SymbolFileDWARFDebugMap::GetASTData(lldb::LanguageType language) {
1445-
if (language == eLanguageTypeSwift) {
1446-
Symtab *symtab = m_obj_file->GetSymtab();
1447-
if (symtab) {
1448-
uint32_t start_idx = 0;
1449-
Symbol *symbol =
1450-
symtab->FindSymbolWithType(eSymbolTypeASTFile, Symtab::eDebugAny,
1451-
Symtab::eVisibilityAny, start_idx);
1452-
if (symbol) {
1453-
FileSpec file_spec(symbol->GetName().GetCString(), false);
1454-
if (file_spec.Exists())
1455-
return file_spec.ReadFileContents();
1444+
std::vector<DataBufferSP>
1445+
SymbolFileDWARFDebugMap::GetASTData(lldb::LanguageType language) {
1446+
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP));
1447+
1448+
std::vector<DataBufferSP> ast_datas;
1449+
if (language != eLanguageTypeSwift) {
1450+
if (log)
1451+
log->Printf("SymbolFileDWARFDebugMap::%s() - ignoring because not Swift",
1452+
__FUNCTION__);
1453+
return ast_datas;
1454+
}
1455+
1456+
Symtab *symtab = m_obj_file->GetSymtab();
1457+
if (!symtab) {
1458+
if (log)
1459+
log->Printf("SymbolFileDWARFDebugMap::%s() - ignoring because the obj "
1460+
"file has no symbol table",
1461+
__FUNCTION__);
1462+
return ast_datas;
1463+
}
1464+
1465+
uint32_t next_idx = 0;
1466+
bool done = false;
1467+
do {
1468+
Symbol *symbol =
1469+
symtab->FindSymbolWithType(eSymbolTypeASTFile, Symtab::eDebugAny,
1470+
Symtab::eVisibilityAny, next_idx);
1471+
if (symbol == nullptr) {
1472+
// We didn't find any more symbols of type eSymbolTypeASTFile. We are
1473+
// done looping for them.
1474+
done = true;
1475+
} else {
1476+
// Try to load the specified file.
1477+
FileSpec file_spec(symbol->GetName().GetCString(), false);
1478+
if (file_spec.Exists()) {
1479+
// We found the source data for the AST data blob.
1480+
// Read it in and add it to our return vector.
1481+
ast_datas.push_back(file_spec.ReadFileContents());
1482+
if (log)
1483+
log->Printf("SymbolFileDWARFDebugMap::%s() - found and loaded AST "
1484+
"data from file %s",
1485+
__FUNCTION__, file_spec.GetPath().c_str());
1486+
} else {
1487+
if (log)
1488+
log->Printf("SymbolFileDWARFDebugMap::%s() - found reference to AST "
1489+
"file %s, but could not find the file, ignoring",
1490+
__FUNCTION__, file_spec.GetPath().c_str());
14561491
}
1492+
1493+
// Regardless of whether we could find the specified file, start the next
1494+
// symbol search at the index past the one we just found.
1495+
++next_idx;
14571496
}
1458-
}
1459-
return DataBufferSP();
1497+
} while (!done);
1498+
1499+
// Return the vector of AST data blobs
1500+
if (log)
1501+
log->Printf("SymbolFileDWARFDebugMap::%s() - returning %d AST data blobs",
1502+
__FUNCTION__, (int)ast_datas.size());
1503+
1504+
return ast_datas;
14601505
}

source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ class SymbolFileDWARFDebugMap : public lldb_private::SymbolFile {
122122
uint32_t type_mask,
123123
lldb_private::TypeList &type_list) override;
124124

125-
lldb::DataBufferSP GetASTData(lldb::LanguageType language) override;
125+
std::vector<lldb::DataBufferSP>
126+
GetASTData(lldb::LanguageType language) override;
126127

127128
//------------------------------------------------------------------
128129
// PluginInterface protocol

0 commit comments

Comments
 (0)