mgorny updated this revision to Diff 442519.
mgorny marked 2 inline comments as done.
mgorny added a comment.
Partial update. Implemented most of the requests, except what noted below. Also
implemented support for `%Stdio:` stdout forwarding style as was eventually
implemented in LLGS.
I haven't been able to figure out how to make the test suite work without the
public-private state change.
As for the `OK` response to `vCtrlC`… it's hard because the incoming packet
causes the pending read in
`GDBRemoteClientBase::SendContinuePacketAndWaitForResponse()` to succeed, so
the `OK` is swallowed there. I can't think of any way to do this that wouldn't
be super ugly.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D126614/new/
https://reviews.llvm.org/D126614
Files:
lldb/packages/Python/lldbsuite/test/gdbclientutils.py
lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteProperties.td
lldb/source/Target/Process.cpp
lldb/test/API/functionalities/gdb_remote_client/TestNonStop.py
Index: lldb/test/API/functionalities/gdb_remote_client/TestNonStop.py
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/gdb_remote_client/TestNonStop.py
@@ -0,0 +1,157 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import *
+from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
+
+
+class TestNonStop(GDBRemoteTestBase):
+ def test_run(self):
+ class MyResponder(MockGDBServerResponder):
+ vStopped_counter = 0
+
+ def qSupported(self, client_supported):
+ return "QNonStop+;" + super().qSupported(client_supported)
+
+ def QNonStop(self, val):
+ return "OK"
+
+ def qfThreadInfo(self):
+ return "m10,12"
+
+ def qsThreadInfo(self):
+ return "l"
+
+ def vStopped(self):
+ self.vStopped_counter += 1
+ return ("OK" if self.vStopped_counter > 1
+ else "T00;thread:10")
+
+ def cont(self):
+ self.vStopped_counter = 0
+ return ["OK", "%Stop:T02;thread:12"]
+
+ def vCtrlC(self):
+ return "OK"
+
+ self.dbg.HandleCommand(
+ "settings set plugin.process.gdb-remote.use-non-stop-protocol true")
+ self.addTearDownHook(lambda:
+ self.runCmd(
+ "settings set plugin.process.gdb-remote.use-non-stop-protocol "
+ "false"))
+ self.server.responder = MyResponder()
+ target = self.dbg.CreateTarget("")
+ process = self.connect(target)
+ self.assertPacketLogContains(["QNonStop:1"])
+
+ process.Continue()
+ self.assertPacketLogContains(["vStopped"])
+ self.assertEqual(process.GetSelectedThread().GetStopReason(),
+ lldb.eStopReasonSignal)
+ self.assertEqual(process.GetSelectedThread().GetStopDescription(100),
+ "signal SIGINT")
+
+ def test_stdio(self):
+ class MyResponder(MockGDBServerResponder):
+ vStdio_counter = 0
+ vStopped_counter = 0
+
+ def qSupported(self, client_supported):
+ return "QNonStop+;" + super().qSupported(client_supported)
+
+ def QNonStop(self, val):
+ return "OK"
+
+ def qfThreadInfo(self):
+ return "m10,12"
+
+ def qsThreadInfo(self):
+ return "l"
+
+ def vStdio(self):
+ self.vStdio_counter += 1
+ # intersperse notifications with replies for better testing
+ return ("OK" if self.vStdio_counter > 1
+ else ["%Stop:T02;thread:12",
+ "O7365636f6e64206c696e650d0a"])
+
+ def vStopped(self):
+ self.vStopped_counter += 1
+ return ("OK" if self.vStopped_counter > 1
+ else "T00;thread:10")
+
+ def cont(self):
+ self.vStopped_counter = 0
+ self.vStdio_counter = 0
+ return ["OK",
+ "%Stdio:O6669727374206c696e650d0a",]
+
+ def vCtrlC(self):
+ return "OK"
+
+ self.dbg.HandleCommand(
+ "settings set plugin.process.gdb-remote.use-non-stop-protocol true")
+ self.addTearDownHook(lambda:
+ self.runCmd(
+ "settings set plugin.process.gdb-remote.use-non-stop-protocol "
+ "false"))
+ self.server.responder = MyResponder()
+ target = self.dbg.CreateTarget("")
+ process = self.connect(target)
+ self.assertPacketLogContains(["QNonStop:1"])
+
+ process.Continue()
+ self.assertPacketLogContains(["vStdio", "vStopped"])
+ self.assertEqual(process.GetSelectedThread().GetStopReason(),
+ lldb.eStopReasonSignal)
+ self.assertEqual(process.GetSelectedThread().GetStopDescription(100),
+ "signal SIGINT")
+
+ def test_vCtrlC(self):
+ class MyResponder(MockGDBServerResponder):
+ vStopped_counter = 0
+
+ def qSupported(self, client_supported):
+ return "QNonStop+;" + super().qSupported(client_supported)
+
+ def QNonStop(self, val):
+ return "OK"
+
+ def qfThreadInfo(self):
+ return "m10,12"
+
+ def vStopped(self):
+ self.vStopped_counter += 1
+ return ("OK" if self.vStopped_counter % 2 == 0
+ else "T00;thread:10")
+
+ def cont(self):
+ return ["OK"]
+
+ def vCtrlC(self):
+ return ["OK", "%Stop:T00;thread:10"]
+
+ self.dbg.HandleCommand(
+ "settings set plugin.process.gdb-remote.use-non-stop-protocol true")
+ self.addTearDownHook(lambda:
+ self.runCmd(
+ "settings set plugin.process.gdb-remote.use-non-stop-protocol "
+ "false"))
+ self.server.responder = MyResponder()
+ target = self.dbg.CreateTarget("")
+ process = self.connect(target)
+ self.assertPacketLogContains(["QNonStop:1"])
+
+ self.dbg.SetAsync(True)
+ lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
+ [lldb.eStateStopped])
+ process.Continue()
+ lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
+ [lldb.eStateRunning])
+ process.Stop()
+ lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
+ [lldb.eStateStopped])
+ self.assertPacketLogContains(["vStopped"])
+ self.assertEqual(process.GetSelectedThread().GetStopReason(),
+ lldb.eStopReasonNone)
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -3095,7 +3095,7 @@
}
Status Process::Halt(bool clear_thread_plans, bool use_run_lock) {
- if (!StateIsRunningState(m_public_state.GetValue()))
+ if (!StateIsRunningState(m_private_state.GetValue()))
return Status("Process is not running.");
// Don't clear the m_clear_thread_plans_on_stop, only set it to true if in
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteProperties.td
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteProperties.td
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteProperties.td
@@ -9,6 +9,10 @@
Global,
DefaultStringValue<"">,
Desc<"The file that provides the description for remote target registers.">;
+ def UseNonStopProtocol: Property<"use-non-stop-protocol", "Boolean">,
+ Global,
+ DefaultFalse,
+ Desc<"If true, LLDB will enable non-stop mode on the server and expect non-stop-style notifications from it. Note that LLDB currently requires that all threads are stopped anyway in non-stop mode.">;
def UseSVR4: Property<"use-libraries-svr4", "Boolean">,
Global,
DefaultTrue,
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -287,6 +287,7 @@
int64_t m_breakpoint_pc_offset;
lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach
bool m_use_g_packet_for_reading;
+ bool m_use_non_stop_protocol;
bool m_allow_flash_writes;
using FlashRangeVector = lldb_private::RangeVector<lldb::addr_t, size_t>;
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -164,6 +164,11 @@
const uint32_t idx = ePropertyUseGPacketForReading;
return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
}
+
+ bool GetUseNonStopProtocol() const {
+ const uint32_t idx = ePropertyUseNonStopProtocol;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
+ }
};
static PluginProperties &GetGlobalPluginProperties() {
@@ -301,6 +306,8 @@
m_use_g_packet_for_reading =
GetGlobalPluginProperties().GetUseGPacketForReading();
+ m_use_non_stop_protocol =
+ GetGlobalPluginProperties().GetUseNonStopProtocol();
}
// Destructor
@@ -1077,6 +1084,11 @@
else
SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture()));
}
+
+ // Delay enabling non-stop protocol until we've taken care of full setup
+ // to workaround a bug in gdbserver.
+ if (m_use_non_stop_protocol)
+ m_gdb_comm.EnableNonStop();
}
void ProcessGDBRemote::MaybeLoadExecutableModule() {
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -321,6 +321,8 @@
void EnableErrorStringInPacket();
+ void EnableNonStop();
+
bool GetQXferLibrariesReadSupported();
bool GetQXferLibrariesSVR4ReadSupported();
@@ -523,6 +525,8 @@
bool GetSaveCoreSupported() const;
+ bool GetNonStopSupported() const;
+
protected:
LazyBool m_supports_not_sending_acks = eLazyBoolCalculate;
LazyBool m_supports_thread_suffix = eLazyBoolCalculate;
@@ -563,6 +567,7 @@
LazyBool m_supports_multiprocess = eLazyBoolCalculate;
LazyBool m_supports_memory_tagging = eLazyBoolCalculate;
LazyBool m_supports_qSaveCore = eLazyBoolCalculate;
+ LazyBool m_supports_QNonStop = eLazyBoolCalculate;
LazyBool m_uses_native_signals = eLazyBoolCalculate;
bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -276,6 +276,7 @@
m_avoid_g_packets = eLazyBoolCalculate;
m_supports_multiprocess = eLazyBoolCalculate;
m_supports_qSaveCore = eLazyBoolCalculate;
+ m_supports_QNonStop = eLazyBoolCalculate;
m_supports_qXfer_auxv_read = eLazyBoolCalculate;
m_supports_qXfer_libraries_read = eLazyBoolCalculate;
m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
@@ -335,6 +336,7 @@
m_supports_QPassSignals = eLazyBoolNo;
m_supports_memory_tagging = eLazyBoolNo;
m_supports_qSaveCore = eLazyBoolNo;
+ m_supports_QNonStop = eLazyBoolNo;
m_uses_native_signals = eLazyBoolNo;
m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
@@ -384,6 +386,8 @@
m_supports_memory_tagging = eLazyBoolYes;
else if (x == "qSaveCore+")
m_supports_qSaveCore = eLazyBoolYes;
+ else if (x == "QNonStop+")
+ m_supports_QNonStop = eLazyBoolYes;
else if (x == "native-signals+")
m_uses_native_signals = eLazyBoolYes;
// Look for a list of compressions in the features list e.g.
@@ -530,6 +534,10 @@
return m_supports_qSaveCore == eLazyBoolYes;
}
+bool GDBRemoteCommunicationClient::GetNonStopSupported() const {
+ return m_supports_QNonStop == eLazyBoolYes;
+}
+
StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() {
// Get information on all threads at one using the "jThreadsInfo" packet
StructuredData::ObjectSP object_sp;
@@ -579,6 +587,18 @@
}
}
+void GDBRemoteCommunicationClient::EnableNonStop() {
+ if (!GetNonStopSupported())
+ return;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("QNonStop:1", response) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ SetNonStopEnabled(true);
+ }
+}
+
bool GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported() {
if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) {
StringExtractorGDBRemote response;
@@ -2747,8 +2767,13 @@
bool GDBRemoteCommunicationClient::GetStopReply(
StringExtractorGDBRemote &response) {
- if (SendPacketAndWaitForResponse("?", response) == PacketResult::Success)
+ if (SendPacketAndWaitForResponse("?", response) == PacketResult::Success) {
+ if (GetNonStopEnabled()) {
+ if (!DrainNotificationQueue("vStopped"))
+ return false; // TODO: better error handling
+ }
return response.IsNormalResponse();
+ }
return false;
}
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
@@ -12,6 +12,7 @@
#include "GDBRemoteCommunicationHistory.h"
#include <condition_variable>
+#include <deque>
#include <future>
#include <mutex>
#include <queue>
@@ -89,17 +90,18 @@
enum class PacketType { Invalid = 0, Standard, Notify };
enum class PacketResult {
- Success = 0, // Success
- ErrorSendFailed, // Status sending the packet
- ErrorSendAck, // Didn't get an ack back after sending a packet
- ErrorReplyFailed, // Status getting the reply
- ErrorReplyTimeout, // Timed out waiting for reply
- ErrorReplyInvalid, // Got a reply but it wasn't valid for the packet that
- // was sent
- ErrorReplyAck, // Sending reply ack failed
- ErrorDisconnected, // We were disconnected
- ErrorNoSequenceLock // We couldn't get the sequence lock for a multi-packet
- // request
+ Success = 0, // Success
+ ErrorSendFailed, // Status sending the packet
+ ErrorSendAck, // Didn't get an ack back after sending a packet
+ ErrorReplyFailed, // Status getting the reply
+ ErrorReplyTimeout, // Timed out waiting for reply
+ ErrorReplyInvalid, // Got a reply but it wasn't valid for the packet that
+ // was sent
+ ErrorReplyAck, // Sending reply ack failed
+ ErrorDisconnected, // We were disconnected
+ ErrorNoSequenceLock, // We couldn't get the sequence lock for a multi-packet
+ // request
+ Notify, // Successfully gotten a notification packet
};
// Class to change the timeout for a given scope and restore it to the
@@ -179,6 +181,7 @@
bool m_is_platform; // Set to true if this class represents a platform,
// false if this class represents a debug session for
// a single process
+ std::deque<std::string> m_notification_packet_queue;
CompressionType m_compression_type;
@@ -190,7 +193,8 @@
bool skip_ack = false);
PacketResult ReadPacket(StringExtractorGDBRemote &response,
- Timeout<std::micro> timeout, bool sync_on_timeout);
+ Timeout<std::micro> timeout, bool sync_on_timeout,
+ bool allow_notification = false);
PacketResult ReadPacketWithOutputSupport(
StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
@@ -199,7 +203,8 @@
PacketResult WaitForPacketNoLock(StringExtractorGDBRemote &response,
Timeout<std::micro> timeout,
- bool sync_on_timeout);
+ bool sync_on_timeout,
+ bool allow_notification = false);
bool CompressionIsEnabled() {
return m_compression_type != CompressionType::None;
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
@@ -239,16 +239,15 @@
return result;
}
-GDBRemoteCommunication::PacketResult
-GDBRemoteCommunication::ReadPacket(StringExtractorGDBRemote &response,
- Timeout<std::micro> timeout,
- bool sync_on_timeout) {
+GDBRemoteCommunication::PacketResult GDBRemoteCommunication::ReadPacket(
+ StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
+ bool sync_on_timeout, bool allow_notification) {
using ResponseType = StringExtractorGDBRemote::ResponseType;
Log *log = GetLog(GDBRLog::Packets);
for (;;) {
- PacketResult result =
- WaitForPacketNoLock(response, timeout, sync_on_timeout);
+ PacketResult result = WaitForPacketNoLock(
+ response, timeout, sync_on_timeout, allow_notification);
if (result != PacketResult::Success ||
(response.GetResponseType() != ResponseType::eAck &&
response.GetResponseType() != ResponseType::eNack))
@@ -260,15 +259,30 @@
GDBRemoteCommunication::PacketResult
GDBRemoteCommunication::WaitForPacketNoLock(StringExtractorGDBRemote &packet,
Timeout<std::micro> timeout,
- bool sync_on_timeout) {
+ bool sync_on_timeout,
+ bool allow_notification) {
uint8_t buffer[8192];
Status error;
Log *log = GetLog(GDBRLog::Packets);
+ // If the caller expects notifications, check the notification queue first.
+ if (allow_notification && !m_notification_packet_queue.empty()) {
+ packet.Reset(m_notification_packet_queue.front());
+ m_notification_packet_queue.pop_front();
+ return PacketResult::Notify;
+ }
+
// Check for a packet from our cache first without trying any reading...
- if (CheckForPacket(nullptr, 0, packet) != PacketType::Invalid)
- return PacketResult::Success;
+ PacketType packet_type = CheckForPacket(nullptr, 0, packet);
+ if (packet_type == PacketType::Notify && !allow_notification) {
+ // If the caller does not support notifications, queue it for later.
+ m_notification_packet_queue.push_back(packet.GetStringRef().str());
+ packet_type = CheckForPacket(nullptr, 0, packet);
+ }
+ if (packet_type != PacketType::Invalid)
+ return packet_type == PacketType::Standard ? PacketResult::Success
+ : PacketResult::Notify;
bool timed_out = false;
bool disconnected = false;
@@ -283,8 +297,12 @@
bytes_read);
if (bytes_read > 0) {
- if (CheckForPacket(buffer, bytes_read, packet) != PacketType::Invalid)
- return PacketResult::Success;
+ packet_type = CheckForPacket(buffer, bytes_read, packet);
+ if (packet_type == PacketType::Notify && !allow_notification)
+ m_notification_packet_queue.push_back(packet.GetStringRef().str());
+ else if (packet_type != PacketType::Invalid)
+ return packet_type == PacketType::Standard ? PacketResult::Success
+ : PacketResult::Notify;
} else {
switch (status) {
case eConnectionStatusTimedOut:
@@ -1315,6 +1333,9 @@
case PacketResult::ErrorNoSequenceLock:
Stream << "ErrorNoSequenceLock";
break;
+ case PacketResult::Notify:
+ Stream << "Notify";
+ break;
}
}
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
@@ -59,6 +59,12 @@
std::chrono::seconds interrupt_timeout,
llvm::function_ref<void(llvm::StringRef)> output_callback);
+ bool GetNonStopEnabled() { return m_non_stop; }
+
+ void SetNonStopEnabled(bool enabled) { m_non_stop = enabled; }
+
+ bool DrainNotificationQueue(llvm::StringRef command);
+
class Lock {
public:
// If interrupt_timeout == 0 seconds, only take the lock if the target is
@@ -133,6 +139,9 @@
/// Whether we should resume after a stop.
bool m_should_stop;
+
+ /// Whether non-stop protocol should be used.
+ bool m_non_stop = false;
/// @}
/// This handles the synchronization between individual async threads. For
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
@@ -52,15 +52,16 @@
if (!cont_lock)
return eStateInvalid;
OnRunPacketSent(true);
- // The main ReadPacket loop wakes up at computed_timeout intervals, just to
+ // The main ReadPacket loop wakes up at computed_timeout intervals, just to
// check that the connection hasn't dropped. When we wake up we also check
// whether there is an interrupt request that has reached its endpoint.
- // If we want a shorter interrupt timeout that kWakeupInterval, we need to
+ // If we want a shorter interrupt timeout that kWakeupInterval, we need to
// choose the shorter interval for the wake up as well.
- std::chrono::seconds computed_timeout = std::min(interrupt_timeout,
- kWakeupInterval);
+ std::chrono::seconds computed_timeout =
+ std::min(interrupt_timeout, kWakeupInterval);
for (;;) {
- PacketResult read_result = ReadPacket(response, computed_timeout, false);
+ PacketResult read_result =
+ ReadPacket(response, computed_timeout, false, m_non_stop);
// Reset the computed_timeout to the default value in case we are going
// round again.
computed_timeout = std::min(interrupt_timeout, kWakeupInterval);
@@ -79,12 +80,14 @@
// time left till the interrupt timeout. But don't wait longer
// than our wakeup timeout.
auto new_wait = m_interrupt_endpoint - cur_time;
- computed_timeout = std::min(kWakeupInterval,
+ computed_timeout = std::min(
+ kWakeupInterval,
std::chrono::duration_cast<std::chrono::seconds>(new_wait));
continue;
}
break;
}
+ case PacketResult::Notify:
case PacketResult::Success:
break;
default:
@@ -95,9 +98,43 @@
if (response.Empty())
return eStateInvalid;
+ LLDB_LOG(log, "got result {0}, packet: {1}", read_result,
+ response.GetStringRef());
+
+ // We do not currently support full asynchronous communication. Instead,
+ // when in non-stop mode, we wait for the asynchronous %Stop notification
+ // and then drain the notification queue
+ // TODO: issue vCont;t to ensure that all threads have actually stopped
+ // (this is not needed for LLGS but for gdbserver)
+
+ if (read_result == PacketResult::Notify &&
+ response.GetStringRef().startswith("Stdio:")) {
+ std::string inferior_stdout;
+ response.SetFilePos(6);
+ while (!response.IsOKResponse()) {
+ if (response.GetChar() == 'O') {
+ response.GetHexByteString(inferior_stdout);
+ delegate.HandleAsyncStdout(inferior_stdout);
+ }
+ if (SendPacketAndWaitForResponseNoLock("vStdio", response) !=
+ PacketResult::Success ||
+ response.IsUnsupportedResponse() || response.IsErrorResponse()) {
+ LLDB_LOG(log, "vStdio request failed");
+ return eStateInvalid;
+ }
+ }
+ continue;
+ }
+
+ if (read_result == PacketResult::Notify &&
+ response.GetStringRef().startswith("Stop:")) {
+ response.Reset(response.GetStringRef().substr(5));
+
+ if (!DrainNotificationQueue("vStopped"))
+ return eStateInvalid;
+ }
+
const char stop_type = response.GetChar();
- LLDB_LOGF(log, "GDBRemoteClientBase::%s () got packet: %s", __FUNCTION__,
- response.GetStringRef().data());
switch (stop_type) {
case 'W':
@@ -283,6 +320,22 @@
BroadcastEvent(eBroadcastBitRunPacketSent, nullptr);
}
+bool GDBRemoteClientBase::DrainNotificationQueue(llvm::StringRef command) {
+ Log *log = GetLog(GDBRLog::Process);
+ for (;;) {
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponseNoLock(command, response) !=
+ PacketResult::Success || response.IsUnsupportedResponse()) {
+ LLDB_LOG(log, "{0} failed", command);
+ return false;
+ }
+
+ if (response.IsOKResponse())
+ return true;
+ // otherwise, loop
+ }
+}
+
///////////////////////////////////////
// GDBRemoteClientBase::ContinueLock //
///////////////////////////////////////
@@ -322,9 +375,20 @@
__FUNCTION__);
return LockResult::Cancelled;
}
- if (m_comm.SendPacketNoLock(m_comm.m_continue_packet) !=
- PacketResult::Success)
- return LockResult::Failed;
+
+ // When in non-stop mode, all continue packets should yield an immediate "OK"
+ // response before we consider the action successful.
+ if (m_comm.m_non_stop) {
+ StringExtractorGDBRemote response;
+ if (m_comm.SendPacketAndWaitForResponseNoLock(
+ m_comm.m_continue_packet, response) != PacketResult::Success ||
+ !response.IsOKResponse())
+ return LockResult::Failed;
+ } else {
+ if (m_comm.SendPacketNoLock(m_comm.m_continue_packet) !=
+ PacketResult::Success)
+ return LockResult::Failed;
+ }
lldbassert(!m_comm.m_is_running);
m_comm.m_is_running = true;
@@ -347,7 +411,7 @@
}
void GDBRemoteClientBase::Lock::SyncWithContinueThread() {
- Log *log = GetLog(GDBRLog::Process|GDBRLog::Packets);
+ Log *log = GetLog(GDBRLog::Process | GDBRLog::Packets);
std::unique_lock<std::mutex> lock(m_comm.m_mutex);
if (m_comm.m_is_running && m_interrupt_timeout == std::chrono::seconds(0))
return; // We were asked to avoid interrupting the sender. Lock is not
@@ -358,17 +422,25 @@
if (m_comm.m_async_count == 1) {
// The sender has sent the continue packet and we are the first async
// packet. Let's interrupt it.
- const char ctrl_c = '\x03';
- ConnectionStatus status = eConnectionStatusSuccess;
- size_t bytes_written = m_comm.Write(&ctrl_c, 1, status, nullptr);
- if (bytes_written == 0) {
+ bool success;
+ if (m_comm.GetNonStopEnabled()) {
+ StringExtractorGDBRemote halt_response;
+ // Note: we expect an "OK" response here but due to the async logic,
+ // we need to check for it in SendContinuePacketAndWaitForResponse()
+ success = m_comm.SendPacketNoLock("vCtrlC") == PacketResult::Success;
+ } else {
+ const char ctrl_c = '\x03';
+ ConnectionStatus status = eConnectionStatusSuccess;
+ success = m_comm.Write(&ctrl_c, 1, status, nullptr) != 0;
+ }
+ if (!success) {
--m_comm.m_async_count;
LLDB_LOGF(log, "GDBRemoteClientBase::Lock::Lock failed to send "
"interrupt packet");
return;
}
m_comm.m_interrupt_endpoint = steady_clock::now() + m_interrupt_timeout;
- if (log)
+ if (log && !m_comm.GetNonStopEnabled())
log->PutCString("GDBRemoteClientBase::Lock::Lock sent packet: \\x03");
}
m_comm.m_cv.wait(lock, [this] { return !m_comm.m_is_running; });
Index: lldb/packages/Python/lldbsuite/test/gdbclientutils.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/gdbclientutils.py
+++ lldb/packages/Python/lldbsuite/test/gdbclientutils.py
@@ -23,10 +23,12 @@
Create a framed packet that's ready to send over the GDB connection
channel.
- Framing includes surrounding the message between $ and #, and appending
- a two character hex checksum.
+ Framing includes adding a $ prefix (unless the message already starts
+ with a % prefix, in which case no additional prefix is added),
+ and appending # followed by a two character hex checksum.
"""
- return "$%s#%02x" % (message, checksum(message))
+ prefix = "" if message.startswith("%") else "$"
+ return "%s%s#%02x" % (prefix, message, checksum(message))
def escape_binary(message):
@@ -200,6 +202,14 @@
if packet.startswith("qRegisterInfo"):
regnum = int(packet[len("qRegisterInfo"):], 16)
return self.qRegisterInfo(regnum)
+ if packet.startswith("QNonStop:"):
+ return self.QNonStop(int(packet.split(":", 1)[1]))
+ if packet == "vStdio":
+ return self.vStdio()
+ if packet == "vStopped":
+ return self.vStopped()
+ if packet in ("vCtrlC", "vCont;t"):
+ return self.vCtrlC()
if packet == "k":
return self.k()
@@ -333,6 +343,18 @@
def qRegisterInfo(self, num):
return ""
+ def QNonStop(self, enabled):
+ return ""
+
+ def vStdio(self):
+ return ""
+
+ def vStopped(self):
+ return ""
+
+ def vCtrlC(self):
+ return ""
+
def k(self):
return ["W01", self.RESPONSE_DISCONNECT]
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits