[Lldb-commits] [lldb] [lldb] Remove calls to exit() in lldb-server (PR #186289)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Thu Mar 12 18:39:44 PDT 2026
https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/186289
>From a779a3daff8ee4c26a333cc2963447d193954619 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Thu, 12 Mar 2026 18:05:51 -0700
Subject: [PATCH 1/2] [lldb] Remove calls to exit() in lldb-server (NFC)
---
lldb/tools/lldb-server/lldb-gdbserver.cpp | 167 +++++++++++-----------
lldb/tools/lldb-server/lldb-server.cpp | 55 ++++---
2 files changed, 111 insertions(+), 111 deletions(-)
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index e1dc15d83069f..2246abe6a9b80 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -9,7 +9,6 @@
#include <cerrno>
#include <cstdint>
#include <cstdio>
-#include <cstdlib>
#include <cstring>
#ifndef _WIN32
@@ -35,6 +34,8 @@
#include "llvm/Option/Option.h"
#include "llvm/Support/Errno.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorExtras.h"
+#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/WithColor.h"
#if defined(__linux__)
@@ -107,23 +108,24 @@ static void sighup_handler(MainLoopBase &mainloop) {
}
#endif // #ifndef _WIN32
-void handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server,
- lldb::pid_t pid) {
+llvm::Error handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server,
+ lldb::pid_t pid) {
Status error = gdb_server.AttachToProcess(pid);
- if (error.Fail()) {
- fprintf(stderr, "error: failed to attach to pid %" PRIu64 ": %s\n", pid,
- error.AsCString());
- exit(1);
- }
+ if (error.Fail())
+ return llvm::createStringErrorV("failed to attach to pid {0}: {1}", pid,
+ error.AsCString());
+ return llvm::Error::success();
}
-void handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server,
- const std::string &process_name) {
+llvm::Error
+handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server,
+ const std::string &process_name) {
// FIXME implement.
+ return llvm::Error::success();
}
-void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server,
- const std::string &attach_target) {
+llvm::Error handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server,
+ const std::string &attach_target) {
assert(!attach_target.empty() && "attach_target cannot be empty");
// First check if the attach_target is convertible to a long. If so, we'll use
@@ -135,23 +137,22 @@ void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server,
if (end_p &&
static_cast<size_t>(end_p - attach_target.c_str()) ==
attach_target.size())
- handle_attach_to_pid(gdb_server, static_cast<lldb::pid_t>(pid));
- else
- handle_attach_to_process_name(gdb_server, attach_target);
+ return handle_attach_to_pid(gdb_server, static_cast<lldb::pid_t>(pid));
+ return handle_attach_to_process_name(gdb_server, attach_target);
}
-void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server,
- llvm::ArrayRef<llvm::StringRef> Arguments) {
+llvm::Error handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server,
+ llvm::ArrayRef<llvm::StringRef> Arguments) {
ProcessLaunchInfo info;
info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug |
eLaunchFlagDisableASLR);
info.SetArguments(Args(Arguments), true);
llvm::SmallString<64> cwd;
- if (std::error_code ec = llvm::sys::fs::current_path(cwd)) {
- llvm::errs() << "Error getting current directory: " << ec.message() << "\n";
- exit(1);
- }
+ if (std::error_code ec = llvm::sys::fs::current_path(cwd))
+ return llvm::createStringErrorV("Error getting current directory: {0}",
+ ec.message());
+
FileSpec cwd_spec(cwd);
FileSystem::Instance().Resolve(cwd_spec);
info.SetWorkingDirectory(cwd_spec);
@@ -160,15 +161,15 @@ void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server,
gdb_server.SetLaunchInfo(info);
Status error = gdb_server.LaunchProcess();
- if (error.Fail()) {
- llvm::errs() << llvm::formatv("error: failed to launch '{0}': {1}\n",
- Arguments[0], error);
- exit(1);
- }
+ if (error.Fail())
+ return llvm::createStringErrorV("failed to launch '{0}': {1}", Arguments[0],
+ error);
+
+ return llvm::Error::success();
}
-static Status writeSocketIdToPipe(Pipe &port_pipe,
- const std::string &socket_id) {
+static llvm::Error writeSocketIdToPipe(Pipe &port_pipe,
+ const std::string &socket_id) {
// NB: Include the nul character at the end.
llvm::StringRef buf(socket_id.data(), socket_id.size() + 1);
while (!buf.empty()) {
@@ -176,34 +177,35 @@ static Status writeSocketIdToPipe(Pipe &port_pipe,
port_pipe.Write(buf.data(), buf.size()))
buf = buf.drop_front(*written);
else
- return Status::FromError(written.takeError());
+ return written.takeError();
}
- return Status();
+ return llvm::Error::success();
}
-Status writeSocketIdToPipe(const char *const named_pipe_path,
- llvm::StringRef socket_id) {
+llvm::Error writeSocketIdToPipe(const char *const named_pipe_path,
+ llvm::StringRef socket_id) {
Pipe port_name_pipe;
// Wait for 10 seconds for pipe to be opened.
if (llvm::Error err = port_name_pipe.OpenAsWriter(named_pipe_path,
std::chrono::seconds{10}))
- return Status::FromError(std::move(err));
+ return err;
return writeSocketIdToPipe(port_name_pipe, socket_id.str());
}
-Status writeSocketIdToPipe(lldb::pipe_t unnamed_pipe,
- llvm::StringRef socket_id) {
+llvm::Error writeSocketIdToPipe(lldb::pipe_t unnamed_pipe,
+ llvm::StringRef socket_id) {
Pipe port_pipe{LLDB_INVALID_PIPE, unnamed_pipe};
return writeSocketIdToPipe(port_pipe, socket_id.str());
}
-void ConnectToRemote(MainLoop &mainloop,
- GDBRemoteCommunicationServerLLGS &gdb_server,
- bool reverse_connect, llvm::StringRef host_and_port,
- const char *const progname, const char *const subcommand,
- const char *const named_pipe_path, pipe_t unnamed_pipe,
- shared_fd_t connection_fd) {
+llvm::Error ConnectToRemote(MainLoop &mainloop,
+ GDBRemoteCommunicationServerLLGS &gdb_server,
+ bool reverse_connect, llvm::StringRef host_and_port,
+ const char *const progname,
+ const char *const subcommand,
+ const char *const named_pipe_path,
+ pipe_t unnamed_pipe, shared_fd_t connection_fd) {
Status error;
std::unique_ptr<Connection> connection_up;
@@ -213,11 +215,9 @@ void ConnectToRemote(MainLoop &mainloop,
#ifdef _WIN32
NativeSocket sockfd;
error = SharedSocket::GetNativeSocket(connection_fd, sockfd);
- if (error.Fail()) {
- llvm::errs() << llvm::formatv("error: GetNativeSocket failed: {0}\n",
- error.AsCString());
- exit(-1);
- }
+ if (error.Fail())
+ return llvm::createStringErrorV("GetNativeSocket failed: {0}",
+ error.AsCString());
connection_up = std::make_unique<ConnectionFileDescriptor>(
std::make_unique<TCPSocket>(sockfd, /*should_close=*/true));
#else
@@ -229,13 +229,10 @@ void ConnectToRemote(MainLoop &mainloop,
} else if (!host_and_port.empty()) {
llvm::Expected<std::string> url_exp =
LLGSArgToURL(host_and_port, reverse_connect);
- if (!url_exp) {
- llvm::errs() << llvm::formatv("error: invalid host:port or URL '{0}': "
- "{1}\n",
- host_and_port,
- llvm::toString(url_exp.takeError()));
- exit(-1);
- }
+ if (!url_exp)
+ return llvm::createStringErrorV("invalid host:port or URL '{0}': {1}",
+ host_and_port,
+ llvm::toString(url_exp.takeError()));
url = std::move(url_exp.get());
}
@@ -250,43 +247,39 @@ void ConnectToRemote(MainLoop &mainloop,
// If we have a named pipe to write the socket id back to, do that
// now.
if (named_pipe_path && named_pipe_path[0]) {
- Status error = writeSocketIdToPipe(named_pipe_path, socket_id);
- if (error.Fail())
+ llvm::Error error = writeSocketIdToPipe(named_pipe_path, socket_id);
+ if (error)
llvm::errs() << llvm::formatv(
"failed to write to the named pipe '{0}': {1}\n",
- named_pipe_path, error.AsCString());
+ named_pipe_path, llvm::fmt_consume(std::move(error)));
}
// If we have an unnamed pipe to write the socket id back to, do
// that now.
else if (unnamed_pipe != LLDB_INVALID_PIPE) {
- Status error = writeSocketIdToPipe(unnamed_pipe, socket_id);
- if (error.Fail())
+ llvm::Error error = writeSocketIdToPipe(unnamed_pipe, socket_id);
+ if (error)
llvm::errs() << llvm::formatv(
- "failed to write to the unnamed pipe: {0}\n", error);
+ "failed to write to the unnamed pipe: {0}\n",
+ llvm::fmt_consume(std::move(error)));
}
},
&error);
- if (error.Fail()) {
- llvm::errs() << llvm::formatv(
- "error: failed to connect to client at '{0}': {1}\n", url, error);
- exit(-1);
- }
- if (connection_result != eConnectionStatusSuccess) {
- llvm::errs() << llvm::formatv(
- "error: failed to connect to client at '{0}' "
- "(connection status: {1})\n",
- url, static_cast<int>(connection_result));
- exit(-1);
- }
+ if (error.Fail())
+ return llvm::createStringErrorV(
+ "failed to connect to client at '{0}': {1}", url, error);
+ if (connection_result != eConnectionStatusSuccess)
+ return llvm::createStringErrorV(
+ "failed to connect to client at '{0}' (connection status: {1})", url,
+ static_cast<int>(connection_result));
connection_up = std::move(conn_fd_up);
}
error = gdb_server.InitializeConnection(std::move(connection_up));
- if (error.Fail()) {
- llvm::errs() << llvm::formatv("failed to initialize connection\n", error);
- exit(-1);
- }
+ if (error.Fail())
+ return llvm::createStringErrorV("failed to initialize connection: {0}",
+ error);
llvm::outs() << "Connection established.\n";
+ return llvm::Error::success();
}
namespace {
@@ -466,17 +459,27 @@ int main_gdbserver(int argc, char *argv[]) {
// to launch a program, or a vAttach packet to attach to an existing process,
// unless
// explicitly asked to attach with the --attach={pid|program_name} form.
- if (!attach_target.empty())
- handle_attach(gdb_server, attach_target);
- else if (!Inputs.empty())
- handle_launch(gdb_server, Inputs);
+ if (!attach_target.empty()) {
+ if (llvm::Error err = handle_attach(gdb_server, attach_target)) {
+ llvm::errs() << "error: " << llvm::toString(std::move(err)) << "\n";
+ return 1;
+ }
+ } else if (!Inputs.empty()) {
+ if (llvm::Error err = handle_launch(gdb_server, Inputs)) {
+ llvm::errs() << "error: " << llvm::toString(std::move(err)) << "\n";
+ return 1;
+ }
+ }
// Print version info.
printf("%s-%s\n", LLGS_PROGRAM_NAME, LLGS_VERSION_STR);
- ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port,
- progname, subcommand, named_pipe_path.c_str(),
- unnamed_pipe, connection_fd);
+ if (llvm::Error err = ConnectToRemote(
+ mainloop, gdb_server, reverse_connect, host_and_port, progname,
+ subcommand, named_pipe_path.c_str(), unnamed_pipe, connection_fd)) {
+ llvm::errs() << "error: " << llvm::toString(std::move(err)) << "\n";
+ return 1;
+ }
if (!gdb_server.IsConnected()) {
fprintf(stderr, "no connection information provided, unable to run\n");
diff --git a/lldb/tools/lldb-server/lldb-server.cpp b/lldb/tools/lldb-server/lldb-server.cpp
index 744f8ceb854fb..62255c708eac0 100644
--- a/lldb/tools/lldb-server/lldb-server.cpp
+++ b/lldb/tools/lldb-server/lldb-server.cpp
@@ -12,6 +12,7 @@
#include "lldb/Version/Version.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/ManagedStatic.h"
@@ -24,14 +25,15 @@
static llvm::ManagedStatic<lldb_private::SystemLifetimeManager>
g_debugger_lifetime;
-static void display_usage(const char *progname) {
- fprintf(stderr, "Usage:\n"
- " %s v[ersion]\n"
- " %s g[dbserver] [options]\n"
- " %s p[latform] [options]\n"
- "Invoke subcommand for additional help\n",
+static int display_usage(const char *progname, int exit_code) {
+ fprintf(stderr,
+ "Usage:\n"
+ " %s v[ersion]\n"
+ " %s g[dbserver] [options]\n"
+ " %s p[latform] [options]\n"
+ "Invoke subcommand for additional help\n",
progname, progname, progname);
- exit(0);
+ return exit_code;
}
// Forward declarations of subcommand main methods.
@@ -39,44 +41,39 @@ int main_gdbserver(int argc, char *argv[]);
int main_platform(int argc, char *argv[]);
namespace llgs {
-static void initialize() {
+static void Initialize() {
if (auto e = g_debugger_lifetime->Initialize(
std::make_unique<SystemInitializerLLGS>()))
llvm::consumeError(std::move(e));
}
-static void terminate_debugger() { g_debugger_lifetime->Terminate(); }
+static void Terminate() { g_debugger_lifetime->Terminate(); }
} // namespace llgs
-// main
int main(int argc, char *argv[]) {
llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL
" and include the crash backtrace.\n");
- int option_error = 0;
const char *progname = argv[0];
- if (argc < 2) {
- display_usage(progname);
- exit(option_error);
- }
+ if (argc < 2)
+ return display_usage(progname, EXIT_SUCCESS);
switch (argv[1][0]) {
- case 'g':
- llgs::initialize();
- main_gdbserver(argc, argv);
- llgs::terminate_debugger();
- break;
- case 'p':
- llgs::initialize();
- main_platform(argc, argv);
- llgs::terminate_debugger();
- break;
+ case 'g': {
+ llgs::Initialize();
+ auto terminate = llvm::scope_exit([]() { llgs::Terminate(); });
+ return main_gdbserver(argc, argv);
+ }
+ case 'p': {
+ llgs::Initialize();
+ auto terminate = llvm::scope_exit([]() { llgs::Terminate(); });
+ return main_platform(argc, argv);
+ }
case 'v':
fprintf(stderr, "%s\n", lldb_private::GetVersion());
- break;
- default:
- display_usage(progname);
- exit(option_error);
+ return EXIT_SUCCESS;
}
+
+ return display_usage(progname, EXIT_FAILURE);
}
>From 969bebfd6cdeaee23a9d507e8a5b773e7d519c36 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Thu, 12 Mar 2026 18:39:27 -0700
Subject: [PATCH 2/2] Update test to expect a non-zero exit code
---
.../lldb-server/TestGdbserverErrorMessages.test | 8 ++++----
.../lldb-server/TestPlatformErrorMessages.test | 16 ++++++++--------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/lldb/test/Shell/lldb-server/TestGdbserverErrorMessages.test b/lldb/test/Shell/lldb-server/TestGdbserverErrorMessages.test
index b9689fb1e4673..c7e0c5cbac6dd 100644
--- a/lldb/test/Shell/lldb-server/TestGdbserverErrorMessages.test
+++ b/lldb/test/Shell/lldb-server/TestGdbserverErrorMessages.test
@@ -1,13 +1,13 @@
-RUN: %lldb-server gdbserver --fd 2>&1 | FileCheck --check-prefixes=FD1,ALL %s
+RUN: not %lldb-server gdbserver --fd 2>&1 | FileCheck --check-prefixes=FD1,ALL %s
FD1: error: --fd: missing argument
-RUN: %lldb-server gdbserver --fd three 2>&1 | FileCheck --check-prefixes=FD2,ALL %s
+RUN: not %lldb-server gdbserver --fd three 2>&1 | FileCheck --check-prefixes=FD2,ALL %s
FD2: error: invalid '--fd' argument
-RUN: %lldb-server gdbserver --bogus 2>&1 | FileCheck --check-prefixes=BOGUS,ALL %s
+RUN: not %lldb-server gdbserver --bogus 2>&1 | FileCheck --check-prefixes=BOGUS,ALL %s
BOGUS: error: unknown argument '--bogus'
-RUN: %lldb-server gdbserver 2>&1 | FileCheck --check-prefixes=CONN,ALL %s
+RUN: not %lldb-server gdbserver 2>&1 | FileCheck --check-prefixes=CONN,ALL %s
CONN: error: no connection arguments
ALL: Use '{{.*}} g[dbserver] --help' for a complete list of options.
diff --git a/lldb/test/Shell/lldb-server/TestPlatformErrorMessages.test b/lldb/test/Shell/lldb-server/TestPlatformErrorMessages.test
index 7d3b37aa5fc39..94d8d0f0bbcee 100644
--- a/lldb/test/Shell/lldb-server/TestPlatformErrorMessages.test
+++ b/lldb/test/Shell/lldb-server/TestPlatformErrorMessages.test
@@ -1,25 +1,25 @@
-RUN: %platformserver 2>&1 | FileCheck --check-prefixes=NO_LISTEN,ALL %s
+RUN: not %platformserver 2>&1 | FileCheck --check-prefixes=NO_LISTEN,ALL %s
NO_LISTEN: error: either --listen or --child-platform-fd is required
-RUN: %lldb-server platform --listen 2>&1 | FileCheck --check-prefixes=LISTEN_MISSING,ALL %s
+RUN: not %lldb-server platform --listen 2>&1 | FileCheck --check-prefixes=LISTEN_MISSING,ALL %s
LISTEN_MISSING: error: --listen: missing argument
-RUN: %lldb-server p --bogus 2>&1 | FileCheck --check-prefixes=BOGUS,ALL %s
+RUN: not %lldb-server p --bogus 2>&1 | FileCheck --check-prefixes=BOGUS,ALL %s
BOGUS: error: unknown argument '--bogus'
-RUN: %platformserver --gdbserver-port 2>&1 | FileCheck --check-prefixes=GDBPORT_MISSING,ALL %s
+RUN: not %platformserver --gdbserver-port 2>&1 | FileCheck --check-prefixes=GDBPORT_MISSING,ALL %s
GDBPORT_MISSING: error: --gdbserver-port: missing argument
-RUN: %platformserver --gdbserver-port notanumber --listen :1234 2>&1 | FileCheck --check-prefixes=GDBPORT_INVALID %s
+RUN: not %platformserver --gdbserver-port notanumber --listen :1234 2>&1 | FileCheck --check-prefixes=GDBPORT_INVALID %s
GDBPORT_INVALID: error: invalid --gdbserver-port value
-RUN: %platformserver --socket-file 2>&1 | FileCheck --check-prefixes=SOCKETFILE_MISSING,ALL %s
+RUN: not %platformserver --socket-file 2>&1 | FileCheck --check-prefixes=SOCKETFILE_MISSING,ALL %s
SOCKETFILE_MISSING: error: --socket-file: missing argument
-RUN: %platformserver --log-file 2>&1 | FileCheck --check-prefixes=LOGFILE_MISSING,ALL %s
+RUN: not %platformserver --log-file 2>&1 | FileCheck --check-prefixes=LOGFILE_MISSING,ALL %s
LOGFILE_MISSING: error: --log-file: missing argument
-RUN: %platformserver --log-channels 2>&1 | FileCheck --check-prefixes=LOGCHANNELS_MISSING,ALL %s
+RUN: not %platformserver --log-channels 2>&1 | FileCheck --check-prefixes=LOGCHANNELS_MISSING,ALL %s
LOGCHANNELS_MISSING: error: --log-channels: missing argument
ALL: Use 'lldb-server{{(\.exe)?}} {{p|platform}} --help' for a complete list of options.
More information about the lldb-commits
mailing list