diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -903,7 +903,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) { @@ -925,33 +924,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; } } } diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py b/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py --- a/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py +++ b/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", + ]) diff --git a/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py b/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py --- a/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py +++ b/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py @@ -194,6 +194,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) @@ -313,6 +317,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 diff --git a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py b/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py --- a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py +++ b/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") diff --git a/lldb/test/API/tools/lldb-server/main.cpp b/lldb/test/API/tools/lldb-server/main.cpp --- a/lldb/test/API/tools/lldb-server/main.cpp +++ b/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]);