Skip to content

[lldb] Swift OS plugin #9839

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
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
2 changes: 2 additions & 0 deletions lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ class TargetProperties : public Properties {

AutoBool GetSwiftPCMValidation() const;

bool GetSwiftUseTasksPlugin() const;

Args GetSwiftPluginServerForPath() const;

bool GetSwiftAutoImportFrameworks() const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ const char *SwiftLanguageRuntime::GetStandardLibraryBaseName() {
return "swiftCore";
}

const char *SwiftLanguageRuntime::GetConcurrencyLibraryBaseName() {
return "swift_Concurrency";
}

static ConstString GetStandardLibraryName(Process &process) {
// This result needs to be stored in the constructor.
PlatformSP platform_sp(process.GetTarget().GetPlatform());
Expand All @@ -100,6 +104,14 @@ static ConstString GetStandardLibraryName(Process &process) {
return {};
}

static ConstString GetConcurrencyLibraryName(Process &process) {
PlatformSP platform_sp = process.GetTarget().GetPlatform();
if (platform_sp)
return platform_sp->GetFullNameForDylib(
ConstString(SwiftLanguageRuntime::GetConcurrencyLibraryBaseName()));
return {};
}

ConstString SwiftLanguageRuntime::GetStandardLibraryName() {
return ::GetStandardLibraryName(*m_process);
}
Expand All @@ -109,6 +121,12 @@ static bool IsModuleSwiftRuntime(lldb_private::Process &process,
return module.GetFileSpec().GetFilename() == GetStandardLibraryName(process);
}

static bool IsModuleSwiftConcurrency(lldb_private::Process &process,
lldb_private::Module &module) {
return module.GetFileSpec().GetFilename() ==
GetConcurrencyLibraryName(process);
}

AppleObjCRuntimeV2 *
SwiftLanguageRuntime::GetObjCRuntime(lldb_private::Process &process) {
if (auto objc_runtime = ObjCLanguageRuntime::Get(process)) {
Expand All @@ -131,6 +149,11 @@ static bool IsStaticSwiftRuntime(Module &image) {
return image.FindFirstSymbolWithNameAndType(swift_reflection_version_sym);
}

static bool IsStaticSwiftConcurrency(Module &image) {
static const ConstString task_switch_symbol("_swift_task_switch");
return image.FindFirstSymbolWithNameAndType(task_switch_symbol);
}

/// \return the Swift or Objective-C runtime found in the loaded images.
static ModuleSP findRuntime(Process &process, RuntimeKind runtime_kind) {
AppleObjCRuntimeV2 *objc_runtime = nullptr;
Expand Down Expand Up @@ -168,6 +191,52 @@ static ModuleSP findRuntime(Process &process, RuntimeKind runtime_kind) {
return runtime_image;
}

ModuleSP SwiftLanguageRuntime::FindConcurrencyModule(Process &process) {
ModuleSP concurrency_module;
process.GetTarget().GetImages().ForEach([&](const ModuleSP &candidate) {
if (candidate && IsModuleSwiftConcurrency(process, *candidate)) {
concurrency_module = candidate;
return false;
}
return true;
});
if (concurrency_module)
return concurrency_module;

// Do a more expensive search for a statically linked Swift runtime.
process.GetTarget().GetImages().ForEach([&](const ModuleSP &candidate) {
if (candidate && IsStaticSwiftConcurrency(*candidate)) {
concurrency_module = candidate;
return false;
}
return true;
});
return concurrency_module;
}

std::optional<uint32_t>
SwiftLanguageRuntime::FindConcurrencyDebugVersion(Process &process) {
ModuleSP concurrency_module = FindConcurrencyModule(process);
if (!concurrency_module)
return {};

const Symbol *version_symbol =
concurrency_module->FindFirstSymbolWithNameAndType(
ConstString("_swift_concurrency_debug_internal_layout_version"));
if (!version_symbol)
return 0;

addr_t symbol_addr = version_symbol->GetLoadAddress(&process.GetTarget());
if (symbol_addr == LLDB_INVALID_ADDRESS)
return {};
Status error;
uint64_t version = process.ReadUnsignedIntegerFromMemory(
symbol_addr, /*width*/ 4, /*fail_value=*/0, error);
if (error.Fail())
return {};
return version;
}

static std::optional<lldb::addr_t>
FindSymbolForSwiftObject(Process &process, RuntimeKind runtime_kind,
StringRef object, const SymbolType sym_type) {
Expand Down Expand Up @@ -2724,7 +2793,11 @@ llvm::Expected<lldb::addr_t> GetTaskAddrFromThreadLocalStorage(Thread &thread) {
#else
// Compute the thread local storage address for this thread.
addr_t tsd_addr = LLDB_INVALID_ADDRESS;
if (auto info_sp = thread.GetExtendedInfo())

// Look through backing threads when inspecting TLS.
Thread &real_thread =
thread.GetBackingThread() ? *thread.GetBackingThread() : thread;
if (auto info_sp = real_thread.GetExtendedInfo())
if (auto *info_dict = info_sp->GetAsDictionary())
info_dict->GetValueForKeyAsInteger("tsd_address", tsd_addr);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ class SwiftLanguageRuntime : public LanguageRuntime {
static SwiftLanguageRuntime *Get(lldb::ProcessSP process_sp) {
return SwiftLanguageRuntime::Get(process_sp.get());
}

/// Returns the Module containing the Swift Concurrency runtime, if it exists.
static lldb::ModuleSP FindConcurrencyModule(Process &process);

/// Returns the version of the swift concurrency runtime debug layout.
/// If no Concurrency module is found, or if errors occur, nullopt is
/// returned.
/// Returns 0 for versions of the module prior to the introduction
/// of versioning.
static std::optional<uint32_t> FindConcurrencyDebugVersion(Process &process);
/// \}

/// PluginInterface protocol.
Expand Down Expand Up @@ -481,6 +491,8 @@ class SwiftLanguageRuntime : public LanguageRuntime {
static const char *GetErrorBackstopName();
ConstString GetStandardLibraryName();
static const char *GetStandardLibraryBaseName();
static const char *GetConcurrencyLibraryBaseName();

static bool IsSwiftClassName(const char *name);
/// Determines wether \c variable is the "self" object.
static bool IsSelf(Variable &variable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,75 +290,6 @@ static ThunkAction GetThunkAction(ThunkKind kind) {
}
}

/// A thread plan to run to a specific address on a specific async context.
class ThreadPlanRunToAddressOnAsyncCtx : public ThreadPlan {
public:
/// Creates a thread plan to run to destination_addr of an async function
/// whose context is async_ctx.
ThreadPlanRunToAddressOnAsyncCtx(Thread &thread, addr_t destination_addr,
addr_t async_ctx)
: ThreadPlan(eKindGeneric, "run-to-funclet", thread, eVoteNoOpinion,
eVoteNoOpinion),
m_destination_addr(destination_addr), m_expected_async_ctx(async_ctx) {
auto &target = thread.GetProcess()->GetTarget();
m_funclet_bp = target.CreateBreakpoint(destination_addr, true, false);
m_funclet_bp->SetBreakpointKind("async-run-to-funclet");
}

bool ValidatePlan(Stream *error) override {
if (m_funclet_bp->HasResolvedLocations())
return true;

// If we failed to resolve any locations, this plan is invalid.
m_funclet_bp->GetTarget().RemoveBreakpointByID(m_funclet_bp->GetID());
return false;
}

void GetDescription(Stream *s, lldb::DescriptionLevel level) override {
s->PutCString("ThreadPlanRunToAddressOnAsyncCtx to address = ");
s->PutHex64(m_destination_addr);
s->PutCString(" with async ctx = ");
s->PutHex64(m_expected_async_ctx);
}

/// This plan explains the stop if the current async context is the async
/// context this plan was created with.
bool DoPlanExplainsStop(Event *event) override {
if (!HasTID())
return false;
return GetCurrentAsyncContext() == m_expected_async_ctx;
}

/// If this plan explained the stop, it always stops: its sole purpose is to
/// run to the breakpoint it set on the right async function invocation.
bool ShouldStop(Event *event) override {
SetPlanComplete();
return true;
}

/// If this plan said ShouldStop, then its job is complete.
bool MischiefManaged() override {
return IsPlanComplete();
}

bool WillStop() override { return false; }
lldb::StateType GetPlanRunState() override { return eStateRunning; }
bool StopOthers() override { return false; }
void DidPop() override {
m_funclet_bp->GetTarget().RemoveBreakpointByID(m_funclet_bp->GetID());
}

private:
addr_t GetCurrentAsyncContext() {
auto frame_sp = GetThread().GetStackFrameAtIndex(0);
return frame_sp->GetStackID().GetCallFrameAddress();
}

addr_t m_destination_addr;
addr_t m_expected_async_ctx;
BreakpointSP m_funclet_bp;
};

/// Given a thread that is stopped at the start of swift_task_switch, create a
/// thread plan that runs to the address of the resume function.
static ThreadPlanSP
Expand All @@ -383,8 +314,8 @@ CreateRunThroughTaskSwitchThreadPlan(Thread &thread,
if (!async_ctx)
return {};

return std::make_shared<ThreadPlanRunToAddressOnAsyncCtx>(
thread, resume_fn_ptr, async_ctx);
return std::make_shared<ThreadPlanRunToAddress>(thread, resume_fn_ptr,
/*stop_others*/ false);
}

/// Creates a thread plan to step over swift runtime functions that can trigger
Expand Down
1 change: 1 addition & 0 deletions lldb/source/Plugins/OperatingSystem/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
if (LLDB_ENABLE_PYTHON)
add_subdirectory(Python)
add_subdirectory(SwiftTasks)
endif()
12 changes: 12 additions & 0 deletions lldb/source/Plugins/OperatingSystem/SwiftTasks/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_lldb_library(lldbPluginOperatingSystemSwiftTasks PLUGIN
OperatingSystemSwiftTasks.cpp

LINK_LIBS
lldbCore
lldbInterpreter
lldbSymbol
lldbTarget
lldbValueObject
lldbPluginProcessUtility
lldbPluginSwiftLanguageRuntime
)
Loading