[Lldb-commits] [lldb] 1c228a0 - [lldb] Have Host::RunShellCommand ret stderr & stdout seperately (#184548)

via lldb-commits lldb-commits at lists.llvm.org
Tue Mar 10 13:48:59 PDT 2026


Author: Jason Molenda
Date: 2026-03-10T13:48:55-07:00
New Revision: 1c228a0533ae76bace6a3e3fe1c22fc2858dbf75

URL: https://github.com/llvm/llvm-project/commit/1c228a0533ae76bace6a3e3fe1c22fc2858dbf75
DIFF: https://github.com/llvm/llvm-project/commit/1c228a0533ae76bace6a3e3fe1c22fc2858dbf75.diff

LOG: [lldb] Have Host::RunShellCommand ret stderr & stdout seperately (#184548)

Host::RunShellCommand takes a std::string *command_output argument and a
bool hide_stderr=false defaulted argument. If the shell command returns
stderr and stdout text, it is intermixed in the same command_output,
unless hide_stderr=true.

In SymbolLocatorDebugSymbols::DownloadObjectAndSymbolFile we call an
external program to find a binary and dSYM by uuid, and the external
program returns a plist (xml) output. In some cases, it printed a
(harmless) warning message to stderr, and then a complete plist output
to stdout. We attempt to parse the combination of these two streams, and
the parse fails - we don't get the output.

This patch removes hide_stderr and instead adds a `std::string
*separated_error_output` argument. If `separated_error_output` is
nullptr, output and error texts are returned combined in the
`command_output` argument. If a std::string object address is provided
for `separated_error_output`, then standard error output is separated
into this string. A caller which wants the old `hide_stderr=true`
behavior should pass a throwaway std::string object to `RunShellCommand`
and ignore it.

rdar://168621579

Added: 
    

Modified: 
    lldb/include/lldb/Host/Host.h
    lldb/include/lldb/Target/Platform.h
    lldb/include/lldb/Target/RemoteAwarePlatform.h
    lldb/source/API/SBPlatform.cpp
    lldb/source/Commands/CommandObjectPlatform.cpp
    lldb/source/Host/common/Host.cpp
    lldb/source/Host/macosx/objcxx/Host.mm
    lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
    lldb/source/Host/windows/Host.cpp
    lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
    lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
    lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
    lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
    lldb/source/Plugins/SymbolLocator/DebugSymbols/SymbolLocatorDebugSymbols.cpp
    lldb/source/Target/Platform.cpp
    lldb/source/Target/RemoteAwarePlatform.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h
index 4e19d15b06743..4538fd206251b 100644
--- a/lldb/include/lldb/Host/Host.h
+++ b/lldb/include/lldb/Host/Host.h
@@ -195,65 +195,117 @@ class Host {
   static Status ShellExpandArguments(ProcessLaunchInfo &launch_info);
 
   /// Run a shell command.
-  /// \arg command  shouldn't be empty
-  /// \arg working_dir Pass empty FileSpec to use the current working directory
-  /// \arg status_ptr  Pass NULL if you don't want the process exit status
-  /// \arg signo_ptr   Pass NULL if you don't want the signal that caused the
-  ///                  process to exit
-  /// \arg command_output  Pass NULL if you don't want the command output
-  /// \arg hide_stderr if this is false, redirect stderr to stdout
+  /// \param[in] command
+  ///            Command to execute, should not be empty.
+  /// \param[in] working_dir
+  ///            Pass empty FileSpec to use the current working directory
+  /// \param[out] status_ptr
+  ///            Pass nullptr if you don't want the process exit status
+  /// \param[out] signo_ptr
+  ///            Pass nullptr if you don't want the signal that caused the
+  ///            process to exit
+  /// \param[out] command_output
+  ///            Pass nullptr if you don't want the command output
+  /// \param[out] separated_error_output
+  ///            If a std::string is specified, error output is routed
+  ///            into a separate string.  If nullptr is provided,
+  ///            command output and error text will be returned combined
+  ///            in \a command_output.
+  /// \param[in] timeout
+  ///            Timeout duration to enforce
+  /// \param[in] run_in_shell
+  ///            Run in a subshell, with glob expansion of args
   static Status RunShellCommand(llvm::StringRef command,
                                 const FileSpec &working_dir, int *status_ptr,
                                 int *signo_ptr, std::string *command_output,
+                                std::string *error_output,
                                 const Timeout<std::micro> &timeout,
-                                bool run_in_shell = true,
-                                bool hide_stderr = false);
+                                bool run_in_shell = true);
 
   /// Run a shell command.
-  /// \arg shell  Pass an empty string if you want to use the default shell
-  /// interpreter \arg command \arg working_dir  Pass empty FileSpec to use the
-  /// current working directory \arg status_ptr   Pass NULL if you don't want
-  /// the process exit status \arg signo_ptr    Pass NULL if you don't want the
-  /// signal that caused
-  ///                   the process to exit
-  /// \arg command_output  Pass NULL if you don't want the command output
-  /// \arg hide_stderr  If this is \b false, redirect stderr to stdout
+  /// \param[in] shell
+  ///            Pass an empty string to use the default shell
+  /// \param[in] command
+  ///            Command to execute, should not be empty.
+  /// \param[in] working_dir
+  ///            Pass empty FileSpec to use the current working directory
+  /// \param[out] status_ptr
+  ///            Pass nullptr if you don't want the process exit status
+  /// \param[out] signo_ptr
+  ///            Pass nullptr if you don't want the signal that caused the
+  ///            process to exit
+  /// \param[out] command_output
+  ///            Pass nullptr if you don't want the command output
+  /// \param[out] separated_error_output
+  ///            If a std::string is specified, error output is routed
+  ///            into a separate string.  If nullptr is provided,
+  ///            command output and error text will be returned combined
+  /// \param[in] timeout
+  ///            Timeout duration to enforce
+  /// \param[in] run_in_shell
+  ///            Run in a subshell, with glob expansion of args
   static Status RunShellCommand(llvm::StringRef shell, llvm::StringRef command,
                                 const FileSpec &working_dir, int *status_ptr,
                                 int *signo_ptr, std::string *command_output,
+                                std::string *separated_error_output,
                                 const Timeout<std::micro> &timeout,
-                                bool run_in_shell = true,
-                                bool hide_stderr = false);
+                                bool run_in_shell = true);
 
   /// Run a shell command.
-  /// \arg working_dir Pass empty FileSpec to use the current working directory
-  /// \arg status_ptr  Pass NULL if you don't want the process exit status
-  /// \arg signo_ptr   Pass NULL if you don't want the signal that caused the
-  ///                  process to exit
-  /// \arg command_output  Pass NULL if you don't want the command output
-  /// \arg hide_stderr if this is false, redirect stderr to stdout
+  /// \param[in] args
+  ///            Command to execute
+  /// \param[in] working_dir
+  ///            Pass empty FileSpec to use the current working directory
+  /// \param[out] status_ptr
+  ///            Pass nullptr if you don't want the process exit status
+  /// \param[out] signo_ptr
+  ///            Pass nullptr if you don't want the signal that caused the
+  ///            process to exit
+  /// \param[out] command_output
+  ///            Pass nullptr if you don't want the command output
+  /// \param[out] separated_error_output
+  ///            If a std::string is specified, error output is routed
+  ///            into a separate string.  If nullptr is provided,
+  ///            command output and error text will be returned combined
+  /// \param[in] timeout
+  ///            Timeout duration to enforce
+  /// \param[in] run_in_shell
+  ///            Run in a subshell, with glob expansion of args
   static Status RunShellCommand(const Args &args, const FileSpec &working_dir,
                                 int *status_ptr, int *signo_ptr,
                                 std::string *command_output,
+                                std::string *separated_error_output,
                                 const Timeout<std::micro> &timeout,
-                                bool run_in_shell = true,
-                                bool hide_stderr = false);
+                                bool run_in_shell = true);
 
   /// Run a shell command.
