Skip to content

Commit 8d1de7b

Browse files
committed
[lldb/gdb-server] Better reporting of launch errors
Use our "rich error" facility to propagate error reported by the stub to the user. lldb-server reports rich launch errors as of D133352. To make this easier to implement, and reduce code duplication, I have moved the vRun/A/qLaunchSuccess handling into a single GDBRemoteCommunicationClient function. Differential Revision: https://reviews.llvm.org/D134754
1 parent 5614438 commit 8d1de7b

File tree

5 files changed

+118
-145
lines changed

5 files changed

+118
-145
lines changed

lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "lldb/Utility/Status.h"
3030
#include "lldb/Utility/StreamString.h"
3131
#include "lldb/Utility/UriParser.h"
32+
#include "llvm/Support/FormatAdapters.h"
3233

3334
#include "Plugins/Process/Utility/GDBRemoteSignals.h"
3435
#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
@@ -361,39 +362,36 @@ Status PlatformRemoteGDBServer::LaunchProcess(ProcessLaunchInfo &launch_info) {
361362
"PlatformRemoteGDBServer::%s() set launch architecture triple to '%s'",
362363
__FUNCTION__, arch_triple ? arch_triple : "<NULL>");
363364

364-
int arg_packet_err;
365365
{
366366
// Scope for the scoped timeout object
367367
process_gdb_remote::GDBRemoteCommunication::ScopedTimeout timeout(
368368
*m_gdb_client_up, std::chrono::seconds(5));
369-
arg_packet_err = m_gdb_client_up->SendArgumentsPacket(launch_info);
369+
// Since we can't send argv0 separate from the executable path, we need to
370+
// make sure to use the actual executable path found in the launch_info...
371+
Args args = launch_info.GetArguments();
372+
if (FileSpec exe_file = launch_info.GetExecutableFile())
373+
args.ReplaceArgumentAtIndex(0, exe_file.GetPath(false));
374+
if (llvm::Error err = m_gdb_client_up->LaunchProcess(args)) {
375+
error.SetErrorStringWithFormatv("Cannot launch '{0}': {1}",
376+
args.GetArgumentAtIndex(0),
377+
llvm::fmt_consume(std::move(err)));
378+
return error;
379+
}
370380
}
371381

372-
if (arg_packet_err == 0) {
373-
std::string error_str;
374-
if (m_gdb_client_up->GetLaunchSuccess(error_str)) {
375-
const auto pid = m_gdb_client_up->GetCurrentProcessID(false);
376-
if (pid != LLDB_INVALID_PROCESS_ID) {
377-
launch_info.SetProcessID(pid);
378-
LLDB_LOGF(log,
379-
"PlatformRemoteGDBServer::%s() pid %" PRIu64
380-
" launched successfully",
381-
__FUNCTION__, pid);
382-
} else {
383-
LLDB_LOGF(log,
384-
"PlatformRemoteGDBServer::%s() launch succeeded but we "
385-
"didn't get a valid process id back!",
386-
__FUNCTION__);
387-
error.SetErrorString("failed to get PID");
388-
}
389-
} else {
390-
error.SetErrorString(error_str.c_str());
391-
LLDB_LOGF(log, "PlatformRemoteGDBServer::%s() launch failed: %s",
392-
__FUNCTION__, error.AsCString());
393-
}
382+
const auto pid = m_gdb_client_up->GetCurrentProcessID(false);
383+
if (pid != LLDB_INVALID_PROCESS_ID) {
384+
launch_info.SetProcessID(pid);
385+
LLDB_LOGF(log,
386+
"PlatformRemoteGDBServer::%s() pid %" PRIu64
387+
" launched successfully",
388+
__FUNCTION__, pid);
394389
} else {
395-
error.SetErrorStringWithFormat("'A' packet returned an error: %i",
396-
arg_packet_err);
390+
LLDB_LOGF(log,
391+
"PlatformRemoteGDBServer::%s() launch succeeded but we "
392+
"didn't get a valid process id back!",
393+
__FUNCTION__);
394+
error.SetErrorString("failed to get PID");
397395
}
398396
return error;
399397
}

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp

Lines changed: 55 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -746,108 +746,69 @@ lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) {
746746
return LLDB_INVALID_PROCESS_ID;
747747
}
748748

749-
bool GDBRemoteCommunicationClient::GetLaunchSuccess(std::string &error_str) {
750-
error_str.clear();
751-
StringExtractorGDBRemote response;
752-
if (SendPacketAndWaitForResponse("qLaunchSuccess", response) ==
753-
PacketResult::Success) {
754-
if (response.IsOKResponse())
755-
return true;
756-
// GDB does not implement qLaunchSuccess -- but if we used vRun,
757-
// then we already received a successful launch indication via stop
758-
// reason.
759-
if (response.IsUnsupportedResponse() && m_supports_vRun)
760-
return true;
761-
if (response.GetChar() == 'E') {
762-
// A string the describes what failed when launching...
763-
error_str = std::string(response.GetStringRef().substr(1));
764-
} else {
765-
error_str.assign("unknown error occurred launching process");
749+
llvm::Error GDBRemoteCommunicationClient::LaunchProcess(const Args &args) {
750+
if (!args.GetArgumentAtIndex(0))
751+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
752+
"Nothing to launch");
753+
// try vRun first
754+
if (m_supports_vRun) {
755+
StreamString packet;
756+
packet.PutCString("vRun");
757+
for (const Args::ArgEntry &arg : args) {
758+
packet.PutChar(';');
759+
packet.PutStringAsRawHex8(arg.ref());
766760
}
767-
} else {
768-
error_str.assign("timed out waiting for app to launch");
769-
}
770-
return false;
771-
}
772761

773-
int GDBRemoteCommunicationClient::SendArgumentsPacket(
774-
const ProcessLaunchInfo &launch_info) {
775-
// Since we don't get the send argv0 separate from the executable path, we
776-
// need to make sure to use the actual executable path found in the
777-
// launch_info...
778-
std::vector<const char *> argv;
779-
FileSpec exe_file = launch_info.GetExecutableFile();
780-
std::string exe_path;
781-
const char *arg = nullptr;
782-
const Args &launch_args = launch_info.GetArguments();
783-
if (exe_file)
784-
exe_path = exe_file.GetPath(false);
785-
else {
786-
arg = launch_args.GetArgumentAtIndex(0);
787-
if (arg)
788-
exe_path = arg;
789-
}
790-
if (!exe_path.empty()) {
791-
argv.push_back(exe_path.c_str());
792-
for (uint32_t i = 1; (arg = launch_args.GetArgumentAtIndex(i)) != nullptr;
793-
++i) {
794-
if (arg)
795-
argv.push_back(arg);
796-
}
797-
}
798-
if (!argv.empty()) {
799-
// try vRun first
800-
if (m_supports_vRun) {
801-
StreamString packet;
802-
packet.PutCString("vRun");
803-
for (const char *arg : argv) {
804-
packet.PutChar(';');
805-
packet.PutBytesAsRawHex8(arg, strlen(arg));
806-
}
762+
StringExtractorGDBRemote response;
763+
if (SendPacketAndWaitForResponse(packet.GetString(), response) !=
764+
PacketResult::Success)
765+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
766+
"Sending vRun packet failed");
807767

808-
StringExtractorGDBRemote response;
809-
if (SendPacketAndWaitForResponse(packet.GetString(), response) !=
810-
PacketResult::Success)
811-
return -1;
768+
if (response.IsErrorResponse())
769+
return response.GetStatus().ToError();
812770

813-
if (response.IsErrorResponse()) {
814-
uint8_t error = response.GetError();
815-
if (error)
816-
return error;
817-
return -1;
818-
}
819-
// vRun replies with a stop reason packet
820-
// FIXME: right now we just discard the packet and LLDB queries
821-
// for stop reason again
822-
if (!response.IsUnsupportedResponse())
823-
return 0;
771+
// vRun replies with a stop reason packet
772+
// FIXME: right now we just discard the packet and LLDB queries
773+
// for stop reason again
774+
if (!response.IsUnsupportedResponse())
775+
return llvm::Error::success();
824776

825-
m_supports_vRun = false;
826-
}
777+
m_supports_vRun = false;
778+
}
827779

828-
// fallback to A
829-
StreamString packet;
830-
packet.PutChar('A');
831-
for (size_t i = 0, n = argv.size(); i < n; ++i) {
832-
arg = argv[i];
833-
const int arg_len = strlen(arg);
834-
if (i > 0)
835-
packet.PutChar(',');
836-
packet.Printf("%i,%i,", arg_len * 2, (int)i);
837-
packet.PutBytesAsRawHex8(arg, arg_len);
838-
}
780+
// fallback to A
781+
StreamString packet;
782+
packet.PutChar('A');
783+
llvm::ListSeparator LS(",");
784+
for (const auto &arg : llvm::enumerate(args)) {
785+
packet << LS;
786+
packet.Format("{0},{1},", arg.value().ref().size() * 2, arg.index());
787+
packet.PutStringAsRawHex8(arg.value().ref());
788+
}
839789

840-
StringExtractorGDBRemote response;
841-
if (SendPacketAndWaitForResponse(packet.GetString(), response) ==
842-
PacketResult::Success) {
843-
if (response.IsOKResponse())
844-
return 0;
845-
uint8_t error = response.GetError();
846-
if (error)
847-
return error;
848-
}
790+
StringExtractorGDBRemote response;
791+
if (SendPacketAndWaitForResponse(packet.GetString(), response) !=
792+
PacketResult::Success) {
793+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
794+
"Sending A packet failed");
849795
}
850-
return -1;
796+
if (!response.IsOKResponse())
797+
return response.GetStatus().ToError();
798+
799+
if (SendPacketAndWaitForResponse("qLaunchSuccess", response) !=
800+
PacketResult::Success) {
801+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
802+
"Sending qLaunchSuccess packet failed");
803+
}
804+
if (response.IsOKResponse())
805+
return llvm::Error::success();
806+
if (response.GetChar() == 'E') {
807+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
808+
response.GetStringRef().substr(1));
809+
}
810+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
811+
"unknown error occurred launching process");
851812
}
852813

