Skip to content

[lldb] Introduce Swift "task info" command #9875

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
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "SwiftLanguageRuntime.h"
#include "Plugins/LanguageRuntime/Swift/LLDBMemoryReader.h"
#include "Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h"
#include "ReflectionContextInterface.h"
#include "SwiftMetadataCache.h"

Expand All @@ -34,6 +35,7 @@
#include "lldb/Interpreter/CommandObject.h"
#include "lldb/Interpreter/CommandObjectMultiword.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Symbol/FuncUnwinders.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/VariableList.h"
Expand All @@ -43,6 +45,7 @@
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/OptionParsing.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/Timer.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/ValueObject/ValueObjectCast.h"
Expand All @@ -52,15 +55,18 @@
#include "lldb/lldb-enumerations.h"
#include "swift/AST/ASTMangler.h"
#include "swift/Demangling/Demangle.h"
#include "swift/RemoteInspection/ReflectionContext.h"
#include "swift/RemoteAST/RemoteAST.h"
#include "swift/RemoteInspection/ReflectionContext.h"
#include "swift/Threading/ThreadLocalStorage.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"

#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/Memory.h"

// FIXME: we should not need this
Expand Down Expand Up @@ -2181,6 +2187,61 @@ class CommandObjectLanguageSwiftTaskSelect final : public CommandObjectParsed {
}
};

class CommandObjectLanguageSwiftTaskInfo final : public CommandObjectParsed {
public:
CommandObjectLanguageSwiftTaskInfo(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "info",
"Print the Task being run on the current thread.") {
}

private:
void DoExecute(Args &command, CommandReturnObject &result) override {
if (!m_exe_ctx.GetThreadPtr()) {
result.AppendError("must be run from a running process and valid thread");
return;
}

auto task_addr_or_err =
GetTaskAddrFromThreadLocalStorage(m_exe_ctx.GetThreadRef());
if (auto error = task_addr_or_err.takeError()) {
result.AppendError(toString(std::move(error)));
return;
}

auto ts_or_err = m_exe_ctx.GetTargetRef().GetScratchTypeSystemForLanguage(
eLanguageTypeSwift);
if (auto error = ts_or_err.takeError()) {
result.AppendErrorWithFormatv("could not get Swift type system: {0}",
llvm::fmt_consume(std::move(error)));
return;
}

auto *ts = llvm::dyn_cast_or_null<TypeSystemSwiftTypeRef>(ts_or_err->get());
if (!ts) {
result.AppendError("could not get Swift type system");
return;
}

addr_t task_addr = task_addr_or_err.get();
// TypeMangling for "Swift.UnsafeCurrentTask"
CompilerType task_type =
ts->GetTypeFromMangledTypename(ConstString("$sSctD"));
auto task_sp = ValueObject::CreateValueObjectFromAddress(
"current_task", task_addr, m_exe_ctx, task_type, false);
if (auto synthetic_sp = task_sp->GetSyntheticValue())
task_sp = synthetic_sp;

auto error = task_sp->Dump(result.GetOutputStream());
if (error) {
result.AppendErrorWithFormatv("failed to print current task: {0}",
toString(std::move(error)));
return;
}

result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
}
};

class CommandObjectLanguageSwiftTask final : public CommandObjectMultiword {
public:
CommandObjectLanguageSwiftTask(CommandInterpreter &interpreter)
Expand All @@ -2193,6 +2254,9 @@ class CommandObjectLanguageSwiftTask final : public CommandObjectMultiword {
LoadSubCommand(
"select",
CommandObjectSP(new CommandObjectLanguageSwiftTaskSelect(interpreter)));
LoadSubCommand(
"info",
CommandObjectSP(new CommandObjectLanguageSwiftTaskInfo(interpreter)));
}
};

Expand Down Expand Up @@ -2639,4 +2703,33 @@ std::optional<lldb::addr_t> SwiftLanguageRuntime::TrySkipVirtualParentProlog(
: sc.function->GetPrologueByteSize();
return pc_value + prologue_size;
}

