[Lldb-commits] [lldb] addb514 - [lldb/Target] Add custom interpreter option to `platform shell`

Med Ismail Bennani via lldb-commits lldb-commits at lists.llvm.org
Wed Sep 2 07:37:47 PDT 2020


Author: Med Ismail Bennani
Date: 2020-09-02T16:36:10+02:00
New Revision: addb5148f58d710fcaba04bb2afec8006ae8ac15

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

LOG: [lldb/Target] Add custom interpreter option to `platform shell`

This patch adds the ability to use a custom interpreter with the
`platform shell` command. If the user set the `-s|--shell` option
with the path to a binary, lldb passes it down to the platform's
`RunShellProcess` method and set it as the shell to use in
`ProcessLaunchInfo to run commands.

Note that not all the Platforms support running shell commands with
custom interpreters (i.e. RemoteGDBServer is only expected to use the
default shell).

This patch also makes some refactoring and cleanups, like swapping
CString for StringRef when possible and updating `SBPlatformShellCommand`
with new methods and a new constructor.

rdar://67759256

Differential Revision: https://reviews.llvm.org/D86667

Signed-off-by: Med Ismail Bennani <medismail.bennani at gmail.com>

Added: 
    lldb/test/API/commands/platform/basic/Makefile
    lldb/test/API/commands/platform/basic/myshell.c

Modified: 
    lldb/bindings/interface/SBPlatform.i
    lldb/include/lldb/API/SBPlatform.h
    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/Commands/Options.td
    lldb/source/Host/common/Host.cpp
    lldb/source/Host/macosx/objcxx/Host.mm
    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/Target/Platform.cpp
    lldb/source/Target/RemoteAwarePlatform.cpp
    lldb/test/API/commands/platform/basic/TestPlatformCommand.py
    lldb/test/API/commands/platform/basic/TestPlatformPython.py

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/interface/SBPlatform.i b/lldb/bindings/interface/SBPlatform.i
index 81945222c059..07aecfc354bb 100644
--- a/lldb/bindings/interface/SBPlatform.i
+++ b/lldb/bindings/interface/SBPlatform.i
@@ -45,6 +45,7 @@ public:
 class SBPlatformShellCommand
 {
 public:
+    SBPlatformShellCommand (const char *shell, const char *shell_command);
     SBPlatformShellCommand (const char *shell_command);
 
     SBPlatformShellCommand (const SBPlatformShellCommand &rhs);
@@ -54,6 +55,12 @@ public:
     void
     Clear();
 
+    const char *
+    GetShell();
+
+    void
+    SetShell(const char *shell_interpreter);
+
     const char *
     GetCommand();
 

diff  --git a/lldb/include/lldb/API/SBPlatform.h b/lldb/include/lldb/API/SBPlatform.h
index 4d251b129954..98291f18247d 100644
--- a/lldb/include/lldb/API/SBPlatform.h
+++ b/lldb/include/lldb/API/SBPlatform.h
@@ -51,6 +51,7 @@ class LLDB_API SBPlatformConnectOptions {
 
 class LLDB_API SBPlatformShellCommand {
 public:
+  SBPlatformShellCommand(const char *shell, const char *shell_command);
   SBPlatformShellCommand(const char *shell_command);
 
   SBPlatformShellCommand(const SBPlatformShellCommand &rhs);
@@ -61,6 +62,10 @@ class LLDB_API SBPlatformShellCommand {
 
   void Clear();
 
+  const char *GetShell();
+
+  void SetShell(const char *shell);
+
   const char *GetCommand();
 
   void SetCommand(const char *shell_command);

diff  --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h
index f19cb85d2329..76792cc6eab5 100644
--- a/lldb/include/lldb/Host/Host.h
+++ b/lldb/include/lldb/Host/Host.h
@@ -196,19 +196,34 @@ class Host {
   static Status ShellExpandArguments(ProcessLaunchInfo &launch_info);
 
   /// Run a shell command.
-  /// \arg command  shouldn't be NULL
+  /// \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
-  /// TODO: Convert this function to take a StringRef.
-  static Status RunShellCommand(const char *command,
+  static Status RunShellCommand(llvm::StringRef command,
                                 const FileSpec &working_dir, int *status_ptr,
                                 int *signo_ptr, std::string *command_output,
                                 const Timeout<std::micro> &timeout,
-                                bool run_in_default_shell = true,
+                                bool run_in_shell = true,
+                                bool hide_stderr = false);
+
+  /// 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
+  static Status 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,
+                                bool run_in_shell = true,
                                 bool hide_stderr = false);
 
   /// Run a shell command.
@@ -222,7 +237,23 @@ class Host {
                                 int *status_ptr, int *signo_ptr,
                                 std::string *command_output,
                                 const Timeout<std::micro> &timeout,
-                                bool run_in_default_shell = true,
+                                bool run_in_shell = true,
+                                bool hide_stderr = false);
+
+  /// 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
+  static Status RunShellCommand(llvm::StringRef shell, const Args &args,
+                                const FileSpec &working_dir, int *status_ptr,
+                                int *signo_ptr, std::string *command_output,
+                                const Timeout<std::micro> &timeout,
+                                bool run_in_shell = true,
                                 bool hide_stderr = false);
 
   static bool OpenFileInExternalEditor(const FileSpec &file_spec,

diff  --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h
index 9335f73b37df..64b49ecca606 100644
--- a/lldb/include/lldb/Target/Platform.h
+++ b/lldb/include/lldb/Target/Platform.h
@@ -621,7 +621,18 @@ class Platform : public PluginInterface {
   }
 
   virtual lldb_private::Status RunShellCommand(
-      const char *command,         // Shouldn't be nullptr
+      llvm::StringRef command,
+      const FileSpec &working_dir, // Pass empty FileSpec to use the current
+                                   // working directory
+      int *status_ptr, // Pass nullptr if you don't want the process exit status
+      int *signo_ptr,  // Pass nullptr if you don't want the signal that caused
+                       // the process to exit
+      std::string
+          *command_output, // Pass nullptr if you don't want the command output
+      const Timeout<std::micro> &timeout);
+
+  virtual lldb_private::Status RunShellCommand(
+      llvm::StringRef shell, llvm::StringRef command,
       const FileSpec &working_dir, // Pass empty FileSpec to use the current
                                    // working directory
       int *status_ptr, // Pass nullptr if you don't want the process exit status

diff  --git a/lldb/include/lldb/Target/RemoteAwarePlatform.h b/lldb/include/lldb/Target/RemoteAwarePlatform.h
index 5741dbe027b7..6d6ac99c093f 100644
--- a/lldb/include/lldb/Target/RemoteAwarePlatform.h
+++ b/lldb/include/lldb/Target/RemoteAwarePlatform.h
@@ -68,11 +68,16 @@ class RemoteAwarePlatform : public Platform {
   bool GetRemoteOSKernelDescription(std::string &s) override;
   ArchSpec GetRemoteSystemArchitecture() override;
 
-  Status RunShellCommand(const char *command, const FileSpec &working_dir,
+  Status RunShellCommand(llvm::StringRef command, const FileSpec &working_dir,
                          int *status_ptr, int *signo_ptr,
                          std::string *command_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,
+                         const Timeout<std::micro> &timeout) override;
+
   const char *GetHostname() override;
   UserIDResolver &GetUserIDResolver() override;
   lldb_private::Environment GetEnvironment() override;

diff  --git a/lldb/source/API/SBPlatform.cpp b/lldb/source/API/SBPlatform.cpp
index 7ac852488ffb..3c6422e211fc 100644
--- a/lldb/source/API/SBPlatform.cpp
+++ b/lldb/source/API/SBPlatform.cpp
@@ -50,14 +50,25 @@ struct PlatformConnectOptions {
 
 // PlatformShellCommand
 struct PlatformShellCommand {
-  PlatformShellCommand(const char *shell_command = nullptr)
+  PlatformShellCommand(llvm::StringRef shell_interpreter,
+                       llvm::StringRef shell_command)
       : m_command(), m_working_dir(), m_status(0), m_signo(0) {
-    if (shell_command && shell_command[0])
-      m_command = shell_command;
+    if (!shell_interpreter.empty())
+      m_shell = shell_interpreter.str();
+
+    if (!m_shell.empty() && !shell_command.empty())
+      m_command = shell_command.str();
+  }
+
+  PlatformShellCommand(llvm::StringRef shell_command = llvm::StringRef())
+      : m_shell(), m_command(), m_working_dir(), m_status(0), m_signo(0) {
+    if (!shell_command.empty())
+      m_command = shell_command.str();
   }
 
   ~PlatformShellCommand() = default;
 
+  std::string m_shell;
   std::string m_command;
   std::string m_working_dir;
   std::string m_output;
@@ -163,6 +174,13 @@ void SBPlatformConnectOptions::SetLocalCacheDirectory(const char *path) {
 }
 
 // SBPlatformShellCommand
+SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_interpreter,
+                                               const char *shell_command)
+    : m_opaque_ptr(new PlatformShellCommand(shell_interpreter, shell_command)) {
+  LLDB_RECORD_CONSTRUCTOR(SBPlatformShellCommand, (const char *, const char *),
+                          shell_interpreter, shell_command);
+}
+
 SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_command)
     : m_opaque_ptr(new PlatformShellCommand(shell_command)) {
   LLDB_RECORD_CONSTRUCTOR(SBPlatformShellCommand, (const char *),
@@ -200,6 +218,24 @@ void SBPlatformShellCommand::Clear() {
   m_opaque_ptr->m_signo = 0;
 }
 
+const char *SBPlatformShellCommand::GetShell() {
+  LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformShellCommand, GetShell);
+
+  if (m_opaque_ptr->m_shell.empty())
+    return nullptr;
+  return m_opaque_ptr->m_shell.c_str();
+}
+
+void SBPlatformShellCommand::SetShell(const char *shell_interpreter) {
+  LLDB_RECORD_METHOD(void, SBPlatformShellCommand, SetShell, (const char *),
+                     shell_interpreter);
+
+  if (shell_interpreter && shell_interpreter[0])
+    m_opaque_ptr->m_shell = shell_interpreter;
+  else
+    m_opaque_ptr->m_shell.clear();
+}
+
 const char *SBPlatformShellCommand::GetCommand() {
   LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformShellCommand, GetCommand);
 
@@ -557,7 +593,8 @@ SBError SBPlatform::Run(SBPlatformShellCommand &shell_command) {
       if (working_dir)
         shell_command.SetWorkingDirectory(working_dir);
     }
-    return platform_sp->RunShellCommand(command, FileSpec(working_dir),
+    return platform_sp->RunShellCommand(shell_command.m_opaque_ptr->m_shell,
+                                        command, FileSpec(working_dir),
                                         &shell_command.m_opaque_ptr->m_status,
                                         &shell_command.m_opaque_ptr->m_signo,
                                         &shell_command.m_opaque_ptr->m_output,
@@ -699,6 +736,8 @@ void RegisterMethods<SBPlatformShellCommand>(Registry &R) {
       SBPlatformShellCommand &,
       SBPlatformShellCommand, operator=,(const lldb::SBPlatformShellCommand &));
   LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, Clear, ());
+  LLDB_REGISTER_METHOD(const char *, SBPlatformShellCommand, GetShell, ());
+  LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, SetShell, (const char *));
   LLDB_REGISTER_METHOD(const char *, SBPlatformShellCommand, GetCommand, ());
   LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, SetCommand,
                        (const char *));

diff  --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp
index b5409e611f05..3a5af9f91cf1 100644
--- a/lldb/source/Commands/CommandObjectPlatform.cpp
+++ b/lldb/source/Commands/CommandObjectPlatform.cpp
@@ -1611,6 +1611,16 @@ class CommandObjectPlatformShell : public CommandObjectRaw {
         else
           m_timeout = std::chrono::seconds(timeout_sec);
         break;
+      case 's': {
+        if (option_arg.empty()) {
+          error.SetErrorStringWithFormat(
+              "missing shell interpreter path for option -i|--interpreter.");
+          return error;
+        }
+
+        m_shell_interpreter = option_arg.str();
+        break;
+      }
       default:
         llvm_unreachable("Unimplemented option");
       }
@@ -1621,10 +1631,12 @@ class CommandObjectPlatformShell : public CommandObjectRaw {
     void OptionParsingStarting(ExecutionContext *execution_context) override {
       m_timeout.reset();
       m_use_host_platform = false;
+      m_shell_interpreter.clear();
     }
 
     Timeout<std::micro> m_timeout = std::chrono::seconds(10);
     bool m_use_host_platform;
+    std::string m_shell_interpreter;
   };
 
   CommandObjectPlatformShell(CommandInterpreter &interpreter)
@@ -1650,7 +1662,6 @@ class CommandObjectPlatformShell : public CommandObjectRaw {
 
     const bool is_alias = !raw_command_line.contains("platform");
     OptionsWithRaw args(raw_command_line);
-    const char *expr = args.GetRawPart().c_str();
 
     if (args.HasArgs())
       if (!ParseOptions(args.GetArgs(), result))
@@ -1662,6 +1673,8 @@ class CommandObjectPlatformShell : public CommandObjectRaw {
       return false;
     }
 
+    llvm::StringRef cmd = args.GetRawPart();
+
     PlatformSP platform_sp(
         m_options.m_use_host_platform
             ? Platform::GetHostPlatform()
@@ -1672,7 +1685,8 @@ class CommandObjectPlatformShell : public CommandObjectRaw {
       std::string output;
       int status = -1;
       int signo = -1;
-      error = (platform_sp->RunShellCommand(expr, working_dir, &status, &signo,
+      error = (platform_sp->RunShellCommand(m_options.m_shell_interpreter, cmd,
+                                            working_dir, &status, &signo,
                                             &output, m_options.m_timeout));
       if (!output.empty())
         result.GetOutputStream().PutCString(output);

diff  --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index fbb64957f48d..4bfaf18ec302 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -631,6 +631,8 @@ let Command = "platform shell" in {
     Desc<"Run the commands on the host shell when enabled.">;
   def platform_shell_timeout : Option<"timeout", "t">, Arg<"Value">,
     Desc<"Seconds to wait for the remote host to finish running the command.">;
+  def platform_shell_interpreter : Option<"shell", "s">, Arg<"Path">,
+    Desc<"Shell interpreter path. This is the binary used to run the command.">;
 }
 
 let Command = "process attach" in {

diff  --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp
index 71c2983ab00f..958fca07850b 100644
--- a/lldb/source/Host/common/Host.cpp
+++ b/lldb/source/Host/common/Host.cpp
@@ -467,14 +467,24 @@ MonitorShellCommand(std::shared_ptr<ShellInfo> shell_info, lldb::pid_t pid,
   return true;
 }
 
-Status Host::RunShellCommand(const char *command, const FileSpec &working_dir,
-                             int *status_ptr, int *signo_ptr,
-                             std::string *command_output_ptr,
+Status Host::RunShellCommand(llvm::StringRef command,
+                             const FileSpec &working_dir, int *status_ptr,
+                             int *signo_ptr, std::string *command_output_ptr,
+                             const Timeout<std::micro> &timeout,
+                             bool run_in_shell, bool hide_stderr) {
+  return RunShellCommand(llvm::StringRef(), Args(command), working_dir,
+                         status_ptr, signo_ptr, command_output_ptr, timeout,
+                         run_in_shell, hide_stderr);
+}
+
+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,
                              const Timeout<std::micro> &timeout,
-                             bool run_in_default_shell,
-                             bool hide_stderr) {
-  return RunShellCommand(Args(command), working_dir, status_ptr, signo_ptr,
-                         command_output_ptr, timeout, run_in_default_shell,
+                             bool run_in_shell, bool hide_stderr) {
+  return RunShellCommand(shell_path, Args(command), working_dir, status_ptr,
+                         signo_ptr, command_output_ptr, timeout, run_in_shell,
                          hide_stderr);
 }
 
@@ -482,14 +492,27 @@ Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir,
                              int *status_ptr, int *signo_ptr,
                              std::string *command_output_ptr,
                              const Timeout<std::micro> &timeout,
-                             bool run_in_default_shell,
-                             bool hide_stderr) {
+                             bool run_in_shell, bool hide_stderr) {
+  return RunShellCommand(llvm::StringRef(), args, working_dir, status_ptr,
+                         signo_ptr, command_output_ptr, timeout, run_in_shell,
+                         hide_stderr);
+}
+
+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,
+                             const Timeout<std::micro> &timeout,
+                             bool run_in_shell, bool hide_stderr) {
   Status error;
   ProcessLaunchInfo launch_info;
   launch_info.SetArchitecture(HostInfo::GetArchitecture());
-  if (run_in_default_shell) {
+  if (run_in_shell) {
     // Run the command in a shell
-    launch_info.SetShell(HostInfo::GetDefaultShell());
+    FileSpec shell = HostInfo::GetDefaultShell();
+    if (!shell_path.empty())
+      shell.SetPath(shell_path);
+
+    launch_info.SetShell(shell);
     launch_info.GetArguments().AppendArguments(args);
     const bool localhost = true;
     const bool will_debug = false;

diff  --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm
index faac6f59190a..8cd3b3591993 100644
--- a/lldb/source/Host/macosx/objcxx/Host.mm
+++ b/lldb/source/Host/macosx/objcxx/Host.mm
@@ -1323,11 +1323,11 @@ static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) {
         launch_info.SetWorkingDirectory(working_dir);
       }
     }
-    bool run_in_default_shell = true;
+    bool run_in_shell = true;
     bool hide_stderr = true;
-    Status e = RunShellCommand(expand_command, cwd, &status, nullptr, &output,
-                               std::chrono::seconds(10), run_in_default_shell,
-                               hide_stderr);
+    Status e =
+        RunShellCommand(expand_command, cwd, &status, nullptr, &output,
+                        std::chrono::seconds(10), run_in_shell, hide_stderr);
 
     if (e.Fail())
       return e;

diff  --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
index e1eb15c3e8c9..0e0b61f1534f 100644
--- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
+++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
@@ -711,7 +711,7 @@ bool PlatformRemoteGDBServer::GetFileExists(const FileSpec &file_spec) {
 }
 
 Status PlatformRemoteGDBServer::RunShellCommand(
-    const char *command, // Shouldn't be NULL
+    llvm::StringRef shell, llvm::StringRef command,
     const FileSpec &
         working_dir, // Pass empty FileSpec to use the current working directory
     int *status_ptr, // Pass NULL if you don't want the process exit status

diff  --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
index 3562b2bb09df..297b482eb87a 100644
--- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
+++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
@@ -140,7 +140,7 @@ class PlatformRemoteGDBServer : public Platform, private UserIDResolver {
   Status Unlink(const FileSpec &path) override;
 
   Status RunShellCommand(
-      const char *command,         // Shouldn't be NULL
+      llvm::StringRef shell, llvm::StringRef command,
       const FileSpec &working_dir, // Pass empty FileSpec to use the current
                                    // working directory
       int *status_ptr, // Pass NULL if you don't want the process exit status

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 0949b9918523..dd0f69841aa7 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -2812,7 +2812,7 @@ lldb::addr_t GDBRemoteCommunicationClient::GetShlibInfoAddr() {
 }
 
 lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand(
-    const char *command, // Shouldn't be NULL
+    llvm::StringRef command,
     const FileSpec &
         working_dir, // Pass empty FileSpec to use the current working directory
     int *status_ptr, // Pass NULL if you don't want the process exit status
@@ -2823,7 +2823,7 @@ lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand(
     const Timeout<std::micro> &timeout) {
   lldb_private::StreamString stream;
   stream.PutCString("qPlatform_shell:");
-  stream.PutBytesAsRawHex8(command, strlen(command));
+  stream.PutBytesAsRawHex8(command.data(), command.size());
   stream.PutChar(',');
   uint32_t timeout_sec = UINT32_MAX;
   if (timeout) {

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 0159125a433b..61acfad5d313 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -399,7 +399,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
   bool GetFileExists(const FileSpec &file_spec);
 
   Status RunShellCommand(
-      const char *command,         // Shouldn't be nullptr
+      llvm::StringRef command,
       const FileSpec &working_dir, // Pass empty FileSpec to use the current
                                    // working directory
       int *status_ptr, // Pass nullptr if you don't want the process exit status

diff  --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp
index e867b8db4723..7416ea6dd40c 100644
--- a/lldb/source/Target/Platform.cpp
+++ b/lldb/source/Target/Platform.cpp
@@ -1319,7 +1319,23 @@ MmapArgList Platform::GetMmapArgumentList(const ArchSpec &arch, addr_t addr,
 }
 
 lldb_private::Status Platform::RunShellCommand(
-    const char *command, // Shouldn't be nullptr
+    llvm::StringRef command,
+    const FileSpec &
+        working_dir, // Pass empty FileSpec to use the current working directory
+    int *status_ptr, // Pass nullptr if you don't want the process exit status
+    int *signo_ptr, // Pass nullptr if you don't want the signal that caused the
+                    // process to exit
+    std::string
+        *command_output, // Pass nullptr if you don't want the command output
+    const Timeout<std::micro> &timeout) {
+  return RunShellCommand(llvm::StringRef(), command, working_dir, status_ptr,
+                         signo_ptr, command_output, timeout);
+}
+
+lldb_private::Status Platform::RunShellCommand(
+    llvm::StringRef shell,   // Pass empty if you want to use the default
+                             // shell interpreter
+    llvm::StringRef command, // Shouldn't be empty
     const FileSpec &
         working_dir, // Pass empty FileSpec to use the current working directory
     int *status_ptr, // Pass nullptr if you don't want the process exit status
@@ -1329,8 +1345,8 @@ lldb_private::Status Platform::RunShellCommand(
         *command_output, // Pass nullptr if you don't want the command output
     const Timeout<std::micro> &timeout) {
   if (IsHost())
-    return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr,
-                                 command_output, timeout);
+    return Host::RunShellCommand(shell, command, working_dir, status_ptr,
+                                 signo_ptr, command_output, timeout);
   else
     return Status("unimplemented");
 }

diff  --git a/lldb/source/Target/RemoteAwarePlatform.cpp b/lldb/source/Target/RemoteAwarePlatform.cpp
index f53158b06b8f..3a186adca04c 100644
--- a/lldb/source/Target/RemoteAwarePlatform.cpp
+++ b/lldb/source/Target/RemoteAwarePlatform.cpp
@@ -171,15 +171,24 @@ Status RemoteAwarePlatform::ResolveExecutable(
 }
 
 Status RemoteAwarePlatform::RunShellCommand(
-    const char *command, const FileSpec &working_dir, int *status_ptr,
+    llvm::StringRef command, const FileSpec &working_dir, int *status_ptr,
     int *signo_ptr, std::string *command_output,
     const Timeout<std::micro> &timeout) {
+  return RunShellCommand(llvm::StringRef(), command, working_dir, status_ptr,
+                         signo_ptr, command_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) {
   if (IsHost())
-    return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr,
-                                 command_output, timeout);
+    return Host::RunShellCommand(shell, command, working_dir, status_ptr,
+                                 signo_ptr, command_output, timeout);
   if (m_remote_platform_sp)
-    return m_remote_platform_sp->RunShellCommand(
-        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, timeout);
   return Status("unable to run a remote command without a platform");
 }
 

diff  --git a/lldb/test/API/commands/platform/basic/Makefile b/lldb/test/API/commands/platform/basic/Makefile
new file mode 100644
index 000000000000..3626466f607c
--- /dev/null
+++ b/lldb/test/API/commands/platform/basic/Makefile
@@ -0,0 +1,5 @@
+C_SOURCES := myshell.c
+CFLAGS_EXTRAS := -g0 # No debug info.
+MAKE_DSYM := NO
+
+include Makefile.rules

diff  --git a/lldb/test/API/commands/platform/basic/TestPlatformCommand.py b/lldb/test/API/commands/platform/basic/TestPlatformCommand.py
index 570f9b3f828d..dc1701258246 100644
--- a/lldb/test/API/commands/platform/basic/TestPlatformCommand.py
+++ b/lldb/test/API/commands/platform/basic/TestPlatformCommand.py
@@ -13,6 +13,7 @@
 class PlatformCommandTestCase(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
 
     @no_debug_info_test
     def test_help_platform(self):
@@ -92,3 +93,11 @@ def test_shell_timeout(self):
                     "error: timed out waiting for shell command to complete"])
         self.expect("shell -t 1 --  sleep 3", error=True, substrs=[
                     "error: timed out waiting for shell command to complete"])
+
+    @no_debug_info_test
+    def test_host_shell_interpreter(self):
+        """ Test the host platform shell with a 
diff erent interpreter """
+        self.build()
+        exe = self.getBuildArtifact('a.out')
+        self.expect("platform shell -h -s " + exe + " -- 'echo $0'",
+                    substrs=['SUCCESS', 'a.out'])

diff  --git a/lldb/test/API/commands/platform/basic/TestPlatformPython.py b/lldb/test/API/commands/platform/basic/TestPlatformPython.py
index ab10d30b6ff5..0063621e5800 100644
--- a/lldb/test/API/commands/platform/basic/TestPlatformPython.py
+++ b/lldb/test/API/commands/platform/basic/TestPlatformPython.py
@@ -79,3 +79,20 @@ def test_available_platform_list(self):
             self.assertEqual(
                 desc_data.GetType(), lldb.eStructuredDataTypeString,
                 'Platform description is a string')
+
+    @add_test_categories(['pyapi'])
+    @no_debug_info_test
+    def test_shell_interpreter(self):
+        """ Test a shell with a custom interpreter """
+        platform = self.dbg.GetSelectedPlatform()
+        self.assertTrue(platform.IsValid())
+
+        sh_cmd = lldb.SBPlatformShellCommand('/bin/zsh', 'echo $0')
+        self.assertIn('/bin/zsh', sh_cmd.GetShell())
+        self.assertIn('echo $0', sh_cmd.GetCommand())
+
+        self.build()
+        sh_cmd.SetShell(self.getBuildArtifact('a.out'))
+        err = platform.Run(sh_cmd)
+        self.assertTrue(err.Success())
+        self.assertIn("SUCCESS", sh_cmd.GetOutput())

diff  --git a/lldb/test/API/commands/platform/basic/myshell.c b/lldb/test/API/commands/platform/basic/myshell.c
new file mode 100644
index 000000000000..d1c0eecb943e
--- /dev/null
+++ b/lldb/test/API/commands/platform/basic/myshell.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char *argv[]) {
+  if (argc < 3) {
+    fprintf(stderr, "ERROR: Too few arguments (count: %d).\n", argc - 1);
+    exit(1);
+  }
+
+#ifdef WIN32
+  char *cmd_opt = "/C";
+#else
+  char *cmd_opt = "-c";
+#endif
+
+  if (strncmp(argv[1], cmd_opt, 2)) {
+    fprintf(stderr, "ERROR: Missing shell command option ('%s').\n", cmd_opt);
+    exit(1);
+  }
+
+  printf("SUCCESS: %s\n", argv[0]);
+  return 0;
+}


        


More information about the lldb-commits mailing list