-  /// \arg shell            Pass an empty string if you want to use the default
-  /// shell interpreter \arg command \arg working_dir Pass empty FileSpec to use
-  /// the current working directory \arg status_ptr    Pass NULL if you don't
-  /// want the process exit status \arg signo_ptr     Pass NULL if you don't
-  /// want the signal that caused the
-  ///               process to exit
-  /// \arg command_output  Pass NULL if you don't want the command output
-  /// \arg hide_stderr If this is \b false, redirect stderr to stdout
+  /// \param[in] shell
+  ///            Pass an empty string to use the default shell
+  /// \param[in] args
+  ///            Command to execute
+  /// \param[in] working_dir
+  ///            Pass empty FileSpec to use the current working directory
+  /// \param[out] status_ptr
+  ///            Pass nullptr if you don't want the process exit status
+  /// \param[out] signo_ptr
+  ///            Pass nullptr if you don't want the signal that caused the
+  ///            process to exit
+  /// \param[out] command_output
+  ///            Pass nullptr if you don't want the command output
+  /// \param[out] separated_error_output
+  ///            If a std::string is specified, error output is routed
+  ///            into a separate string.  If nullptr is provided,
+  ///            command output and error text will be returned combined
+  /// \param[in] timeout
+  ///            Timeout duration to enforce
+  /// \param[in] run_in_shell
+  ///            Run in a subshell, with glob expansion of args
   static Status RunShellCommand(llvm::StringRef shell, const Args &args,
                                 const FileSpec &working_dir, int *status_ptr,
                                 int *signo_ptr, std::string *command_output,
+                                std::string *separated_error_output,
                                 const Timeout<std::micro> &timeout,
-                                bool run_in_shell = true,
-                                bool hide_stderr = false);
+                                bool run_in_shell = true);
 
   static llvm::Error OpenFileInExternalEditor(llvm::StringRef editor,
                                               const FileSpec &file_spec,

diff  --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h
index fe9d4f7982bbf..1ba7516f0102c 100644
--- a/lldb/include/lldb/Target/Platform.h
+++ b/lldb/include/lldb/Target/Platform.h
@@ -679,6 +679,9 @@ class Platform : public PluginInterface {
                        // the process to exit
       std::string
           *command_output, // Pass nullptr if you don't want the command output
+      std::string
+          *separated_error_output, // Pass nullptr to have error and command
+                                   // output combined in command_output.
       const Timeout<std::micro> &timeout);
 
   virtual lldb_private::Status RunShellCommand(
@@ -690,6 +693,9 @@ class Platform : public PluginInterface {
                        // the process to exit
       std::string
           *command_output, // Pass nullptr if you don't want the command output
+      std::string
+          *separated_error_output, // Pass nullptr to have error and command
+                                   // output combined in command_output.
       const Timeout<std::micro> &timeout);
 
   virtual void SetLocalCacheDirectory(const char *local);

diff  --git a/lldb/include/lldb/Target/RemoteAwarePlatform.h b/lldb/include/lldb/Target/RemoteAwarePlatform.h
index de13b18f30d85..ac2d7ca943f11 100644
--- a/lldb/include/lldb/Target/RemoteAwarePlatform.h
+++ b/lldb/include/lldb/Target/RemoteAwarePlatform.h
@@ -71,11 +71,13 @@ class RemoteAwarePlatform : public Platform {
   Status RunShellCommand(llvm::StringRef command, const FileSpec &working_dir,
                          int *status_ptr, int *signo_ptr,
                          std::string *command_output,
+                         std::string *separated_error_output,
                          const Timeout<std::micro> &timeout) override;
 
   Status RunShellCommand(llvm::StringRef interpreter, llvm::StringRef command,
                          const FileSpec &working_dir, int *status_ptr,
                          int *signo_ptr, std::string *command_output,
+                         std::string *separated_error_output,
                          const Timeout<std::micro> &timeout) override;
 
   const char *GetHostname() override;

diff  --git a/lldb/source/API/SBPlatform.cpp b/lldb/source/API/SBPlatform.cpp
index 9a0b47ce80336..567744e36829f 100644
--- a/lldb/source/API/SBPlatform.cpp
+++ b/lldb/source/API/SBPlatform.cpp
@@ -564,7 +564,7 @@ SBError SBPlatform::Run(SBPlatformShellCommand &shell_command) {
             FileSpec(shell_command.GetWorkingDirectory()),
             &shell_command.m_opaque_ptr->m_status,
             &shell_command.m_opaque_ptr->m_signo,
-            &shell_command.m_opaque_ptr->m_output,
+            &shell_command.m_opaque_ptr->m_output, nullptr,
             shell_command.m_opaque_ptr->m_timeout);
       });
 }

