mgorny created this revision.
mgorny added reviewers: krytarowski, emaste, labath, jasonmolenda, JDevlieghere.
mgorny requested review of this revision.

Fall back to QEnvironmentHexEncoded if QEnvironment is not supported.
The latter packet is an LLDB extension, while the former is universally
supported.

Add tests for both QEnvironment and QEnvironmentHexEncoded packets,
including both use due to characters that need escaping and fallback
when QEnvironment is not supported.


https://reviews.llvm.org/D108018

Files:
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py
  lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
  lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
  lldb/test/API/tools/lldb-server/main.cpp

Index: lldb/test/API/tools/lldb-server/main.cpp
===================================================================
--- lldb/test/API/tools/lldb-server/main.cpp
+++ lldb/test/API/tools/lldb-server/main.cpp
@@ -326,6 +326,10 @@
       g_threads_do_segfault = true;
     } else if (consume_front(arg, "print-pid")) {
       print_pid();
+    } else if (consume_front(arg, "print-env:")) {
+      // Print the value of specified envvar to stdout.
+      const char *value = getenv(arg.c_str());
+      printf("%s\n", value ? value : "__unset__");
     } else {
       // Treat the argument as text for stdout.
       printf("%s\n", argv[i]);
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
@@ -1307,3 +1307,56 @@
              "send packet: $W00#00"],
             True)
         self.expect_gdbremote_sequence()
+
+    def test_QEnvironment(self):
+        self.build()
+        exe_path = self.getBuildArtifact("a.out")
+        env = {"FOO": "test", "BAR": "a=z"}
+        args = [exe_path, "print-env:FOO", "print-env:BAR"]
+        hex_args = [binascii.b2a_hex(x.encode()).decode() for x in args]
+
+        server = self.connect_to_debug_monitor()
+        self.assertIsNotNone(server)
+        self.do_handshake()
+
+        for key, value in env.items():
+            self.test_sequence.add_log_lines(
+                ["read packet: $QEnvironment:%s=%s#00" % (key, value),
+                 "send packet: $OK#00"],
+                True)
+        self.test_sequence.add_log_lines(
+            ["read packet: $vRun;%s#00" % (";".join(hex_args),),
+             {"direction": "send",
+              "regex": r"^\$T([0-9a-fA-F]+)"},
+             "read packet: $c#00",
+             "send packet: $W00#00"],
+            True)
+        context = self.expect_gdbremote_sequence()
+        self.assertEqual(context["O_content"], b"test\r\na=z\r\n")
+
+    def test_QEnvironmentHexEncoded(self):
+        self.build()
+        exe_path = self.getBuildArtifact("a.out")
+        env = {"FOO": "test", "BAR": "a=z", "BAZ": "a*}#z"}
+        args = [exe_path, "print-env:FOO", "print-env:BAR", "print-env:BAZ"]
+        hex_args = [binascii.b2a_hex(x.encode()).decode() for x in args]
+
+        server = self.connect_to_debug_monitor()
+        self.assertIsNotNone(server)
+        self.do_handshake()
+
+        for key, value in env.items():
+            hex_enc = binascii.b2a_hex(("%s=%s" % (key, value)).encode()).decode()
+            self.test_sequence.add_log_lines(
+                ["read packet: $QEnvironmentHexEncoded:%s#00" % (hex_enc,),
+                 "send packet: $OK#00"],
+                True)
+        self.test_sequence.add_log_lines(
+            ["read packet: $vRun;%s#00" % (";".join(hex_args),),
+             {"direction": "send",
+              "regex": r"^\$T([0-9a-fA-F]+)"},
+             "read packet: $c#00",
+             "send packet: $W00#00"],
+            True)
+        context = self.expect_gdbremote_sequence()
+        self.assertEqual(context["O_content"], b"test\r\na=z\r\na*}#z\r\n")
Index: lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
===================================================================
--- lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
+++ lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
@@ -187,6 +187,10 @@
             return self.vRun(packet)
         if packet.startswith("qLaunchSuccess"):
             return self.qLaunchSuccess()
+        if packet.startswith("QEnvironment:"):
+            return self.QEnvironment(packet)
+        if packet.startswith("QEnvironmentHexEncoded:"):
+            return self.QEnvironmentHexEncoded(packet)
 
         return self.other(packet)
 
@@ -303,6 +307,12 @@
     def qLaunchSuccess(self):
         return ""
 
+    def QEnvironment(self, packet):
+        return "OK"
+
+    def QEnvironmentHexEncoded(self, packet):
+        return "OK"
+
     """
     Raised when we receive a packet for which there is no default action.
     Override the responder class to implement behavior suitable for the test at
Index: lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py
===================================================================
--- lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py
+++ lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py
@@ -258,3 +258,96 @@
         self.assertPacketLogContains([
           "vRun;%s;61726731;61726732;61726733" % (exe_hex,)
         ])
+
+    def test_launch_QEnvironment(self):
+        class MyResponder(MockGDBServerResponder):
+            def qC(self):
+                return "E42"
+
+            def qfThreadInfo(self):
+               return "E42"
+
+            def vRun(self, packet):
+                self.started = True
+                return "E28"
+
+        self.server.responder = MyResponder()
+
+        target = self.createTarget("a.yaml")
+        process = self.connect(target)
+        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
+                                      [lldb.eStateConnected])
+
+        target.Launch(lldb.SBListener(),
+                      [],  # argv
+                      ["PLAIN=foo",
+                       "NEEDSENC=frob$",
+                       "NEEDSENC2=fr*ob",
+                       "NEEDSENC3=fro}b",
+                       "NEEDSENC4=f#rob",
+                       "EQUALS=foo=bar",
+                       ],  # envp
+                      None,  # stdin_path
+                      None,  # stdout_path
+                      None,  # stderr_path
+                      None,  # working_directory
+                      0,  # launch_flags
+                      True,  # stop_at_entry
+                      lldb.SBError())  # error
+
+        self.assertPacketLogContains([
+          "QEnvironment:PLAIN=foo",
+          "QEnvironmentHexEncoded:4e45454453454e433d66726f6224",
+          "QEnvironmentHexEncoded:4e45454453454e43323d66722a6f62",
+          "QEnvironmentHexEncoded:4e45454453454e43333d66726f7d62",
+          "QEnvironmentHexEncoded:4e45454453454e43343d6623726f62",
+          "QEnvironment:EQUALS=foo=bar",
+        ])
+
+    def test_launch_QEnvironmentHexEncoded_only(self):
+        class MyResponder(MockGDBServerResponder):
+            def qC(self):
+                return "E42"
+
+            def qfThreadInfo(self):
+               return "E42"
+
+            def vRun(self, packet):
+                self.started = True
+                return "E28"
+
+            def QEnvironment(self, packet):
+                return ""
+
+        self.server.responder = MyResponder()
+
+        target = self.createTarget("a.yaml")
+        process = self.connect(target)
+        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
+                                      [lldb.eStateConnected])
+
+        target.Launch(lldb.SBListener(),
+                      [],  # argv
+                      ["PLAIN=foo",
+                       "NEEDSENC=frob$",
+                       "NEEDSENC2=fr*ob",
+                       "NEEDSENC3=fro}b",
+                       "NEEDSENC4=f#rob",
+                       "EQUALS=foo=bar",
+                       ],  # envp
+                      None,  # stdin_path
+                      None,  # stdout_path
+                      None,  # stderr_path
+                      None,  # working_directory
+                      0,  # launch_flags
+                      True,  # stop_at_entry
+                      lldb.SBError())  # error
+
+        self.assertPacketLogContains([
+          "QEnvironmentHexEncoded:504c41494e3d666f6f",
+          "QEnvironmentHexEncoded:4e45454453454e433d66726f6224",
+          "QEnvironmentHexEncoded:4e45454453454e43323d66722a6f62",
+          "QEnvironmentHexEncoded:4e45454453454e43333d66726f7d62",
+          "QEnvironmentHexEncoded:4e45454453454e43343d6623726f62",
+          "QEnvironmentHexEncoded:455155414c533d666f6f3d626172",
+        ])
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
@@ -876,7 +876,6 @@
 int GDBRemoteCommunicationClient::SendEnvironmentPacket(
     char const *name_equal_value) {
   if (name_equal_value && name_equal_value[0]) {
-    StreamString packet;
     bool send_hex_encoding = false;
     for (const char *p = name_equal_value; *p != '\0' && !send_hex_encoding;
          ++p) {
@@ -898,33 +897,43 @@
     }
 
     StringExtractorGDBRemote response;
-    if (send_hex_encoding) {
-      if (m_supports_QEnvironmentHexEncoded) {
-        packet.PutCString("QEnvironmentHexEncoded:");
-        packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value));
-        if (SendPacketAndWaitForResponse(packet.GetString(), response) ==
-            PacketResult::Success) {
-          if (response.IsOKResponse())
-            return 0;
-          uint8_t error = response.GetError();
-          if (error)
-            return error;
-          if (response.IsUnsupportedResponse())
-            m_supports_QEnvironmentHexEncoded = false;
-        }
+    // Prefer sending unencoded, if possible and the server supports it.
+    if (!send_hex_encoding && m_supports_QEnvironment) {
+      StreamString packet;
+      packet.Printf("QEnvironment:%s", name_equal_value);
+      if (SendPacketAndWaitForResponse(packet.GetString(), response) !=
+          PacketResult::Success)
+        return -1;
+
+      if (response.IsOKResponse())
+        return 0;
+      if (response.IsUnsupportedResponse())
+        m_supports_QEnvironment = false;
+      else {
+        uint8_t error = response.GetError();
+        if (error)
+          return error;
+        return -1;
       }
+    }
 
-    } else if (m_supports_QEnvironment) {
-      packet.Printf("QEnvironment:%s", name_equal_value);
-      if (SendPacketAndWaitForResponse(packet.GetString(), response) ==
-          PacketResult::Success) {
-        if (response.IsOKResponse())
-          return 0;
+    if (m_supports_QEnvironmentHexEncoded) {
+      StreamString packet;
+      packet.PutCString("QEnvironmentHexEncoded:");
+      packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value));
+      if (SendPacketAndWaitForResponse(packet.GetString(), response) !=
+          PacketResult::Success)
+        return -1;
+
+      if (response.IsOKResponse())
+        return 0;
+      if (response.IsUnsupportedResponse())
+        m_supports_QEnvironmentHexEncoded = false;
+      else {
         uint8_t error = response.GetError();
         if (error)
           return error;
-        if (response.IsUnsupportedResponse())
-          m_supports_QEnvironment = false;
+        return -1;
       }
     }
   }
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to