mgorny updated this revision to Diff 438007.
mgorny added a comment.
Move tests to a separate file. Rename and reverse `allow_async` into
`force_synchronous`. Remove `queue_all_threads` — we now always queue all in
async mode. Rework `Handle_stop_reason` not to rely on
`SendStopReasonForState()`, and fix reporting exiting processes via `?` when
not acked by client yet. Add tests for correct exit reporting.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D125575/new/
https://reviews.llvm.org/D125575
Files:
lldb/include/lldb/Utility/StringExtractorGDBRemote.h
lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
lldb/source/Utility/StringExtractorGDBRemote.cpp
lldb/test/API/tools/lldb-server/TestNonStop.py
Index: lldb/test/API/tools/lldb-server/TestNonStop.py
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-server/TestNonStop.py
@@ -0,0 +1,165 @@
+from lldbsuite.test.lldbtest import *
+
+import gdbremote_testcase
+
+
+class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def test_run(self):
+ self.build()
+ self.set_inferior_startup_launch()
+ thread_num = 3
+ procs = self.prep_debug_monitor_and_inferior(
+ inferior_args=["thread:segfault"] + thread_num * ["thread:new"])
+ self.test_sequence.add_log_lines(
+ ["read packet: $QNonStop:1#00",
+ "send packet: $OK#00",
+ "read packet: $c#63",
+ "send packet: $OK#00",
+ ], True)
+ self.expect_gdbremote_sequence()
+
+ segv_signo = lldbutil.get_signal_number('SIGSEGV')
+ all_threads = set()
+ all_segv_threads = []
+
+ # we should get segfaults from all the threads
+ for segv_no in range(thread_num):
+ # first wait for the notification event
+ self.reset_test_sequence()
+ self.test_sequence.add_log_lines(
+ [{"direction": "send",
+ "regex": r"^%Stop:(T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);)",
+ "capture": {1: "packet", 2: "signo", 3: "thread_id"},
+ },
+ ], True)
+ m = self.expect_gdbremote_sequence()
+ del m["O_content"]
+ threads = [m]
+
+ # then we may get events for the remaining threads
+ # (but note that not all threads may have been started yet)
+ while True:
+ self.reset_test_sequence()
+ self.test_sequence.add_log_lines(
+ ["read packet: $vStopped#00",
+ {"direction": "send",
+ "regex": r"^\$(OK|T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);)",
+ "capture": {1: "packet", 2: "signo", 3: "thread_id"},
+ },
+ ], True)
+ m = self.expect_gdbremote_sequence()
+ if m["packet"] == "OK":
+ break
+ del m["O_content"]
+ threads.append(m)
+
+ segv_threads = []
+ other_threads = []
+ for t in threads:
+ signo = int(t["signo"], 16)
+ if signo == segv_signo:
+ segv_threads.append(t["thread_id"])
+ else:
+ self.assertEqual(signo, 0)
+ other_threads.append(t["thread_id"])
+
+ # verify that exactly one thread segfaulted
+ self.assertEqual(len(segv_threads), 1)
+ # we should get only one segv from every thread
+ self.assertNotIn(segv_threads[0], all_segv_threads)
+ all_segv_threads.extend(segv_threads)
+ # segv_threads + other_threads should always be a superset
+ # of all_threads, i.e. we should get states for all threads
+ # already started
+ self.assertFalse(
+ all_threads.difference(other_threads + segv_threads))
+ all_threads.update(other_threads + segv_threads)
+
+ # verify that `?` returns the same result
+ self.reset_test_sequence()
+ self.test_sequence.add_log_lines(
+ ["read packet: $?#00",
+ ], True)
+ threads_verify = []
+ while True:
+ self.test_sequence.add_log_lines(
+ [{"direction": "send",
+ "regex": r"^\$(OK|T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);)",
+ "capture": {1: "packet", 2: "signo", 3: "thread_id"},
+ },
+ ], True)
+ m = self.expect_gdbremote_sequence()
+ if m["packet"] == "OK":
+ break
+ del m["O_content"]
+ threads_verify.append(m)
+ self.reset_test_sequence()
+ self.test_sequence.add_log_lines(
+ ["read packet: $vStopped#00",
+ ], True)
+
+ self.assertEqual(threads, threads_verify)
+
+ self.reset_test_sequence()
+ self.test_sequence.add_log_lines(
+ ["read packet: $vCont;C{:02x}:{};c#00"
+ .format(segv_signo, segv_threads[0]),
+ "send packet: $OK#00",
+ ], True)
+ self.expect_gdbremote_sequence()
+
+ # finally, verify that all threads have started
+ self.assertEqual(len(all_threads), thread_num + 1)
+
+ def test_vCtrlC(self):
+ self.build()
+ self.set_inferior_startup_launch()
+ procs = self.prep_debug_monitor_and_inferior(
+ inferior_args=["thread:new"])
+ self.test_sequence.add_log_lines(
+ ["read packet: $QNonStop:1#00",
+ "send packet: $OK#00",
+ "read packet: $c#63",
+ "send packet: $OK#00",
+ "read packet: $vCtrlC#00",
+ "send packet: $OK#00",
+ {"direction": "send",
+ "regex": r"^%Stop:T13",
+ },
+ ], True)
+ self.expect_gdbremote_sequence()
+
+ def test_exit(self):
+ self.build()
+ self.set_inferior_startup_launch()
+ procs = self.prep_debug_monitor_and_inferior()
+ self.test_sequence.add_log_lines(
+ ["read packet: $QNonStop:1#00",
+ "send packet: $OK#00",
+ "read packet: $c#63",
+ "send packet: $OK#00",
+ "send packet: %Stop:W00#00",
+ "read packet: $vStopped#00",
+ "send packet: $OK#00",
+ ], True)
+ self.expect_gdbremote_sequence()
+
+ def test_exit_query(self):
+ self.build()
+ self.set_inferior_startup_launch()
+ procs = self.prep_debug_monitor_and_inferior()
+ self.test_sequence.add_log_lines(
+ ["read packet: $QNonStop:1#00",
+ "send packet: $OK#00",
+ "read packet: $c#63",
+ "send packet: $OK#00",
+ "send packet: %Stop:W00#00",
+ "read packet: $?#00",
+ "send packet: $W00#00",
+ "read packet: $vStopped#00",
+ "send packet: $OK#00",
+ ], True)
+ self.expect_gdbremote_sequence()
Index: lldb/source/Utility/StringExtractorGDBRemote.cpp
===================================================================
--- lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -150,6 +150,11 @@
return eServerPacketType_QMemTags;
break;
+ case 'N':
+ if (PACKET_STARTS_WITH("QNonStop:"))
+ return eServerPacketType_QNonStop;
+ break;
+
case 'R':
if (PACKET_STARTS_WITH("QRestoreRegisterState:"))
return eServerPacketType_QRestoreRegisterState;
@@ -369,6 +374,12 @@
return eServerPacketType_vCont_actions;
if (PACKET_STARTS_WITH("vRun;"))
return eServerPacketType_vRun;
+ if (PACKET_MATCHES("vStopped"))
+ return eServerPacketType_vStopped;
+ if (PACKET_MATCHES("vCtrlC"))
+ return eServerPacketType_vCtrlC;
+ break;
+
}
break;
case '_':
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
@@ -106,6 +106,8 @@
uint32_t m_next_saved_registers_id = 1;
bool m_thread_suffix_supported = false;
bool m_list_threads_in_stop_reply = false;
+ bool m_non_stop = false;
+ std::deque<std::string> m_stop_notification_queue;
NativeProcessProtocol::Extension m_extensions_supported = {};
@@ -113,9 +115,15 @@
PacketResult SendWResponse(NativeProcessProtocol *process);
- PacketResult SendStopReplyPacketForThread(lldb::tid_t tid);
+ StreamString PrepareStopReplyPacketForThread(NativeThreadProtocol &thread);
- PacketResult SendStopReasonForState(lldb::StateType process_state);
+ PacketResult SendStopReplyPacketForThread(lldb::tid_t tid,
+ bool force_synchronous);
+
+ PacketResult SendStopReasonForState(lldb::StateType process_state,
+ bool force_synchronous);
+
+ void EnqueueStopReplyPackets(lldb::tid_t thread_to_skip);
PacketResult Handle_k(StringExtractorGDBRemote &packet);
@@ -217,6 +225,12 @@
PacketResult Handle_qSaveCore(StringExtractorGDBRemote &packet);
+ PacketResult Handle_QNonStop(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vStopped(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vCtrlC(StringExtractorGDBRemote &packet);
+
PacketResult Handle_g(StringExtractorGDBRemote &packet);
PacketResult Handle_qMemTags(StringExtractorGDBRemote &packet);
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
@@ -235,6 +235,16 @@
RegisterMemberFunctionHandler(
StringExtractorGDBRemote::eServerPacketType_qLLDBSaveCore,
&GDBRemoteCommunicationServerLLGS::Handle_qSaveCore);
+
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QNonStop,
+ &GDBRemoteCommunicationServerLLGS::Handle_QNonStop);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vStopped,
+ &GDBRemoteCommunicationServerLLGS::Handle_vStopped);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vCtrlC,
+ &GDBRemoteCommunicationServerLLGS::Handle_vCtrlC);
}
void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(const ProcessLaunchInfo &info) {
@@ -474,7 +484,11 @@
StreamGDBRemote response;
response.Format("{0:g}", *wait_status);
- return SendPacketNoLock(response.GetString());
+ if (m_non_stop)
+ return SendNotificationPacketNoLock("Stop", m_stop_notification_queue,
+ response.GetString());
+ else
+ return SendPacketNoLock(response.GetString());
}
static void AppendHexValue(StreamString &response, const uint8_t *buf,
@@ -764,29 +778,20 @@
return threads_array;
}
-GDBRemoteCommunication::PacketResult
-GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread(
- lldb::tid_t tid) {
+StreamString
+GDBRemoteCommunicationServerLLGS::PrepareStopReplyPacketForThread(
+ NativeThreadProtocol &thread) {
Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread);
- // Ensure we have a debugged process.
- if (!m_current_process ||
- (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID))
- return SendErrorResponse(50);
-
LLDB_LOG(log, "preparing packet for pid {0} tid {1}",
- m_current_process->GetID(), tid);
-
- // Ensure we can get info on the given thread.
- NativeThreadProtocol *thread = m_current_process->GetThreadByID(tid);
- if (!thread)
- return SendErrorResponse(51);
+ m_current_process->GetID(), thread.GetID());
// Grab the reason this thread stopped.
+ StreamString response;
struct ThreadStopInfo tid_stop_info;
std::string description;
- if (!thread->GetStopReason(tid_stop_info, description))
- return SendErrorResponse(52);
+ if (!thread.GetStopReason(tid_stop_info, description))
+ return response;
// FIXME implement register handling for exec'd inferiors.
// if (tid_stop_info.reason == eStopReasonExec) {
@@ -794,24 +799,23 @@
// InitializeRegisters(force);
// }
- StreamString response;
// Output the T packet with the thread
response.PutChar('T');
int signum = tid_stop_info.details.signal.signo;
LLDB_LOG(
log,
"pid {0}, tid {1}, got signal signo = {2}, reason = {3}, exc_type = {4}",
- m_current_process->GetID(), tid, signum, int(tid_stop_info.reason),
- tid_stop_info.details.exception.type);
+ m_current_process->GetID(), thread.GetID(), signum,
+ int(tid_stop_info.reason), tid_stop_info.details.exception.type);
// Print the signal number.
response.PutHex8(signum & 0xff);
// Include the tid.
- response.Printf("thread:%" PRIx64 ";", tid);
+ response.Printf("thread:%" PRIx64 ";", thread.GetID());
// Include the thread name if there is one.
- const std::string thread_name = thread->GetName();
+ const std::string thread_name = thread.GetName();
if (!thread_name.empty()) {
size_t thread_name_len = thread_name.length();
@@ -875,7 +879,7 @@
char delimiter = ':';
for (NativeThreadProtocol *thread;
(thread = m_current_process->GetThreadAtIndex(i)) != nullptr; ++i) {
- NativeRegisterContext& reg_ctx = thread->GetRegisterContext();
+ NativeRegisterContext ®_ctx = thread->GetRegisterContext();
uint32_t reg_to_read = reg_ctx.ConvertRegisterKindToRegisterNumber(
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
@@ -906,7 +910,7 @@
//
// Grab the register context.
- NativeRegisterContext& reg_ctx = thread->GetRegisterContext();
+ NativeRegisterContext ®_ctx = thread.GetRegisterContext();
const auto expedited_regs =
reg_ctx.GetExpeditedRegisters(ExpeditedRegs::Full);
@@ -923,8 +927,9 @@
®_value, lldb::eByteOrderBig);
response.PutChar(';');
} else {
- LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s failed to read "
- "register '%s' index %" PRIu32 ": %s",
+ LLDB_LOGF(log,
+ "GDBRemoteCommunicationServerLLGS::%s failed to read "
+ "register '%s' index %" PRIu32 ": %s",
__FUNCTION__,
reg_info_p->name ? reg_info_p->name : "<unnamed-register>",
reg_num, error.AsCString());
@@ -973,9 +978,52 @@
tid_stop_info.details.fork.child_tid);
}
+ return response;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread(
+ lldb::tid_t tid, bool force_synchronous) {
+ // Ensure we have a debugged process.
+ if (!m_current_process ||
+ (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(50);
+
+ // Ensure we can get info on the given thread.
+ NativeThreadProtocol *thread = m_current_process->GetThreadByID(tid);
+ if (!thread)
+ return SendErrorResponse(51);
+
+ StreamString response = PrepareStopReplyPacketForThread(*thread);
+ if (response.Empty())
+ return SendErrorResponse(42);
+
+ PacketResult ret;
+ if (m_non_stop && !force_synchronous) {
+ PacketResult ret = SendNotificationPacketNoLock(
+ "Stop", m_stop_notification_queue, response.GetString());
+ // Queue notification events for the remaining threads.
+ EnqueueStopReplyPackets(tid);
+ return ret;
+ }
+
return SendPacketNoLock(response.GetString());
}
+void GDBRemoteCommunicationServerLLGS::EnqueueStopReplyPackets(
+ lldb::tid_t thread_to_skip) {
+ if (!m_non_stop)
+ return;
+
+ uint32_t thread_index = 0;
+ while (NativeThreadProtocol *listed_thread =
+ m_current_process->GetThreadAtIndex(thread_index++)) {
+ if (listed_thread->GetID() != thread_to_skip)
+ m_stop_notification_queue.push_back(
+ PrepareStopReplyPacketForThread(*listed_thread).GetString().str());
+ }
+}
+
void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited(
NativeProcessProtocol *process) {
assert(process && "process cannot be NULL");
@@ -983,7 +1031,7 @@
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
- PacketResult result = SendStopReasonForState(StateType::eStateExited);
+ PacketResult result = SendStopReasonForState(StateType::eStateExited, false);
if (result != PacketResult::Success) {
LLDB_LOGF(log,
"GDBRemoteCommunicationServerLLGS::%s failed to send stop "
@@ -995,9 +1043,13 @@
// up.
MaybeCloseInferiorTerminalConnection();
- // We are ready to exit the debug monitor.
- m_exit_now = true;
- m_mainloop.RequestTermination();
+ // When running in non-stop mode, wait for the vStopped to clear
+ // the notification queue.
+ if (!m_non_stop) {
+ // We are ready to exit the debug monitor.
+ m_exit_now = true;
+ m_mainloop.RequestTermination();
+ }
}
void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped(
@@ -1015,7 +1067,8 @@
break;
default:
// In all other cases, send the stop reason.
- PacketResult result = SendStopReasonForState(StateType::eStateStopped);
+ PacketResult result =
+ SendStopReasonForState(StateType::eStateStopped, false);
if (result != PacketResult::Success) {
LLDB_LOGF(log,
"GDBRemoteCommunicationServerLLGS::%s failed to send stop "
@@ -1371,9 +1424,11 @@
LLDB_LOG(log, "Failed to kill debugged process {0}: {1}",
m_current_process->GetID(), error);
- // No OK response for kill packet.
- // return SendOKResponse ();
- return PacketResult::Success;
+ // The response to kill packet is undefined per the spec. LLDB
+ // follows the same rules as for continue packets, i.e. no response
+ // in all-stop mode, and "OK" in non-stop mode; in both cases this
+ // is followed by the actual stop reason.
+ return m_non_stop ? SendOKResponse() : PacketResult::Success;
}
GDBRemoteCommunication::PacketResult
@@ -1501,8 +1556,9 @@
return SendErrorResponse(0x38);
}
- // Don't send an "OK" packet; response is the stopped/exited message.
- return PacketResult::Success;
+ // Don't send an "OK" packet, except in non-stop mode;
+ // otherwise, the response is the stopped/exited message.
+ return m_non_stop ? SendOKResponse() : PacketResult::Success;
}
GDBRemoteCommunication::PacketResult
@@ -1541,8 +1597,9 @@
}
LLDB_LOG(log, "continued process {0}", m_continue_process->GetID());
- // No response required from continue.
- return PacketResult::Success;
+
+ // No response required from continue, unless in non-stop mode.
+ return m_non_stop ? SendOKResponse() : PacketResult::Success;
}
GDBRemoteCommunication::PacketResult
@@ -1580,6 +1637,9 @@
// Move past the ';', then do a simple 's'.
packet.SetFilePos(packet.GetFilePos() + 1);
return Handle_s(packet);
+ } else if (m_non_stop && ::strcmp(packet.Peek(), ";t") == 0) {
+ // TODO: add full support for "t" action
+ return SendOKResponse();
}
// Ensure we have a native process.
@@ -1657,8 +1717,9 @@
}
LLDB_LOG(log, "continued process {0}", m_continue_process->GetID());
- // No response required from vCont.
- return PacketResult::Success;
+
+ // No response required from vCont, unless in non-stop mode.
+ return m_non_stop ? SendOKResponse() : PacketResult::Success;
}
void GDBRemoteCommunicationServerLLGS::SetCurrentThreadID(lldb::tid_t tid) {
@@ -1686,12 +1747,39 @@
if (!m_current_process)
return SendErrorResponse(02);
- return SendStopReasonForState(m_current_process->GetState());
+ if (m_non_stop) {
+ // Clear the notification queue first, except for pending exit
+ // notifications.
+ for (auto it = m_stop_notification_queue.begin();
+ it != m_stop_notification_queue.end();) {
+ if (it->front() != 'W' && it->front() != 'X')
+ it = m_stop_notification_queue.erase(it);
+ else
+ ++it;
+ }
+
+ // Queue stop reply packets for all active threads. Start with the current
+ // thread (for clients that don't actually support multiple stop reasons).
+ NativeThreadProtocol *thread = m_current_process->GetCurrentThread();
+ if (thread)
+ m_stop_notification_queue.push_back(
+ PrepareStopReplyPacketForThread(*thread).GetString().str());
+ EnqueueStopReplyPackets(thread ? thread->GetID() : LLDB_INVALID_THREAD_ID);
+
+ // If the notification queue is empty (i.e. everything is running), send OK.
+ if (m_stop_notification_queue.empty())
+ return SendOKResponse();
+
+ // Send the first item from the new notification queue synchronously.
+ return SendPacketNoLock(m_stop_notification_queue.front());
+ }
+
+ return SendStopReasonForState(m_current_process->GetState(), true);
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::SendStopReasonForState(
- lldb::StateType process_state) {
+ lldb::StateType process_state, bool force_synchronous) {
Log *log = GetLog(LLDBLog::Process);
switch (process_state) {
@@ -1712,7 +1800,7 @@
// Make sure we set the current thread so g and p packets return the data
// the gdb will expect.
SetCurrentThreadID(tid);
- return SendStopReplyPacketForThread(tid);
+ return SendStopReplyPacketForThread(tid, force_synchronous);
}
case eStateInvalid:
@@ -2804,8 +2892,9 @@
return SendErrorResponse(0x49);
}
- // No response here - the stop or exit will come from the resulting action.
- return PacketResult::Success;
+ // No response here, unless in non-stop mode.
+ // Otherwise, the stop or exit will come from the resulting action.
+ return m_non_stop ? SendOKResponse() : PacketResult::Success;
}
llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
@@ -3172,7 +3261,7 @@
}
// Notify we attached by sending a stop packet.
- return SendStopReasonForState(m_current_process->GetState());
+ return SendStopReasonForState(m_current_process->GetState(), false);
}
GDBRemoteCommunication::PacketResult
@@ -3202,7 +3291,7 @@
}
// Notify we attached by sending a stop packet.
- return SendStopReasonForState(m_current_process->GetState());
+ return SendStopReasonForState(m_current_process->GetState(), false);
}
GDBRemoteCommunication::PacketResult
@@ -3238,7 +3327,7 @@
}
// Notify we attached by sending a stop packet.
- return SendStopReasonForState(m_current_process->GetState());
+ return SendStopReasonForState(m_current_process->GetState(), false);
}
GDBRemoteCommunication::PacketResult
@@ -3267,7 +3356,7 @@
m_process_launch_info.GetArguments()[0].ref(), FileSpec::Style::native);
m_process_launch_error = LaunchProcess();
if (m_process_launch_error.Success())
- return SendStopReasonForState(m_current_process->GetState());
+ return SendStopReasonForState(m_current_process->GetState(), true);
LLDB_LOG(log, "failed to launch exe: {0}", m_process_launch_error);
}
return SendErrorResponse(8);
@@ -3334,7 +3423,7 @@
__FUNCTION__, packet.GetStringRef().data());
return SendErrorResponse(0x15);
}
- return SendStopReplyPacketForThread(tid);
+ return SendStopReplyPacketForThread(tid, true);
}
GDBRemoteCommunication::PacketResult
@@ -3657,6 +3746,53 @@
return SendPacketNoLock(response.GetString());
}
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QNonStop(
+ StringExtractorGDBRemote &packet) {
+ StringRef packet_str{packet.GetStringRef()};
+ assert(packet_str.startswith("QNonStop:"));
+ packet_str.consume_front("QNonStop:");
+ if (packet_str == "0") {
+ m_non_stop = false;
+ // TODO: stop all threads
+ } else if (packet_str == "1") {
+ m_non_stop = true;
+ } else
+ return SendErrorResponse(Status("Invalid QNonStop packet"));
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_vStopped(
+ StringExtractorGDBRemote &packet) {
+ if (m_stop_notification_queue.empty())
+ return SendErrorResponse(Status("No pending notification to ack"));
+ m_stop_notification_queue.pop_front();
+ if (!m_stop_notification_queue.empty())
+ return SendPacketNoLock(m_stop_notification_queue.front());
+ // If this was the last notification and the process exited, terminate
+ // the server.
+ if (m_inferior_prev_state == eStateExited) {
+ m_exit_now = true;
+ m_mainloop.RequestTermination();
+ }
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_vCtrlC(
+ StringExtractorGDBRemote &packet) {
+ if (!m_non_stop)
+ return SendErrorResponse(Status("vCtrl is only valid in non-stop mode"));
+
+ PacketResult interrupt_res = Handle_interrupt(packet);
+ // If interrupting the process failed, pass the result through.
+ if (interrupt_res != PacketResult::Success)
+ return interrupt_res;
+ // Otherwise, vCtrlC should issue an OK response (normal interrupts do not).
+ return SendOKResponse();
+}
+
void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() {
Log *log = GetLog(LLDBLog::Process);
@@ -3840,6 +3976,7 @@
"QThreadSuffixSupported+",
"QListThreadsInStopReply+",
"qXfer:features:read+",
+ "QNonStop+",
});
// report server-only features
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -183,6 +183,9 @@
CompressionType m_compression_type;
PacketResult SendPacketNoLock(llvm::StringRef payload);
+ PacketResult SendNotificationPacketNoLock(llvm::StringRef notify_type,
+ std::deque<std::string>& queue,
+ llvm::StringRef payload);
PacketResult SendRawPacketNoLock(llvm::StringRef payload,
bool skip_ack = false);
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -124,6 +124,29 @@
return SendRawPacketNoLock(packet_str);
}
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::SendNotificationPacketNoLock(
+ llvm::StringRef notify_type, std::deque<std::string> &queue,
+ llvm::StringRef payload) {
+ PacketResult ret = PacketResult::Success;
+
+ // If there are no notification in the queue, send the notification
+ // packet.
+ if (queue.empty()) {
+ StreamString packet(0, 4, eByteOrderBig);
+ packet.PutChar('%');
+ packet.Write(notify_type.data(), notify_type.size());
+ packet.PutChar(':');
+ packet.Write(payload.data(), payload.size());
+ packet.PutChar('#');
+ packet.PutHex8(CalculcateChecksum(payload));
+ ret = SendRawPacketNoLock(packet.GetString(), true);
+ }
+
+ queue.push_back(payload.str());
+ return ret;
+}
+
GDBRemoteCommunication::PacketResult
GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet,
bool skip_ack) {
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py
@@ -866,7 +866,7 @@
class Server(object):
- _GDB_REMOTE_PACKET_REGEX = re.compile(br'^\$([^\#]*)#[0-9a-fA-F]{2}')
+ _GDB_REMOTE_PACKET_REGEX = re.compile(br'^[\$%]([^\#]*)#[0-9a-fA-F]{2}')
class ChecksumMismatch(Exception):
pass
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -851,6 +851,7 @@
"memory-tagging",
"qSaveCore",
"native-signals",
+ "QNonStop",
]
def parse_qSupported_response(self, context):
Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h
===================================================================
--- lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -174,7 +174,10 @@
eServerPacketType_QMemTags, // write memory tags
eServerPacketType_qLLDBSaveCore,
- eServerPacketType_QSetIgnoredExceptions
+ eServerPacketType_QSetIgnoredExceptions,
+ eServerPacketType_QNonStop,
+ eServerPacketType_vStopped,
+ eServerPacketType_vCtrlC,
};
ServerPacketType GetServerPacketType() const;
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits