https://github.com/jasonmolenda created https://github.com/llvm/llvm-project/pull/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 &error_output argument, and callers can choose to use/print/return error_output, or ignore it. This keeps command_output the clean stdout from the shell command. rdar://168621579 >From 4d9ef5d9d945f8533d7d70eb02583406961d09f1 Mon Sep 17 00:00:00 2001 From: Jason Molenda <[email protected]> Date: Tue, 3 Mar 2026 21:16:03 -0800 Subject: [PATCH] [lldb] Have Host::RunShellCommand ret stderr & stdout seperately 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 &error_output argument, and callers can choose to use/print/return error_output, or ignore it. This keeps command_output the clean stdout from the shell command. rdar://168621579 --- lldb/include/lldb/Host/Host.h | 117 ++++++++++++------ lldb/include/lldb/Target/Platform.h | 4 + .../include/lldb/Target/RemoteAwarePlatform.h | 3 +- lldb/source/API/SBPlatform.cpp | 3 +- .../source/Commands/CommandObjectPlatform.cpp | 9 +- lldb/source/Host/common/Host.cpp | 72 ++++++++--- lldb/source/Host/macosx/objcxx/Host.mm | 4 +- .../Host/macosx/objcxx/HostInfoMacOSX.mm | 8 +- lldb/source/Host/windows/Host.cpp | 7 +- .../Platform/MacOSX/PlatformDarwin.cpp | 13 +- .../Plugins/Platform/POSIX/PlatformPOSIX.cpp | 10 +- .../gdb-server/PlatformRemoteGDBServer.cpp | 6 +- .../gdb-server/PlatformRemoteGDBServer.h | 2 + .../GDBRemoteCommunicationClient.cpp | 2 + .../gdb-remote/GDBRemoteCommunicationClient.h | 2 + .../GDBRemoteCommunicationServerCommon.cpp | 4 +- .../SymbolLocatorDebugSymbols.cpp | 5 +- lldb/source/Target/Platform.cpp | 9 +- lldb/source/Target/RemoteAwarePlatform.cpp | 15 +-- 19 files changed, 208 insertions(+), 87 deletions(-) diff --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h index 4e19d15b06743..a9ab31d180591 100644 --- a/lldb/include/lldb/Host/Host.h +++ b/lldb/include/lldb/Host/Host.h @@ -195,65 +195,108 @@ 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] error_output + /// Pass nullptr if you don't want the command error 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] error_output + /// Pass nullptr if you don't want the command error 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 shell, 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 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] error_output + /// Pass nullptr if you don't want the command error 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(const Args &args, 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] 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] error_output + /// Pass nullptr if you don't want the command error 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 shell, const Args &args, 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); 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 637d4c37b90bc..ee79c946ad91d 100644 --- a/lldb/include/lldb/Target/Platform.h +++ b/lldb/include/lldb/Target/Platform.h @@ -677,6 +677,8 @@ class Platform : public PluginInterface { // the process to exit std::string *command_output, // Pass nullptr if you don't want the command output + std::string + *error_output, // Pass nullptr if you don't want the command output const Timeout<std::micro> &timeout); virtual lldb_private::Status RunShellCommand( @@ -688,6 +690,8 @@ class Platform : public PluginInterface { // the process to exit std::string *command_output, // Pass nullptr if you don't want the command output + std::string + *error_output, // Pass nullptr if you don't want the 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..c1d8d9994654d 100644 --- a/lldb/include/lldb/Target/RemoteAwarePlatform.h +++ b/lldb/include/lldb/Target/RemoteAwarePlatform.h @@ -70,12 +70,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 *command_output, std::string *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 *error, 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..5bab9d9e112fd 100644 --- a/lldb/source/API/SBPlatform.cpp +++ b/lldb/source/API/SBPlatform.cpp @@ -559,12 +559,13 @@ SBError SBPlatform::Run(SBPlatformShellCommand &shell_command) { if (!platform_working_dir.empty()) shell_command.SetWorkingDirectory(platform_working_dir.c_str()); } + std::string stderr_output; return platform_sp->RunShellCommand( shell_command.m_opaque_ptr->m_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, &stderr_output, shell_command.m_opaque_ptr->m_timeout); }); } diff --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp index 4865c48c82b7f..d7c87f36f6e2e 100644 --- a/lldb/source/Commands/CommandObjectPlatform.cpp +++ b/lldb/source/Commands/CommandObjectPlatform.cpp @@ -1706,13 +1706,16 @@ class CommandObjectPlatformShell : public CommandObjectRaw { if (platform_sp) { FileSpec working_dir{}; std::string output; + std::string error_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, &error_output, m_options.m_timeout)); if (!output.empty()) result.GetOutputStream().PutCString(output); + if (!error_output.empty()) + result.GetOutputStream().PutCString(error_output); if (status > 0) { if (signo > 0) { const char *signo_cstr = Host::GetSignalAsCString(signo); diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 510f9c7696d12..8c0de586eaa7d 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 *command_error_ptr, 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, + command_error_ptr, 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 *command_error_ptr, 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, command_error_ptr, + timeout); } Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output_ptr, + std::string *command_error_ptr, 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, command_error_ptr, + timeout); } 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 *command_error_ptr, 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 (command_error_ptr) { + // 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 "command_output_ptr" + 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,8 +492,9 @@ 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); @@ -524,10 +545,33 @@ Status Host::RunShellCommand(llvm::StringRef shell_path, const Args &args, } } } + if (command_error_ptr) { + command_error_ptr->clear(); + uint64_t file_size = + FileSystem::Instance().GetByteSize(error_file_spec); + if (file_size > 0) { + if (file_size > command_error_ptr->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()) + command_error_ptr->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..4d6ad08e1ad74 100644 --- a/lldb/source/Host/macosx/objcxx/Host.mm +++ b/lldb/source/Host/macosx/objcxx/Host.mm @@ -1516,6 +1516,7 @@ static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) { int status; std::string output; + std::string error_output; FileSpec cwd(launch_info.GetWorkingDirectory()); if (!FileSystem::Instance().Exists(cwd)) { char *wd = getcwd(nullptr, 0); @@ -1530,10 +1531,9 @@ static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) { } } bool run_in_shell = true; - bool hide_stderr = true; 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..7f5acadffef94 100644 --- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -452,12 +452,14 @@ static bool ResolveAndVerifyCandidateSupportDir(FileSpec &path) { int status = 0; int signo = 0; std::string output_str; + std::string error_str; // The first time after Xcode was updated or freshly installed, // 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, + &error_str, timeout, run_in_shell); // Check that xcrun returned something useful. if (error.Fail()) { @@ -471,6 +473,8 @@ static bool ResolveAndVerifyCandidateSupportDir(FileSpec &path) { LLDB_LOG(log, "xcrun returned exit code {0}", status); if (!output_str.empty()) LLDB_LOG(log, "xcrun output was:\n{0}", output_str); + if (!error_str.empty()) + LLDB_LOG(log, "xcrun error output was:\n{0}", error_str); return ""; } if (output_str.empty()) { diff --git a/lldb/source/Host/windows/Host.cpp b/lldb/source/Host/windows/Host.cpp index d5704eed10ecb..def887943f37e 100644 --- a/lldb/source/Host/windows/Host.cpp +++ b/lldb/source/Host/windows/Host.cpp @@ -230,10 +230,11 @@ Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { int status; std::string output; + std::string error; 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, &error, 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 3d12f9f815661..0f901f56bef8d 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -683,12 +683,13 @@ static FileSpec GetXcodeSelectPath() { int exit_status = -1; int signo = -1; std::string command_output; - Status status = - Host::RunShellCommand("/usr/bin/xcode-select --print-path", - FileSpec(), // current working directory - &exit_status, &signo, &command_output, - std::chrono::seconds(2), // short timeout - false); // don't run in a shell + std::string error_output; + Status status = Host::RunShellCommand( + "/usr/bin/xcode-select --print-path", + FileSpec(), // current working directory + &exit_status, &signo, &command_output, &error_output, + std::chrono::seconds(2), // short timeout + false); // don't run in a shell if (status.Success() && exit_status == 0 && !command_output.empty()) { size_t first_non_newline = command_output.find_last_not_of("\r\n"); if (first_non_newline != std::string::npos) { 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..898e848fb719a 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -684,12 +684,14 @@ 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 NULL if you don't want the command output + std::string *error_output, // Pass NULL 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, + 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..42246efd540fd 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -142,6 +142,8 @@ class PlatformRemoteGDBServer : public Platform, private UserIDResolver { // process to exit std::string *command_output, // Pass NULL if you don't want the command output + std::string + *error_output, // Pass NULL 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..e87cacea7d553 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -2984,6 +2984,8 @@ lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand( // process to exit std::string *command_output, // Pass NULL if you don't want the command output + std::string + *error_output, // Pass NULL 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..20e4b47c54621 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 *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 4a1117222f34c..67a990b5fb98a 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -748,11 +748,12 @@ GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell( packet.GetHexByteString(working_dir); int status, signo; std::string output; + std::string error; FileSpec working_spec(working_dir); FileSystem::Instance().Resolve(working_spec); Status err = Host::RunShellCommand(path.c_str(), working_spec, &status, &signo, - &output, std::chrono::seconds(10)); + &output, &error, std::chrono::seconds(10)); StreamGDBRemote response; if (err.Fail()) { response.PutCString("F,"); @@ -764,6 +765,7 @@ GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell( response.PutHex32(signo); response.PutChar(','); response.PutEscapedBytes(output.c_str(), output.size()); + response.PutEscapedBytes(error.c_str(), error.size()); } return SendPacketNoLock(response.GetString()); } diff --git a/lldb/source/Plugins/SymbolLocator/DebugSymbols/SymbolLocatorDebugSymbols.cpp b/lldb/source/Plugins/SymbolLocator/DebugSymbols/SymbolLocatorDebugSymbols.cpp index 5c1aaac886d6c..862f53df9fae2 100644 --- a/lldb/source/Plugins/SymbolLocator/DebugSymbols/SymbolLocatorDebugSymbols.cpp +++ b/lldb/source/Plugins/SymbolLocator/DebugSymbols/SymbolLocatorDebugSymbols.cpp @@ -1095,12 +1095,14 @@ 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) @@ -1108,7 +1110,7 @@ bool SymbolLocatorDebugSymbols::DownloadObjectAndSymbolFile( if (error.Fail() || exit_status != 0 || command_output.empty()) { LLDB_LOGF(log, "'%s' failed (exit status: %d, error: '%s', output: '%s')", command.GetData(), exit_status, error.AsCString(), - command_output.c_str()); + error_output.c_str()); return false; } @@ -1123,6 +1125,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 4068210cc1632..b73b86ea15524 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -1248,9 +1248,11 @@ lldb_private::Status Platform::RunShellCommand( // process to exit std::string *command_output, // Pass nullptr if you don't want the command output + std::string * + 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, error_output, timeout); } lldb_private::Status Platform::RunShellCommand( @@ -1264,10 +1266,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 * + 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, 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..48d3da9c080f3 100644 --- a/lldb/source/Target/RemoteAwarePlatform.cpp +++ b/lldb/source/Target/RemoteAwarePlatform.cpp @@ -54,22 +54,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, + int *signo_ptr, std::string *command_output, std::string *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, 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 *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, + error_output, timeout); return Platform::RunShellCommand(shell, command, working_dir, status_ptr, - signo_ptr, command_output, timeout); + signo_ptr, command_output, error_output, + timeout); } Status RemoteAwarePlatform::MakeDirectory(const FileSpec &file_spec, _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