diff  --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp
index 9bdb3de388bea..0bca26d89e18d 100644
--- a/lldb/source/Commands/CommandObjectPlatform.cpp
+++ b/lldb/source/Commands/CommandObjectPlatform.cpp
@@ -1707,9 +1707,9 @@ class CommandObjectPlatformShell : public CommandObjectRaw {
       std::string output;
       int status = -1;
       int signo = -1;
-      error = (platform_sp->RunShellCommand(m_options.m_shell_interpreter, cmd,
-                                            working_dir, &status, &signo,
-                                            &output, m_options.m_timeout));
+      error = (platform_sp->RunShellCommand(
+          m_options.m_shell_interpreter, cmd, working_dir, &status, &signo,
+          &output, nullptr, m_options.m_timeout));
       if (!output.empty())
         result.GetOutputStream().PutCString(output);
       if (status > 0) {

diff  --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp
index 510f9c7696d12..68456e199cd04 100644
--- a/lldb/source/Host/common/Host.cpp
+++ b/lldb/source/Host/common/Host.cpp
@@ -389,39 +389,43 @@ MonitorShellCommand(std::shared_ptr<ShellInfo> shell_info, lldb::pid_t pid,
 Status Host::RunShellCommand(llvm::StringRef command,
                              const FileSpec &working_dir, int *status_ptr,
                              int *signo_ptr, std::string *command_output_ptr,
+                             std::string *separated_error_output,
                              const Timeout<std::micro> &timeout,
-                             bool run_in_shell, bool hide_stderr) {
+                             bool run_in_shell) {
   return RunShellCommand(llvm::StringRef(), Args(command), working_dir,
-                         status_ptr, signo_ptr, command_output_ptr, timeout,
-                         run_in_shell, hide_stderr);
+                         status_ptr, signo_ptr, command_output_ptr,
+                         separated_error_output, timeout, run_in_shell);
 }
 
 Status Host::RunShellCommand(llvm::StringRef shell_path,
                              llvm::StringRef command,
                              const FileSpec &working_dir, int *status_ptr,
                              int *signo_ptr, std::string *command_output_ptr,
+                             std::string *separated_error_output,
                              const Timeout<std::micro> &timeout,
-                             bool run_in_shell, bool hide_stderr) {
+                             bool run_in_shell) {
   return RunShellCommand(shell_path, Args(command), working_dir, status_ptr,
-                         signo_ptr, command_output_ptr, timeout, run_in_shell,
-                         hide_stderr);
+                         signo_ptr, command_output_ptr, separated_error_output,
+                         timeout, run_in_shell);
 }
 
 Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir,
                              int *status_ptr, int *signo_ptr,
                              std::string *command_output_ptr,
+                             std::string *separated_error_output,
                              const Timeout<std::micro> &timeout,
-                             bool run_in_shell, bool hide_stderr) {
+                             bool run_in_shell) {
   return RunShellCommand(llvm::StringRef(), args, working_dir, status_ptr,
-                         signo_ptr, command_output_ptr, timeout, run_in_shell,
-                         hide_stderr);
+                         signo_ptr, command_output_ptr, separated_error_output,
+                         timeout, run_in_shell);
 }
 
 Status Host::RunShellCommand(llvm::StringRef shell_path, const Args &args,
                              const FileSpec &working_dir, int *status_ptr,
                              int *signo_ptr, std::string *command_output_ptr,
+                             std::string *separated_error_output,
                              const Timeout<std::micro> &timeout,
-                             bool run_in_shell, bool hide_stderr) {
+                             bool run_in_shell) {
   Status error;
   ProcessLaunchInfo launch_info;
   launch_info.SetArchitecture(HostInfo::GetArchitecture());
@@ -448,9 +452,10 @@ Status Host::RunShellCommand(llvm::StringRef shell_path, const Args &args,
   if (working_dir)
     launch_info.SetWorkingDirectory(working_dir);
   llvm::SmallString<64> output_file_path;
+  llvm::SmallString<64> error_file_path;
 
   if (command_output_ptr) {
-    // Create a temporary file to get the stdout/stderr and redirect the output
+    // Create a temporary file to get the stdout and redirect the output
     // of the command into this file. We will later read this file if all goes
     // well and fill the data into "command_output_ptr"
     if (FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir()) {
@@ -463,7 +468,22 @@ Status Host::RunShellCommand(llvm::StringRef shell_path, const Args &args,
     }
   }
 
+  if (separated_error_output) {
+    // Create a temporary file to get the stderr and redirect the output
+    // of the command into this file. We will later read this file if all goes
+    // well and fill the data into "separated_error_output".
+    if (FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir()) {
+      tmpdir_file_spec.AppendPathComponent("lldb-shell-error.%%%%%%");
+      llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(),
+                                      error_file_path);
+    } else {
+      llvm::sys::fs::createTemporaryFile("lldb-shell-error.%%%%%%", "",
+                                         error_file_path);
+    }
+  }
+
   FileSpec output_file_spec(output_file_path.str());
+  FileSpec error_file_spec(error_file_path.str());
   // Set up file descriptors.
   launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false);
   if (output_file_spec)
@@ -472,10 +492,11 @@ Status Host::RunShellCommand(llvm::StringRef shell_path, const Args &args,
   else
     launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true);
 
-  if (output_file_spec && !hide_stderr)
-    launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO);
+  if (error_file_spec)
+    launch_info.AppendOpenFileAction(STDERR_FILENO, error_file_spec, false,
+                                     true);
   else
