diff --git a/lldb/source/Plugins/Language/Swift/SwiftFormatters.cpp b/lldb/source/Plugins/Language/Swift/SwiftFormatters.cpp index 220742361c1e0..56aed0f5a2076 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftFormatters.cpp +++ b/lldb/source/Plugins/Language/Swift/SwiftFormatters.cpp @@ -15,6 +15,7 @@ #include "Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h" #include "Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "Plugins/TypeSystem/Swift/SwiftDemangle.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/DataFormatters/StringPrinter.h" #include "lldb/Symbol/CompilerType.h" @@ -28,6 +29,7 @@ #include "lldb/ValueObject/ValueObject.h" #include "lldb/lldb-enumerations.h" #include "swift/AST/Types.h" +#include "swift/Demangling/Demangle.h" #include "swift/Demangling/ManglingMacros.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" @@ -761,10 +763,10 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd { auto ts_or_err = target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeSwift); if (auto err = ts_or_err.takeError()) { - LLDB_LOG( - GetLog(LLDBLog::DataFormatters | LLDBLog::Types), - "could not get Swift type system for Task synthetic provider: {0}", - fmt_consume(std::move(err))); + LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters | LLDBLog::Types), + std::move(err), + "could not get Swift type system for Task synthetic " + "provider: {0}"); return; } m_ts = llvm::dyn_cast_or_null(ts_or_err->get()); @@ -782,6 +784,7 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd { "isStatusRecordLocked", "isEscalated", "isEnqueued", + "children", "isRunning", }; @@ -837,7 +840,22 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd { RETURN_CHILD(m_is_escalated_sp, isEscalated, bool_type); case 10: RETURN_CHILD(m_is_enqueued_sp, isEnqueued, bool_type); - case 11: + case 11: { + if (!m_child_tasks_sp) { + const auto &tasks = m_task_info.childTasks; + std::string mangled_typename = + mangledTypenameForTasksTuple(tasks.size()); + CompilerType tasks_tuple_type = + m_ts->GetTypeFromMangledTypename(ConstString(mangled_typename)); + DataExtractor data{tasks.data(), tasks.size() * sizeof(tasks[0]), + endian::InlHostByteOrder(), sizeof(void *)}; + m_child_tasks_sp = ValueObject::CreateValueObjectFromData( + "children", data, m_backend.GetExecutionContextRef(), + tasks_tuple_type); + } + return m_child_tasks_sp; + } + case 12: RETURN_CHILD(m_is_running_sp, isRunning, bool_type); default: return {}; @@ -858,9 +876,9 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd { llvm::Expected task_info = reflection_ctx->asyncTaskInfo(task_ptr); if (auto err = task_info.takeError()) { - LLDB_LOG(GetLog(LLDBLog::DataFormatters | LLDBLog::Types), - "could not get info for async task {0:x}: {1}", task_ptr, - fmt_consume(std::move(err))); + LLDB_LOG_ERROR( + GetLog(LLDBLog::DataFormatters | LLDBLog::Types), std::move(err), + "could not get info for async task {0:x}: {1}", task_ptr); } else { m_task_info = *task_info; for (auto child : @@ -868,7 +886,7 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd { m_is_future_sp, m_is_group_child_task_sp, m_is_async_let_task_sp, m_is_cancelled_sp, m_is_status_record_locked_sp, m_is_escalated_sp, - m_is_enqueued_sp, m_is_running_sp}) + m_is_enqueued_sp, m_child_tasks_sp, m_is_running_sp}) child.reset(); } } @@ -886,6 +904,35 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd { return std::distance(children.begin(), it); } +private: + std::string mangledTypenameForTasksTuple(size_t count) { + /* + Global > TypeMangling > Type > Tuple + TupleElement > Type > Structure + Module, text="Swift" + Identifier, text="UnsafeCurrentTask" + */ + using namespace ::swift::Demangle; + using Kind = Node::Kind; + NodeFactory factory; + auto [root, tuple] = swift_demangle::MakeNodeChain( + {Kind::TypeMangling, Kind::Type, Kind::Tuple}, factory); + + // Make a TupleElement subtree N times, where N is the number of subtasks. + for (size_t i = 0; i < count; ++i) { + auto *structure = swift_demangle::MakeNodeChain( + tuple, {Kind::TupleElement, Kind::Type, Kind::Structure}, factory); + if (structure) { + structure->addChild( + factory.createNode(Kind::Module, ::swift::STDLIB_NAME), factory); + structure->addChild( + factory.createNode(Kind::Identifier, "UnsafeCurrentTask"), factory); + } + } + + return mangleNode(root).result(); + } + private: TypeSystemSwiftTypeRef *m_ts = nullptr; ReflectionContextInterface::AsyncTaskInfo m_task_info; @@ -900,6 +947,7 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd { ValueObjectSP m_is_status_record_locked_sp; ValueObjectSP m_is_escalated_sp; ValueObjectSP m_is_enqueued_sp; + ValueObjectSP m_child_tasks_sp; ValueObjectSP m_is_running_sp; }; } diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp index ec6fc373d45fb..d18a1ba4b0bfd 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp @@ -405,6 +405,8 @@ class TargetReflectionContext : public ReflectionContextInterface { result.kind = task_info.Kind; result.enqueuePriority = task_info.EnqueuePriority; result.resumeAsyncContext = task_info.ResumeAsyncContext; + for (auto child : task_info.ChildTasks) + result.childTasks.push_back(child); return result; } diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h b/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h index 73297152d23dd..3d7f8517119a6 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h +++ b/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h @@ -168,6 +168,7 @@ class ReflectionContextInterface { uint32_t kind = 0; uint32_t enqueuePriority = 0; lldb::addr_t resumeAsyncContext = LLDB_INVALID_ADDRESS; + std::vector childTasks; }; // The default limits are copied from swift-inspect. virtual llvm::Expected diff --git a/lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h b/lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h index dbef04426c9ff..8946f5b4ea31a 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h +++ b/lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h @@ -17,6 +17,7 @@ #include "swift/Demangling/Demangler.h" #include "swift/Demangling/ManglingFlavor.h" #include "llvm/ADT/ArrayRef.h" +#include namespace lldb_private { namespace swift_demangle { @@ -97,6 +98,41 @@ GetDemangledTypeMangling(swift::Demangle::Demangler &dem, return GetTypeMangling(dem.demangleSymbol(name)); } +/// Create a parent-child chain of demangle nodes for the specified node kinds. +/// +/// \param initial[in] The starting node to construct the chain from. +/// \param kinds[in] The node kind for each created node. +/// \param factory[in] The factory to create nodes. +/// \return The tail leaf node created, whose kind is the last kind in \c kinds. +inline NodePointer +MakeNodeChain(NodePointer initial, + std::initializer_list kinds, + NodeFactory &factory) { + auto *current = initial; + for (auto kind : kinds) { + if (!current) + return nullptr; + auto *child = factory.createNode(kind); + current->addChild(child, factory); + current = child; + } + return current; +} + +/// Create a parent-child chain of demangle nodes for the specified node kinds. +/// +/// \param initial[in] The starting node to construct the chain from. +/// \param kinds[in] The node kind for each created node. +/// \param factory[in] The factory to create nodes. +/// \return A pair of nodes: the head and tail of the constructed nodes. +inline std::pair +MakeNodeChain(std::initializer_list kinds, + NodeFactory &factory) { + auto *root = factory.createNode(Node::Kind::Global); + auto *leaf = MakeNodeChain(root, kinds, factory); + return {root, leaf}; +} + /// Wrap node in Global/TypeMangling/Type. inline swift::Demangle::NodePointer MangleType(swift::Demangle::Demangler &dem, diff --git a/lldb/test/API/lang/swift/async/formatters/task/TestSwiftTaskSyntheticProvider.py b/lldb/test/API/lang/swift/async/formatters/task/TestSwiftTaskSyntheticProvider.py index 7eb516fadbcfb..8538038360d21 100644 --- a/lldb/test/API/lang/swift/async/formatters/task/TestSwiftTaskSyntheticProvider.py +++ b/lldb/test/API/lang/swift/async/formatters/task/TestSwiftTaskSyntheticProvider.py @@ -28,6 +28,7 @@ def test_top_level_task(self): "isAsyncLetTask = false", "isCancelled = false", "isEnqueued = ", + "children = {}", ], ) @@ -49,5 +50,6 @@ def test_current_task(self): "isAsyncLetTask = true", "isCancelled = false", "isEnqueued = false", + "children = {}", ], ) diff --git a/lldb/test/API/lang/swift/async/formatters/task/children/Makefile b/lldb/test/API/lang/swift/async/formatters/task/children/Makefile new file mode 100644 index 0000000000000..cca30b939e652 --- /dev/null +++ b/lldb/test/API/lang/swift/async/formatters/task/children/Makefile @@ -0,0 +1,3 @@ +SWIFT_SOURCES := main.swift +SWIFTFLAGS_EXTRAS := -parse-as-library +include Makefile.rules diff --git a/lldb/test/API/lang/swift/async/formatters/task/children/TestSwiftSyntheticTaskChildren.py b/lldb/test/API/lang/swift/async/formatters/task/children/TestSwiftSyntheticTaskChildren.py new file mode 100644 index 0000000000000..21f256b906993 --- /dev/null +++ b/lldb/test/API/lang/swift/async/formatters/task/children/TestSwiftSyntheticTaskChildren.py @@ -0,0 +1,31 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + + @skipUnlessDarwin + @swiftTest + def test(self): + """""" + self.build() + lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.swift") + ) + self.expect( + "language swift task info", + substrs=[ + "(UnsafeCurrentTask) current_task = {", + "id = 1", + "isChildTask = false", + "isAsyncLetTask = false", + "children = {", + "0 = {", + "id = 2", + "isChildTask = true", + "isAsyncLetTask = true", + "children = {}", + ], + ) diff --git a/lldb/test/API/lang/swift/async/formatters/task/children/main.swift b/lldb/test/API/lang/swift/async/formatters/task/children/main.swift new file mode 100644 index 0000000000000..5d6e02a1e4072 --- /dev/null +++ b/lldb/test/API/lang/swift/async/formatters/task/children/main.swift @@ -0,0 +1,11 @@ +func f() async -> Int { + try? await Task.sleep(for: .seconds(300)) + return 30 +} + +@main struct Main { + static func main() async { + async let number = f() + await print("break here \(number)") + } +}