diff --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h index 4e19d15b0674..4538fd206251 100644 --- a/lldb/include/lldb/Host/Host.h +++ b/lldb/include/lldb/Host/Host.h @@ -195,65 +195,117 @@ public: 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 &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 &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 &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 &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 fe9d4f7982bb..1ba7516f0102 100644 --- a/lldb/include/lldb/Target/Platform.h +++ b/lldb/include/lldb/Target/Platform.h @@ -679,6 +679,9 @@ public: // 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 &timeout); virtual lldb_private::Status RunShellCommand( @@ -690,6 +693,9 @@ public: // 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 &timeout); virtual void SetLocalCacheDirectory(const char *local); diff --git a/lldb/include/lldb/Target/RemoteAwarePlatform.h b/lldb/include/lldb/Target/RemoteAwarePlatform.h index de13b18f30d8..ac2d7ca943f1 100644 --- a/lldb/include/lldb/Target/RemoteAwarePlatform.h +++ b/lldb/include/lldb/Target/RemoteAwarePlatform.h @@ -71,11 +71,13 @@ public: 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 &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 &timeout) override; const char *GetHostname() override; diff --git a/lldb/source/API/SBPlatform.cpp b/lldb/source/API/SBPlatform.cpp index 9a0b47ce8033..567744e36829 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 9bdb3de388be..0bca26d89e18 100644 --- a/lldb/source/Commands/CommandObjectPlatform.cpp +++ b/lldb/source/Commands/CommandObjectPlatform.cpp @@ -1707,9 +1707,9 @@ public: 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 510f9c7696d1..68456e199cd0 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -389,39 +389,43 @@ MonitorShellCommand(std::shared_ptr 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 &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 &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 &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 &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 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(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 f52b78f257ca..1aa6b5376b6b 100644 --- a/lldb/source/Host/macosx/objcxx/Host.mm +++ b/lldb/source/Host/macosx/objcxx/Host.mm @@ -1530,10 +1530,11 @@ Status Host::ShellExpandArguments(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 63cef827a91c..d60b551765d9 100644 --- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -456,8 +456,9 @@ xcrun(const std::string &sdk, llvm::ArrayRef arguments, // 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 d5704eed10ec..1610f52b3b27 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 c49bd6961822..fa1dbc0eeda9 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 ea2a22b15434..fdc52d29d2a3 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 a4e6e69aba18..1f69ef5f3f6c 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 &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 diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h index 1fba9f5beb11..5332f43c3c7a 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -141,7 +141,10 @@ public: 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 &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 738e4013b615..a15dc13d7076 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 &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 d04f6370bb6a..6b5ed9958343 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -403,6 +403,8 @@ public: // 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 &timeout); llvm::ErrorOr 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 786ced652536..1beec8bf8715 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 5c1aaac886d6..8173f611c1c8 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 c85c7d2b578e..c47ef47b0f60 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 &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 &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 89b946ba7516..3dc3cbbd4463 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 &timeout) { + std::string *separated_error_output, const Timeout &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 &timeout) { + std::string *separated_error_output, const Timeout &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,