llvm::Expected<lldb::addr_t> GetTaskAddrFromThreadLocalStorage(Thread &thread) {
#if !SWIFT_THREADING_USE_RESERVED_TLS_KEYS

Choose a reason for hiding this comment

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

Oh you flipped the condition later?

Copy link
Author

Choose a reason for hiding this comment

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

The swift::tis_get_key function is available only on Darwin. Using SWIFT_THREADING_USE_RESERVED_TLS_KEYS is to ensure that swift::tls_get_key exists. If that compiler condition is false, then this function (GetTaskAddrFromThreadLocalStorage) will return an error. On Linux and Windows we'll need to figure out how to implement this, where thread_local variables are used instead of TSD.

return llvm::createStringError(
"getting the current task from a thread is not supported");
#else
// Compute the thread local storage address for this thread.
addr_t tsd_addr = LLDB_INVALID_ADDRESS;
if (auto info_sp = thread.GetExtendedInfo())
if (auto *info_dict = info_sp->GetAsDictionary())
info_dict->GetValueForKeyAsInteger("tsd_address", tsd_addr);

if (tsd_addr == LLDB_INVALID_ADDRESS)
return llvm::createStringError("could not read current task from thread");

// Offset of the Task pointer in a Thread's local storage.
Process &process = *thread.GetProcess();
size_t ptr_size = process.GetAddressByteSize();
uint64_t task_ptr_offset_in_tls =
swift::tls_get_key(swift::tls_key::concurrency_task) * ptr_size;
addr_t task_addr_location = tsd_addr + task_ptr_offset_in_tls;
Status status;
addr_t task_addr = process.ReadPointerFromMemory(task_addr_location, status);
if (status.Fail())
return llvm::createStringError("could not get current task from thread: %s",
status.AsCString());
return task_addr;
#endif
}
} // namespace lldb_private
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,10 @@ struct AsyncUnwindRegisterNumbers {

std::optional<AsyncUnwindRegisterNumbers>
GetAsyncUnwindRegisterNumbers(llvm::Triple::ArchType triple);

/// Inspects thread local storage to find the address of the currently executing
/// task.
llvm::Expected<lldb::addr_t> GetTaskAddrFromThreadLocalStorage(Thread &thread);
} // namespace lldb_private

#endif // liblldb_SwiftLanguageRuntime_h_
3 changes: 3 additions & 0 deletions lldb/test/API/lang/swift/async/tasks/info/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SWIFT_SOURCES := main.swift
SWIFTFLAGS_EXTRAS := -parse-as-library
include Makefile.rules
27 changes: 27 additions & 0 deletions lldb/test/API/lang/swift/async/tasks/info/TestSwiftTaskInfo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import TestBase
import lldbsuite.test.lldbutil as lldbutil

import re


def _tail(output):
"""Delete the first line of output text."""
result, _ = re.subn(r"^.*\n", "", output, count=1)
return result


class TestCase(TestBase):

@skipUnlessDarwin
Copy link
Member

Choose a reason for hiding this comment

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

Is this conceptual for the features tested in async/tasks? Or is this a workaround for the Windows test failure linked above? In that case, can we mark it xfail instead, so that it pops up when this is fixed?

Copy link
Author

Choose a reason for hiding this comment

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

this is specific to this task info command. A thread's current task is stored differently on Darwin, vs other platforms. This PR supports only Darwin, a follow up PR will need to support other platforms.

def test(self):
self.build()
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.swift")
)
self.runCmd("frame variable task")
frame_variable_output = self.res.GetOutput()
self.runCmd("language swift task info")
task_info_output = self.res.GetOutput()
self.assertEqual(_tail(task_info_output), _tail(frame_variable_output))
9 changes: 9 additions & 0 deletions lldb/test/API/lang/swift/async/tasks/info/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@main struct Main {
static func main() async {
withUnsafeCurrentTask { task in
if let task {
print("break here")
}
}
}
}