-    launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true);
+    launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO);
 
   std::shared_ptr<ShellInfo> shell_info_sp(new ShellInfo());
   launch_info.SetMonitorProcessCallback(
@@ -524,10 +545,33 @@ Status Host::RunShellCommand(llvm::StringRef shell_path, const Args &args,
           }
         }
       }
+      if (separated_error_output) {
+        separated_error_output->clear();
+        uint64_t file_size =
+            FileSystem::Instance().GetByteSize(error_file_spec);
+        if (file_size > 0) {
+          if (file_size > separated_error_output->max_size()) {
+            error = Status::FromErrorStringWithFormat(
+                "shell command error output is too large to fit into a "
+                "std::string");
+          } else {
+            WritableDataBufferSP Buffer =
+                FileSystem::Instance().CreateWritableDataBuffer(
+                    error_file_spec);
+            if (error.Success())
+              separated_error_output->assign(
+                  reinterpret_cast<char *>(Buffer->GetBytes()),
+                  Buffer->GetByteSize());
+          }
+        }
+      }
     }
   }
 
-  llvm::sys::fs::remove(output_file_spec.GetPath());
+  if (output_file_spec)
+    llvm::sys::fs::remove(output_file_spec.GetPath());
+  if (error_file_spec)
+    llvm::sys::fs::remove(error_file_spec.GetPath());
   return error;
 }
 