853814
int GDBRemoteCommunicationClient::SendEnvironment(const Environment &env) {

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,6 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
8080

8181
lldb::pid_t GetCurrentProcessID(bool allow_lazy = true);
8282

83-
bool GetLaunchSuccess(std::string &error_str);
84-
8583
bool LaunchGDBServer(const char *remote_accept_hostname, lldb::pid_t &pid,
8684
uint16_t &port, std::string &socket_name);
8785

@@ -90,19 +88,11 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
9088

9189
bool KillSpawnedProcess(lldb::pid_t pid);
9290

93-
/// Sends a GDB remote protocol 'A' packet that delivers program
94-
/// arguments to the remote server.
95-
///
96-
/// \param[in] launch_info
97-
/// A NULL terminated array of const C strings to use as the
98-
/// arguments.
91+
/// Launch the process using the provided arguments.
9992
///
100-
/// \return
101-
/// Zero if the response was "OK", a positive value if the
102-
/// the response was "Exx" where xx are two hex digits, or
103-
/// -1 if the call is unsupported or any other unexpected
104-
/// response was received.
105-
int SendArgumentsPacket(const ProcessLaunchInfo &launch_info);
93+
/// \param[in] args
94+
/// A list of program arguments. The first entry is the program being run.
95+
llvm::Error LaunchProcess(const Args &args);
10696

10797
/// Sends a "QEnvironment:NAME=VALUE" packet that will build up the
10898
/// environment that will get used when launching an application

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484

8585
#include "llvm/ADT/ScopeExit.h"
8686
#include "llvm/ADT/StringSwitch.h"
87+
#include "llvm/Support/FormatAdapters.h"
8788
#include "llvm/Support/Threading.h"
8889
#include "llvm/Support/raw_ostream.h"
8990

@@ -799,17 +800,17 @@ Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module,
799800
GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm,
800801
std::chrono::seconds(10));
801802

802-
int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info);
803-
if (arg_packet_err == 0) {
804-
std::string error_str;
805-
if (m_gdb_comm.GetLaunchSuccess(error_str)) {
806-
SetID(m_gdb_comm.GetCurrentProcessID());
807-
} else {
808-
error.SetErrorString(error_str.c_str());
809-
}
803+
// Since we can't send argv0 separate from the executable path, we need to
804+
// make sure to use the actual executable path found in the launch_info...
805+
Args args = launch_info.GetArguments();
806+
if (FileSpec exe_file = launch_info.GetExecutableFile())
807+
args.ReplaceArgumentAtIndex(0, exe_file.GetPath(false));
808+
if (llvm::Error err = m_gdb_comm.LaunchProcess(args)) {
809+
error.SetErrorStringWithFormatv("Cannot launch '{0}': {1}",
810+
args.GetArgumentAtIndex(0),
811+
llvm::fmt_consume(std::move(err)));
810812
} else {
811-
error.SetErrorStringWithFormat("'A' packet returned an error: %i",
812-
arg_packet_err);
813+
SetID(m_gdb_comm.GetCurrentProcessID());
813814
}
814815
}
815816

lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,30 @@ def A(self, packet):
8686
error = lldb.SBError()
8787
target.Launch(lldb.SBListener(), None, None, None, None, None,
8888
None, 0, True, error)
89-
self.assertEquals("'A' packet returned an error: 71", error.GetCString())
89+
self.assertRegex(error.GetCString(), "Cannot launch '.*a': Error 71")
90+
91+
def test_launch_rich_error(self):
92+
class MyResponder(MockGDBServerResponder):
93+
def qC(self):
94+
return "E42"
95+
96+
def qfThreadInfo(self):
97+
return "OK" # No threads.
98+
99+
# Then, when we are asked to attach, error out.
100+
def vRun(self, packet):
101+
return "Eff;" + seven.hexlify("I'm a teapot")
102+
103+
self.server.responder = MyResponder()
104+
105+
target = self.createTarget("a.yaml")
106+
process = self.connect(target)
107+
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected])
108+
109+
error = lldb.SBError()
110+
target.Launch(lldb.SBListener(), None, None, None, None, None,
111+
None, 0, True, error)
112+
self.assertRegex(error.GetCString(), "Cannot launch '.*a': I'm a teapot")
90113

91114
def test_read_registers_using_g_packets(self):
92115
"""Test reading registers using 'g' packets (default behavior)"""

0 commit comments

Comments
 (0)