mgorny updated this revision to Diff 431140.
mgorny added a comment.
Implemented `vCtrlC` packet and added a test for it.
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/TestLldbGdbServer.py
Index: lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
===================================================================
--- lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
+++ lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
@@ -1410,3 +1410,100 @@
self.assertEqual(decoded[errno_idx], 0) # si_errno
self.assertEqual(decoded[code_idx], SEGV_MAPERR) # si_code
self.assertEqual(decoded[addr_idx], 0) # si_addr
+
+ def test_QNonStop(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: "signo", 2: "thread_id"},
+ },
+ ], True)
+ threads = [self.expect_gdbremote_sequence()]
+
+ # 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
+ 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)
+
+ 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_QNonStop_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()
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 = {};
@@ -115,6 +117,9 @@
PacketResult SendStopReplyPacketForThread(lldb::tid_t tid);
+ PacketResult SendStopReplyPacketForThread(NativeThreadProtocol &thread,
+ bool send_notify = true);
+
PacketResult SendStopReasonForState(lldb::StateType process_state);
PacketResult Handle_k(StringExtractorGDBRemote &packet);
@@ -217,6 +222,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) {
@@ -767,25 +777,31 @@
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread(
lldb::tid_t tid) {
- 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);
+ return SendStopReplyPacketForThread(*thread);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread(
+ NativeThreadProtocol &thread, bool send_notify) {
+ Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread);
+
+ LLDB_LOG(log, "preparing packet for pid {0} tid {1}",
+ m_current_process->GetID(), thread.GetID());
+
// Grab the reason this thread stopped.
struct ThreadStopInfo tid_stop_info;
std::string description;
- if (!thread->GetStopReason(tid_stop_info, description))
+ if (!thread.GetStopReason(tid_stop_info, description))
return SendErrorResponse(52);
// FIXME implement register handling for exec'd inferiors.
@@ -801,17 +817,17 @@
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 +891,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 +922,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 +939,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,7 +990,21 @@
tid_stop_info.details.fork.child_tid);
}
- return SendPacketNoLock(response.GetString());
+ if (m_non_stop) {
+ PacketResult ret = SendNotificationPacketNoLock(
+ "Stop", m_stop_notification_queue, response.GetString());
+ if (send_notify) {
+ // Queue notification events for the remaining threads.
+ uint32_t thread_index = 0;
+ while (NativeThreadProtocol *listed_thread =
+ m_current_process->GetThreadAtIndex(thread_index++)) {
+ if (listed_thread->GetID() != thread.GetID())
+ SendStopReplyPacketForThread(*listed_thread, false);
+ }
+ }
+ return ret;
+ } else
+ return SendPacketNoLock(response.GetString());
}
void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited(
@@ -1501,8 +1532,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 +1573,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
@@ -1657,8 +1690,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) {
@@ -2804,8 +2838,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>>
@@ -3657,6 +3692,47 @@
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());
+ 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 +3916,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