diff  --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm
index f52b78f257ca8..1aa6b5376b6b1 100644
--- a/lldb/source/Host/macosx/objcxx/Host.mm
+++ b/lldb/source/Host/macosx/objcxx/Host.mm
@@ -1530,10 +1530,11 @@ static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) {
       }
     }
     bool run_in_shell = true;
-    bool hide_stderr = true;
+    std::string error_output; // Pass stderr string arg so it is not mixed with
+                              // stdout.
     Status e =
         RunShellCommand(expand_command, cwd, &status, nullptr, &output,
-                        std::chrono::seconds(10), run_in_shell, hide_stderr);
+                        &error_output, std::chrono::seconds(10), run_in_shell);
 
     if (e.Fail())
       return e;

diff  --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
index 63cef827a91c3..d60b551765d95 100644
--- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
+++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
@@ -456,8 +456,9 @@ static bool ResolveAndVerifyCandidateSupportDir(FileSpec &path) {
   // xcrun can take surprisingly long to build up its database.
   auto timeout = std::chrono::seconds(60);
   bool run_in_shell = false;
-  lldb_private::Status error = Host::RunShellCommand(
-      args, FileSpec(), &status, &signo, &output_str, timeout, run_in_shell);
+  lldb_private::Status error =
+      Host::RunShellCommand(args, FileSpec(), &status, &signo, &output_str,
+                            nullptr, timeout, run_in_shell);
 
   // Check that xcrun returned something useful.
   if (error.Fail()) {

diff  --git a/lldb/source/Host/windows/Host.cpp b/lldb/source/Host/windows/Host.cpp
index d5704eed10ecb..1610f52b3b27d 100644
--- a/lldb/source/Host/windows/Host.cpp
+++ b/lldb/source/Host/windows/Host.cpp
@@ -231,9 +231,9 @@ Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
     int status;
     std::string output;
     std::string command = expand_command.GetString().str();
-    Status e =
-        RunShellCommand(command.c_str(), launch_info.GetWorkingDirectory(),
-                        &status, nullptr, &output, std::chrono::seconds(10));
+    Status e = RunShellCommand(
+        command.c_str(), launch_info.GetWorkingDirectory(), &status, nullptr,
+        &output, nullptr, std::chrono::seconds(10));
 
     if (e.Fail())
       return e;

diff  --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
index c49bd69618227..fa1dbc0eeda98 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
@@ -683,7 +683,7 @@ static FileSpec GetXcodeSelectPath() {
       Status status =
           Host::RunShellCommand("/usr/bin/xcode-select --print-path",
                                 FileSpec(), // current working directory
-                                &exit_status, &signo, &command_output,
+                                &exit_status, &signo, &command_output, nullptr,
                                 std::chrono::seconds(2), // short timeout
                                 false);                  // don't run in a shell
       if (status.Success() && exit_status == 0 && !command_output.empty()) {

diff  --git a/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp b/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
index ea2a22b154345..fdc52d29d2a33 100644
--- a/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
+++ b/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
@@ -84,7 +84,7 @@ static uint32_t chown_file(Platform *platform, const char *path,
   command.Printf("%s", path);
   int status;
   platform->RunShellCommand(command.GetData(), FileSpec(), &status, nullptr,
-                            nullptr, std::chrono::seconds(10));
+                            nullptr, nullptr, std::chrono::seconds(10));
   return status;
 }
 
@@ -109,7 +109,7 @@ PlatformPOSIX::PutFile(const lldb_private::FileSpec &source,
     command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
     int status;
     RunShellCommand(command.GetData(), FileSpec(), &status, nullptr, nullptr,
-                    std::chrono::seconds(10));
+                    nullptr, std::chrono::seconds(10));
     if (status != 0)
       return Status::FromErrorString("unable to perform copy");
     if (uid == UINT32_MAX && gid == UINT32_MAX)
@@ -140,7 +140,7 @@ PlatformPOSIX::PutFile(const lldb_private::FileSpec &source,
       LLDB_LOGF(log, "[PutFile] Running command: %s\n", command.GetData());
       int retcode;
       Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
-                            nullptr, std::chrono::minutes(1));
+                            nullptr, nullptr, std::chrono::minutes(1));
       if (retcode == 0) {
         // Don't chown a local file for a remote system
         //                if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
@@ -178,7 +178,7 @@ lldb_private::Status PlatformPOSIX::GetFile(
     cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
     int status;
     RunShellCommand(cp_command.GetData(), FileSpec(), &status, nullptr, nullptr,
-                    std::chrono::seconds(10));
+                    nullptr, std::chrono::seconds(10));
     if (status != 0)
       return Status::FromErrorString("unable to perform copy");
     return Status();
@@ -199,7 +199,7 @@ lldb_private::Status PlatformPOSIX::GetFile(
       LLDB_LOGF(log, "[GetFile] Running command: %s\n", command.GetData());
       int retcode;
       Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
-                            nullptr, std::chrono::minutes(1));
+                            nullptr, nullptr, std::chrono::minutes(1));
       if (retcode == 0)
         return Status();
       // If we are here, rsync has failed - let's try the slow way before

diff  --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
index a4e6e69aba18b..1f69ef5f3f6ca 100644
--- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
+++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
@@ -684,12 +684,15 @@ Status PlatformRemoteGDBServer::RunShellCommand(
     int *signo_ptr,  // Pass NULL if you don't want the signal that caused the
                      // process to exit
     std::string
-        *command_output, // Pass NULL if you don't want the command output
+        *command_output, // Pass nullptr if you don't want the command output
+    std::string *separated_error_output, // Pass nullptr if you don't want the
+                                         // error output
     const Timeout<std::micro> &timeout) {
   if (!IsConnected())
     return Status::FromErrorStringWithFormat("Not connected.");
   return m_gdb_client_up->RunShellCommand(command, working_dir, status_ptr,
-                                          signo_ptr, command_output, timeout);
+                                          signo_ptr, command_output,
+                                          separated_error_output, timeout);
 }
 
 llvm::ErrorOr<llvm::MD5::MD5Result>

diff  --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
index 1fba9f5beb115..5332f43c3c7ad 100644
--- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
+++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
@@ -141,7 +141,10 @@ class PlatformRemoteGDBServer : public Platform, private UserIDResolver {
       int *signo_ptr,  // Pass NULL if you don't want the signal that caused the
                        // process to exit
       std::string
-          *command_output, // Pass NULL if you don't want the command output
+          *command_output, // Pass nullptr if you don't want the command output
+      std::string
+          *separated_error_outputerror_output, // Pass nullptr if you don't want
+                                               // the command error output
       const lldb_private::Timeout<std::micro> &timeout) override;
 
   void CalculateTrapHandlerSymbolNames() override;

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 738e4013b6154..a15dc13d7076e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -2983,7 +2983,9 @@ lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand(
     int *signo_ptr,  // Pass NULL if you don't want the signal that caused the
                      // process to exit
     std::string
-        *command_output, // Pass NULL if you don't want the command output
+        *command_output, // Pass nullptr if you don't want the command output
+    std::string *separated_error_output, // Pass nullptr if you don't want the
+                                         // command error output
     const Timeout<std::micro> &timeout) {
   lldb_private::StreamString stream;
   stream.PutCString("qPlatform_shell:");

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index d04f6370bb6ae..6b5ed99583439 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -403,6 +403,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
                        // the process to exit
       std::string
           *command_output, // Pass nullptr if you don't want the command output
+      std::string *separated_error_output, // Pass nullptr if you don't want the
+                                           // command error output
       const Timeout<std::micro> &timeout);
 
   llvm::ErrorOr<llvm::MD5::MD5Result> CalculateMD5(const FileSpec &file_spec);

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
index 786ced6525364..1beec8bf87155 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -752,7 +752,7 @@ GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell(
       FileSystem::Instance().Resolve(working_spec);
       Status err =
           Host::RunShellCommand(path.c_str(), working_spec, &status, &signo,
-                                &output, std::chrono::seconds(10));
+                                &output, nullptr, std::chrono::seconds(10));
       StreamGDBRemote response;
       if (err.Fail()) {
         response.PutCString("F,");

diff  --git a/lldb/source/Plugins/SymbolLocator/DebugSymbols/SymbolLocatorDebugSymbols.cpp b/lldb/source/Plugins/SymbolLocator/DebugSymbols/SymbolLocatorDebugSymbols.cpp
index 5c1aaac886d6c..8173f611c1c84 100644
--- a/lldb/source/Plugins/SymbolLocator/DebugSymbols/SymbolLocatorDebugSymbols.cpp
+++ b/lldb/source/Plugins/SymbolLocator/DebugSymbols/SymbolLocatorDebugSymbols.cpp
@@ -1095,20 +1095,24 @@ bool SymbolLocatorDebugSymbols::DownloadObjectAndSymbolFile(
   int exit_status = -1;
   int signo = -1;
   std::string command_output;
+  std::string error_output;
   error = Host::RunShellCommand(
       command.GetData(),
       FileSpec(),      // current working directory
       &exit_status,    // Exit status
       &signo,          // Signal int *
       &command_output, // Command output
+      &error_output,   // Command error output
       std::chrono::seconds(
           640), // Large timeout to allow for long dsym download times
       false);   // Don't run in a shell (we don't need shell expansion)
 
   if (error.Fail() || exit_status != 0 || command_output.empty()) {
-    LLDB_LOGF(log, "'%s' failed (exit status: %d, error: '%s', output: '%s')",
+    LLDB_LOGF(log,
+              "'%s' failed (exit status: %d, error: '%s', stdout: '%s', "
+              "stderr: '%s')",
               command.GetData(), exit_status, error.AsCString(),
-              command_output.c_str());
+              command_output.c_str(), error_output.c_str());
     return false;
   }
 
@@ -1123,6 +1127,7 @@ bool SymbolLocatorDebugSymbols::DownloadObjectAndSymbolFile(
   if (!plist.get()) {
     LLDB_LOGF(log, "'%s' failed: output is not a valid plist",
               command.GetData());
+    LLDB_LOGF(log, "Response:\n%s\n", command_output.c_str());
     return false;
   }
 

diff  --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp
index c85c7d2b578ef..c47ef47b0f60c 100644
--- a/lldb/source/Target/Platform.cpp
+++ b/lldb/source/Target/Platform.cpp
@@ -1241,9 +1241,12 @@ lldb_private::Status Platform::RunShellCommand(
                     // process to exit
     std::string
         *command_output, // Pass nullptr if you don't want the command output
+    std::string *separated_error_output, // Pass nullptr if you don't want the
+                                         // command error output
     const Timeout<std::micro> &timeout) {
   return RunShellCommand(llvm::StringRef(), command, working_dir, status_ptr,
-                         signo_ptr, command_output, timeout);
+                         signo_ptr, command_output, separated_error_output,
+                         timeout);
 }
 
 lldb_private::Status Platform::RunShellCommand(
@@ -1257,10 +1260,13 @@ lldb_private::Status Platform::RunShellCommand(
                     // process to exit
     std::string
         *command_output, // Pass nullptr if you don't want the command output
+    std::string *separated_error_output, // Pass nullptr if you don't want the
+                                         // command error output
     const Timeout<std::micro> &timeout) {
   if (IsHost())
     return Host::RunShellCommand(shell, command, working_dir, status_ptr,
-                                 signo_ptr, command_output, timeout);
+                                 signo_ptr, command_output,
+                                 separated_error_output, timeout);
   return Status::FromErrorString(
       "unable to run a remote command without a platform");
 }

diff  --git a/lldb/source/Target/RemoteAwarePlatform.cpp b/lldb/source/Target/RemoteAwarePlatform.cpp
index 89b946ba75162..3dc3cbbd4463b 100644
--- a/lldb/source/Target/RemoteAwarePlatform.cpp
+++ b/lldb/source/Target/RemoteAwarePlatform.cpp
@@ -55,21 +55,23 @@ Status RemoteAwarePlatform::ResolveExecutable(const ModuleSpec &module_spec,
 Status RemoteAwarePlatform::RunShellCommand(
     llvm::StringRef command, const FileSpec &working_dir, int *status_ptr,
     int *signo_ptr, std::string *command_output,
-    const Timeout<std::micro> &timeout) {
+    std::string *separated_error_output, const Timeout<std::micro> &timeout) {
   return RunShellCommand(llvm::StringRef(), command, working_dir, status_ptr,
-                         signo_ptr, command_output, timeout);
+                         signo_ptr, command_output, separated_error_output,
+                         timeout);
 }
 
 Status RemoteAwarePlatform::RunShellCommand(
     llvm::StringRef shell, llvm::StringRef command, const FileSpec &working_dir,
     int *status_ptr, int *signo_ptr, std::string *command_output,
-    const Timeout<std::micro> &timeout) {
+    std::string *separated_error_output, const Timeout<std::micro> &timeout) {
   if (m_remote_platform_sp)
-    return m_remote_platform_sp->RunShellCommand(shell, command, working_dir,
-                                                 status_ptr, signo_ptr,
-                                                 command_output, timeout);
+    return m_remote_platform_sp->RunShellCommand(
+        shell, command, working_dir, status_ptr, signo_ptr, command_output,
+        separated_error_output, timeout);
   return Platform::RunShellCommand(shell, command, working_dir, status_ptr,
-                                   signo_ptr, command_output, timeout);
+                                   signo_ptr, command_output,
+                                   separated_error_output, timeout);
 }
 
 Status RemoteAwarePlatform::MakeDirectory(const FileSpec &file_spec,


        


More information about the lldb-commits mailing list