[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