Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -243,6 +243,8 @@ PacketResult Handle_QMemTags(StringExtractorGDBRemote &packet); + PacketResult Handle_T(StringExtractorGDBRemote &packet); + void SetCurrentThreadID(lldb::tid_t tid); lldb::tid_t GetCurrentThreadID() const; Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -107,6 +107,8 @@ &GDBRemoteCommunicationServerLLGS::Handle_P); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC, &GDBRemoteCommunicationServerLLGS::Handle_qC); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_T, + &GDBRemoteCommunicationServerLLGS::Handle_T); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qfThreadInfo, &GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo); @@ -3923,6 +3925,36 @@ return SendOKResponse(); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_T(StringExtractorGDBRemote &packet) { + packet.SetFilePos(strlen("T")); + auto pid_tid = packet.GetPidTid(m_current_process ? m_current_process->GetID() + : LLDB_INVALID_PROCESS_ID); + if (!pid_tid) + return SendErrorResponse(llvm::make_error( + inconvertibleErrorCode(), "Malformed thread-id")); + + lldb::pid_t pid = pid_tid->first; + lldb::tid_t tid = pid_tid->second; + + // Technically, this would also be caught by the PID check but let's be more + // explicit about the error. + if (pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(llvm::make_error( + inconvertibleErrorCode(), "No current process and no PID provided")); + + // Check the process ID and find respective process instance. + auto new_process_it = m_debugged_processes.find(pid); + if (new_process_it == m_debugged_processes.end()) + return SendErrorResponse(1); + + // Check the thread ID + if (!new_process_it->second->GetThreadByID(tid)) + return SendErrorResponse(2); + + return SendOKResponse(); +} + void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() { Log *log = GetLog(LLDBLog::Process); Index: lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py =================================================================== --- lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py +++ lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py @@ -866,3 +866,76 @@ "send packet: $QCp{:x}.{:x}#00".format(*pidtid), ], True) self.expect_gdbremote_sequence() + + @add_test_categories(["fork"]) + def test_T(self): + self.build() + self.prep_debug_monitor_and_inferior( + inferior_args=["fork", + "thread:new", + "trap", + ]) + self.add_qSupported_packets(["multiprocess+", + "fork-events+"]) + ret = self.expect_gdbremote_sequence() + self.assertIn("fork-events+", ret["qSupported_response"]) + self.reset_test_sequence() + + # continue and expect fork + self.test_sequence.add_log_lines([ + "read packet: $c#00", + {"direction": "send", "regex": self.fork_regex.format("fork"), + "capture": self.fork_capture}, + ], True) + self.add_threadinfo_collection_packets() + ret = self.expect_gdbremote_sequence() + pidtids = [ + (ret["parent_pid"], ret["parent_tid"]), + (ret["child_pid"], ret["child_tid"]), + ] + self.reset_test_sequence() + + for pidtid in pidtids: + self.test_sequence.add_log_lines( + ["read packet: $Hcp{}.{}#00".format(*pidtid), + "send packet: $OK#00", + "read packet: $c#00", + {"direction": "send", + "regex": "^[$]T05thread:p{}.{}.*".format(*pidtid), + }, + ], True) + + self.add_threadinfo_collection_packets() + ret = self.expect_gdbremote_sequence() + self.reset_test_sequence() + + pidtids = set(self.parse_threadinfo_packets(ret)) + self.assertEqual(len(pidtids), 4) + max_pid = max(pid for pid, tid in pidtids) + max_tid = max(tid for pid, tid in pidtids) + bad_pidtids = ( + (max_pid, max_tid + 1, "E02"), + (max_pid + 1, max_tid, "E01"), + (max_pid + 1, max_tid + 1, "E01"), + ) + + for pidtid in pidtids: + self.test_sequence.add_log_lines( + [ + # test explicit PID+TID + "read packet: $Tp{:x}.{:x}#00".format(*pidtid), + "send packet: $OK#00", + # test implicit PID via Hg + "read packet: $Hgp{:x}.{:x}#00".format(*pidtid), + "send packet: $OK#00", + "read packet: $T{:x}#00".format(max_tid + 1), + "send packet: $E02#00", + "read packet: $T{:x}#00".format(pidtid[1]), + "send packet: $OK#00", + ], True) + for pid, tid, expected in bad_pidtids: + self.test_sequence.add_log_lines( + ["read packet: $Tp{:x}.{:x}#00".format(pid, tid), + "send packet: ${}#00".format(expected), + ], True) + self.expect_gdbremote_sequence()