Skip to content

Support debugging Swift dylibs that were loaded after SwiftASTContext #3311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 30, 2021
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1949,7 +1949,7 @@ static lldb::ModuleSP GetUnitTestModule(lldb_private::ModuleList &modules) {
/// Scan a newly added lldb::Module fdor Swift modules and report any errors in
/// its module SwiftASTContext to Target.
static void
ProcessModule(ModuleSP &&module_sp, std::string m_description,
ProcessModule(ModuleSP module_sp, std::string m_description,
bool use_all_compiler_flags, Target &target,
std::vector<std::string> &module_search_paths,
std::vector<std::pair<std::string, bool>> &framework_search_paths,
Expand Down Expand Up @@ -5069,6 +5069,37 @@ void SwiftASTContext::PrintDiagnostics(DiagnosticManager &diagnostic_manager,

void SwiftASTContext::ModulesDidLoad(ModuleList &module_list) {
ClearModuleDependentCaches();

// Scan the new modules for Swift contents and try to import it if
// safe, otherwise poison this context.
TargetSP target_sp = GetTarget().lock();
if (!target_sp)
return;

bool use_all_compiler_flags = target_sp->GetUseAllCompilerFlags();
unsigned num_images = module_list.GetSize();
for (size_t mi = 0; mi != num_images; ++mi) {
std::vector<std::string> module_search_paths;
std::vector<std::pair<std::string, bool>> framework_search_paths;
std::vector<std::string> extra_clang_args;
lldb::ModuleSP module_sp = module_list.GetModuleAtIndex(mi);
ProcessModule(module_sp, m_description, use_all_compiler_flags, *target_sp,
module_search_paths, framework_search_paths,
extra_clang_args);
// If the use-all-compiler-flags setting is enabled, the expression
// context is supposed to merge all search paths form all dylibs.
if (use_all_compiler_flags && !extra_clang_args.empty()) {
// We cannot reconfigure ClangImporter after its creation.
// Instead poison the SwiftASTContext so it gets recreated.
m_fatal_errors.SetErrorStringWithFormat(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth adding an API on SwiftASTContext (ClearClangImporter or something) to do this? I worry that having a fatal error pending on the context could get you into trouble if some other subsystem checks for errors but isn't prepared to reconstitute the context.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way to clear clangimporter is to create a new Swift compiler instance (= a new SwiftASTContext). The recreation is transparently handled by Target's GetScratchContext function.

"New Swift image added: %s",
module_sp->GetFileSpec().GetPath().c_str());
}

// Scan the dylib for .swiftast sections.
std::vector<std::string> module_names;
RegisterSectionModules(*module_sp, module_names);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be done for all modules? Or would it be already done for most modules?

}
}

void SwiftASTContext::ClearModuleDependentCaches() {
Expand Down
21 changes: 21 additions & 0 deletions lldb/test/API/lang/swift/late_dylib/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This Makefile recursively calls itself, hence the ?=.
EXE ?= a.out
C_SOURCES ?= loader.c
all: dylib $(EXE)

include Makefile.rules

.PHONY: dylib
dylib:
$(MAKE) MAKE_DSYM=$(MAKE_DSYM) CC=$(CC) SWIFTC=$(SWIFTC) \
ARCH=$(ARCH) DSYMUTIL=$(DSYMUTIL) \
VPATH=$(SRCDIR) -I $(SRCDIR) \
-f $(SRCDIR)/Makefile \
DYLIB_FILENAME=dylib.dylib \
DYLIB_SWIFT_SOURCES=dylib.swift \
DYLIB_NAME=dylib \
DYLIB_ONLY=YES \
C_SOURCES= \
LD_EXTRAS="-lSwiftCore -Xlinker -exported_symbol -Xlinker _f" \
dylib.dylib

21 changes: 21 additions & 0 deletions lldb/test/API/lang/swift/late_dylib/TestSwiftLateDylib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil

class TestSwiftLateDylib(TestBase):

mydir = TestBase.compute_mydir(__file__)

@skipUnlessDarwin
@swiftTest
@skipIfDarwinEmbedded
def test(self):
"""Test that a late loaded Swift dylib is debuggable"""
self.build()
target, process, _, _ = lldbutil.run_to_name_breakpoint(self, "main")
# Initialize SwiftASTContext before loading the dylib.
self.expect("expr -l Swift -- 0")
bkpt = target.BreakpointCreateByLocation(lldb.SBFileSpec('dylib.swift'), 7)
lldbutil.continue_to_breakpoint(process, bkpt)
self.expect("v x", substrs=['Hello from the Dylib'])
self.expect("expr x", substrs=['Hello from the Dylib'])
8 changes: 8 additions & 0 deletions lldb/test/API/lang/swift/late_dylib/dylib.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
struct FromDylib {
let msg = "Hello from the Dylib!"
}

@_silgen_name("f") public func f() {
let x = FromDylib()
print(x) // line 7
}
12 changes: 12 additions & 0 deletions lldb/test/API/lang/swift/late_dylib/loader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>

int main(int argc, const char **argv) {
char *dylib_name = strcat(dirname(argv[0]),"/dylib.dylib");
void *dylib = dlopen(dylib_name, RTLD_NOW);
void (*f)() = dlsym(dylib, "f");
f();
return 0;
}
3 changes: 3 additions & 0 deletions lldb/test/API/lang/swift/late_dylib_clangdeps/ClangMod.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
struct FromClang {
int x;
};
22 changes: 22 additions & 0 deletions lldb/test/API/lang/swift/late_dylib_clangdeps/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This Makefile recursively calls itself, hence the ?=.
EXE ?= a.out
C_SOURCES ?= loader.c
all: dylib $(EXE)

include Makefile.rules

.PHONY: dylib
dylib:
$(MAKE) MAKE_DSYM=$(MAKE_DSYM) CC=$(CC) SWIFTC=$(SWIFTC) \
ARCH=$(ARCH) DSYMUTIL=$(DSYMUTIL) \
VPATH=$(SRCDIR) -I $(SRCDIR) \
-f $(SRCDIR)/Makefile \
DYLIB_FILENAME=dylib.dylib \
DYLIB_SWIFT_SOURCES=dylib.swift \
DYLIB_NAME=dylib \
DYLIB_ONLY=YES \
SWIFTFLAGS_EXTRAS="-Xcc -I$(SRCDIR)" \
C_SOURCES= \
LD_EXTRAS="-lSwiftCore -Xlinker -exported_symbol -Xlinker _f" \
dylib.dylib

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil

class TestSwiftLateDylibClangDeps(TestBase):

mydir = TestBase.compute_mydir(__file__)

@skipUnlessDarwin
@swiftTest
@skipIfDarwinEmbedded
def test(self):
"""Test that a late loaded Swift dylib with Clang dependencies is debuggable"""
self.build()
target, process, _, _ = lldbutil.run_to_name_breakpoint(self, "main")
# Initialize SwiftASTContext before loading the dylib.
self.expect("expr -l Swift -- 0")
bkpt = target.BreakpointCreateByLocation(lldb.SBFileSpec('dylib.swift'), 5)
lldbutil.continue_to_breakpoint(process, bkpt)
self.expect("v x", substrs=['42'])
self.expect("expr x", substrs=['42'])
6 changes: 6 additions & 0 deletions lldb/test/API/lang/swift/late_dylib_clangdeps/dylib.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import ClangMod

@_silgen_name("f") public func f() {
let x = FromClang(x: 42)
print(x) // line 5
}
12 changes: 12 additions & 0 deletions lldb/test/API/lang/swift/late_dylib_clangdeps/loader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>

int main(int argc, const char **argv) {
char *dylib_name = strcat(dirname(argv[0]),"/dylib.dylib");
void *dylib = dlopen(dylib_name, RTLD_NOW);
void (*f)() = dlsym(dylib, "f");
f();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module ClangMod {
header "ClangMod.h"
}