diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp index df000ba74ec36..65b49100c7275 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp @@ -2086,6 +2086,46 @@ class CommandObjectSwift_RefCount : public CommandObjectRaw { } }; +/// Construct a `ThreadTask` instance for a Task variable contained in the first +/// argument. +static llvm::Expected +ThreadForTaskVariable(Args &command, ExecutionContext &exe_ctx) { + if (!exe_ctx.GetFramePtr()) + return llvm::createStringError("no active frame selected"); + + if (command.empty() || command[0].ref().empty()) + return llvm::createStringError("missing task variable argument"); + + StackFrame &frame = exe_ctx.GetFrameRef(); + uint32_t path_options = + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess; + VariableSP var_sp; + Status status; + ValueObjectSP valobj_sp = frame.GetValueForVariableExpressionPath( + command[0].c_str(), eDynamicDontRunTarget, path_options, var_sp, status); + if (!valobj_sp) + return status.takeError(); + + addr_t task_ptr = LLDB_INVALID_ADDRESS; + ThreadSafeReflectionContext reflection_ctx; + if (ValueObjectSP task_obj_sp = valobj_sp->GetChildMemberWithName("_task")) { + task_ptr = task_obj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + if (task_ptr != LLDB_INVALID_ADDRESS) + if (auto *runtime = SwiftLanguageRuntime::Get(exe_ctx.GetProcessSP())) + reflection_ctx = runtime->GetReflectionContext(); + } + if (task_ptr == LLDB_INVALID_ADDRESS || !reflection_ctx) + return llvm::createStringError("failed to access Task data from runtime"); + + llvm::Expected task_info = + reflection_ctx->asyncTaskInfo(task_ptr); + if (auto error = task_info.takeError()) + return error; + + return ThreadTask::Create(task_info->id, task_info->resumeAsyncContext, + exe_ctx); +} + class CommandObjectLanguageSwiftTaskBacktrace final : public CommandObjectParsed { public: @@ -2099,60 +2139,44 @@ class CommandObjectLanguageSwiftTaskBacktrace final private: void DoExecute(Args &command, CommandReturnObject &result) override { - if (!m_exe_ctx.GetFramePtr()) { - result.AppendError("no active frame selected"); - return; - } - - if (command.empty() || command[0].ref().empty()) { - result.AppendError("no task variable"); - return; - } - - StackFrame &frame = m_exe_ctx.GetFrameRef(); - uint32_t path_options = - StackFrame::eExpressionPathOptionsAllowDirectIVarAccess; - VariableSP var_sp; - Status status; - ValueObjectSP valobj_sp = frame.GetValueForVariableExpressionPath( - command[0].c_str(), eDynamicDontRunTarget, path_options, var_sp, - status); - if (!valobj_sp) { - result.AppendError(status.AsCString()); + llvm::Expected thread_task = + ThreadForTaskVariable(command, m_exe_ctx); + if (auto error = thread_task.takeError()) { + result.AppendError(toString(std::move(error))); return; } - addr_t task_ptr = LLDB_INVALID_ADDRESS; - ThreadSafeReflectionContext reflection_ctx; - if (ValueObjectSP task_obj_sp = - valobj_sp->GetChildMemberWithName("_task")) { - task_ptr = task_obj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); - if (task_ptr != LLDB_INVALID_ADDRESS) - if (auto *runtime = SwiftLanguageRuntime::Get(m_exe_ctx.GetProcessSP())) - reflection_ctx = runtime->GetReflectionContext(); - } - if (task_ptr == LLDB_INVALID_ADDRESS || !reflection_ctx) { - result.AppendError("failed to access Task data from runtime"); - return; - } + // GetStatus prints the backtrace. + thread_task.get()->GetStatus(result.GetOutputStream(), 0, UINT32_MAX, 0, + false, true); + result.SetStatus(lldb::eReturnStatusSuccessFinishResult); + } +}; - llvm::Expected task_info = - reflection_ctx->asyncTaskInfo(task_ptr); - if (auto error = task_info.takeError()) { - result.AppendError(toString(std::move(error))); - return; - } +class CommandObjectLanguageSwiftTaskSelect final : public CommandObjectParsed { +public: + CommandObjectLanguageSwiftTaskSelect(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "select", + "Change the currently selected thread to thread representation of " + "the given Swift Task. See `thread select`.", + "language swift task select ") { + AddSimpleArgumentList(eArgTypeVarName); + } - auto thread_task = ThreadTask::Create( - task_info->id, task_info->resumeAsyncContext, m_exe_ctx); +private: + void DoExecute(Args &command, CommandReturnObject &result) override { + llvm::Expected thread_task = + ThreadForTaskVariable(command, m_exe_ctx); if (auto error = thread_task.takeError()) { result.AppendError(toString(std::move(error))); return; } - // GetStatus prints the backtrace. - thread_task.get()->GetStatus(result.GetOutputStream(), 0, UINT32_MAX, 0, - false, true); + auto &thread_list = m_exe_ctx.GetProcessRef().GetThreadList(); + thread_list.AddThread(thread_task.get()); + thread_list.SetSelectedThreadByID(thread_task.get()->GetID()); + result.SetStatus(lldb::eReturnStatusSuccessFinishResult); } }; @@ -2166,6 +2190,9 @@ class CommandObjectLanguageSwiftTask final : public CommandObjectMultiword { LoadSubCommand("backtrace", CommandObjectSP(new CommandObjectLanguageSwiftTaskBacktrace( interpreter))); + LoadSubCommand( + "select", + CommandObjectSP(new CommandObjectLanguageSwiftTaskSelect(interpreter))); } }; diff --git a/lldb/test/API/lang/swift/async/tasks/TestSwiftTaskSelect.py b/lldb/test/API/lang/swift/async/tasks/TestSwiftTaskSelect.py new file mode 100644 index 0000000000000..61d4f38d0b056 --- /dev/null +++ b/lldb/test/API/lang/swift/async/tasks/TestSwiftTaskSelect.py @@ -0,0 +1,55 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import TestBase +import lldbsuite.test.lldbutil as lldbutil + + +class TestCase(TestBase): + + def test_backtrace_selected_task(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.swift") + ) + self.runCmd("language swift task select task") + self.expect( + "thread backtrace", + substrs=[ + ".sleep(", + "`second() at main.swift:6:", + "`first() at main.swift:2:", + "`closure #1 in static Main.main() at main.swift:12:", + ], + ) + + def test_navigate_selected_task_stack(self): + self.build() + _, process, _, _ = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.swift") + ) + self.runCmd("language swift task select task") + + thread = process.selected_thread + self.assertEqual(thread.id, 2) + self.assertEqual(thread.idx, 0xFFFFFFFF) + self.assertIn( + "libswift_Concurrency.", thread.GetSelectedFrame().module.file.basename + ) + + frame_idx = -1 + for frame in thread: + if "`second()" in str(frame): + frame_idx = frame.idx + self.assertNotEqual(frame_idx, -1) + + self.expect(f"frame select {frame_idx}", substrs=[f"frame #{frame_idx}:"]) + frame = thread.GetSelectedFrame() + self.assertIn(".second()", frame.function.name) + + self.expect("up", substrs=[f"frame #{frame_idx + 1}:"]) + frame = thread.GetSelectedFrame() + self.assertIn(".first()", frame.function.name) + + self.expect("up", substrs=[f"frame #{frame_idx + 2}:"]) + frame = thread.GetSelectedFrame() + self.assertIn(".Main.main()", frame.function.name)