mgorny created this revision.
mgorny added reviewers: labath, emaste, jingham, krytarowski.
Herald added a subscriber: arichardson.
Herald added a project: All.
mgorny requested review of this revision.

Add explicit support for reading (or skipping) notification packets
to GDBRemoteCommunication::ReadPacket().  Previously, the code would not
really distinguish between regular replies and notifications,
and would return the next packet to the caller without even a clear
indication whether it is a regular or notification packet.

Since the majority of existing call sites do not expect to read
notifications, add a new "allow_notification" parameter that defaults to
false.  If it is false, then any notifications received from the server
are stashed onto a queue and the function waits for a regular reply
instead.  If it is true, then the function either returns the first
notification queued previously or waits for any packet (either regular
or notification).

To make it possible to clearly distinguish between regular
and notification packets, add a new PacketResult::Notify.  It is used
in lieu of PacketResult::Success when a notification packet is read.
While admittedly this is not the cleanest possible solution, it requires
minimal changes to the existing code.  In the end, we expect only a few
call sites to actually expect and read notifications.

Split from the non-stop protocol support in D126614 
<https://reviews.llvm.org/D126614>.

Sponsored by: The FreeBSD Foundation


https://reviews.llvm.org/D131094

Files:
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
  lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp

Index: lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp
===================================================================
--- lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp
+++ lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp
@@ -20,9 +20,11 @@
   TestClient()
       : GDBRemoteCommunication("test.client", "test.client.listener") {}
 
-  PacketResult ReadPacket(StringExtractorGDBRemote &response) {
+  PacketResult ReadPacket(StringExtractorGDBRemote &response,
+                          bool allow_notify = false) {
     return GDBRemoteCommunication::ReadPacket(response, std::chrono::seconds(1),
-                                              /*sync_on_timeout*/ false);
+                                              /*sync_on_timeout*/ false,
+                                              allow_notify);
   }
 };
 
@@ -69,3 +71,23 @@
     ASSERT_EQ(PacketResult::Success, server.GetAck());
   }
 }
+
+TEST_F(GDBRemoteCommunicationTest, ReadPacketNotify) {
+  StringExtractorGDBRemote response;
+  ASSERT_TRUE(Write("%Stdio:O48656c6c6f20776f726c640d0a#f1"));
+  ASSERT_TRUE(Write("%Stop:W29;process:114b63#97"));
+  ASSERT_EQ(PacketResult::Notify, client.ReadPacket(response, true));
+  ASSERT_EQ("Stdio:O48656c6c6f20776f726c640d0a", response.GetStringRef());
+  ASSERT_EQ(PacketResult::Notify, client.ReadPacket(response, true));
+  ASSERT_EQ("Stop:W29;process:114b63", response.GetStringRef());
+}
+
+TEST_F(GDBRemoteCommunicationTest, ReadPacketNotifyQueue) {
+  StringExtractorGDBRemote response;
+  ASSERT_TRUE(Write("%Stop:W29;process:114b63#97"));
+  ASSERT_TRUE(Write("$foobar#79"));
+  ASSERT_EQ(PacketResult::Success, client.ReadPacket(response));
+  ASSERT_EQ("foobar", response.GetStringRef());
+  ASSERT_EQ(PacketResult::Notify, client.ReadPacket(response, true));
+  ASSERT_EQ("Stop:W29;process:114b63", response.GetStringRef());
+}
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,14 @@
               bytes_read);
 
     if (bytes_read > 0) {
-      if (CheckForPacket(buffer, bytes_read, packet) != PacketType::Invalid)
-        return PacketResult::Success;
+      packet_type = CheckForPacket(buffer, bytes_read, packet);
+      while (packet_type == PacketType::Notify && !allow_notification) {
+        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;
     } else {
       switch (status) {
       case eConnectionStatusTimedOut:
@@ -1315,6 +1335,9 @@
   case PacketResult::ErrorNoSequenceLock:
     Stream << "ErrorNoSequenceLock";
     break;
+  case PacketResult::Notify:
+    Stream << "Notify";
+    break;
   }
 }
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to