[Lldb-commits] [lldb] r154730 - in /lldb/trunk: include/lldb/Host/Host.h include/lldb/Target/Process.h source/Commands/CommandObjectPlatform.cpp source/Host/common/Host.cpp source/Plugins/Platform/Linux/PlatformLinux.cpp source/Target/Platform.cpp source/Target/Process.cpp

Greg Clayton gclayton at apple.com
Fri Apr 13 18:42:46 PDT 2012


Author: gclayton
Date: Fri Apr 13 20:42:46 2012
New Revision: 154730

URL: http://llvm.org/viewvc/llvm-project?rev=154730&view=rev
Log:
Added a new host function that allows us to run shell command and get the output from them along with the status and signal:

Error
Host::RunShellCommand (const char *command,
                       const char *working_dir,
                       int *status_ptr,
                       int *signo_ptr,
                       std::string *command_output_ptr,
                       uint32_t timeout_sec);

This will allow us to use this functionality in the host lldb_private::Platform, and also use it in our lldb-platform binary. It leverages the existing code in Host::LaunchProcess and ProcessLaunchInfo.


Modified:
    lldb/trunk/include/lldb/Host/Host.h
    lldb/trunk/include/lldb/Target/Process.h
    lldb/trunk/source/Commands/CommandObjectPlatform.cpp
    lldb/trunk/source/Host/common/Host.cpp
    lldb/trunk/source/Plugins/Platform/Linux/PlatformLinux.cpp
    lldb/trunk/source/Target/Platform.cpp
    lldb/trunk/source/Target/Process.cpp

Modified: lldb/trunk/include/lldb/Host/Host.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Host/Host.h?rev=154730&r1=154729&r2=154730&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Host/Host.h (original)
+++ lldb/trunk/include/lldb/Host/Host.h Fri Apr 13 20:42:46 2012
@@ -414,6 +414,14 @@
     static Error
     LaunchProcess (ProcessLaunchInfo &launch_info);
 
+    static Error
+    RunShellCommand (const char *command,           // Shouldn't be NULL
+                     const char *working_dir,       // Pass NULL to use the current working directory
+                     int *status_ptr,               // Pass NULL if you don't want the process exit status
+                     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
+                     uint32_t timeout_sec);         // Timeout in seconds to wait for shell program to finish
+    
     static lldb::DataBufferSP
     GetAuxvData (lldb_private::Process *process);
 

Modified: lldb/trunk/include/lldb/Target/Process.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Process.h?rev=154730&r1=154729&r2=154730&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/Process.h (original)
+++ lldb/trunk/include/lldb/Target/Process.h Fri Apr 13 20:42:46 2012
@@ -733,7 +733,10 @@
     }
 
     bool
-    ConvertArgumentsForLaunchingInShell (Error &error, bool localhost);
+    ConvertArgumentsForLaunchingInShell (Error &error,
+                                         bool localhost,
+                                         bool will_debug,
+                                         bool first_arg_is_full_shell_command);
     
     void
     SetMonitorProcessCallback (Host::MonitorChildProcessCallback callback, 

Modified: lldb/trunk/source/Commands/CommandObjectPlatform.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectPlatform.cpp?rev=154730&r1=154729&r2=154730&view=diff
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectPlatform.cpp (original)
+++ lldb/trunk/source/Commands/CommandObjectPlatform.cpp Fri Apr 13 20:42:46 2012
@@ -870,6 +870,75 @@
     DISALLOW_COPY_AND_ASSIGN (CommandObjectPlatformProcess);
 };
 
+
+class CommandObjectPlatformShell : public CommandObject
+{
+public:
+    CommandObjectPlatformShell (CommandInterpreter &interpreter) :
+    CommandObject (interpreter, 
+                   "platform shell",
+                   "Run a shell command on a the selected platform.",
+                   "platform shell <shell-command>",
+                   0)
+    {
+    }
+    
+    virtual
+    ~CommandObjectPlatformShell ()
+    {
+    }
+    
+    virtual bool
+    Execute (Args& command,
+             CommandReturnObject &result)
+    {
+        return false;
+    }
+    
+    virtual bool
+    WantsRawCommandString() { return true; }    
+
+    bool
+    ExecuteRawCommandString (const char *raw_command_line, CommandReturnObject &result)
+    {
+        // TODO: Implement "Platform::RunShellCommand()" and switch over to using
+        // the current platform when it is in the interface. 
+        const char *working_dir = NULL;
+        std::string output;
+        int status = -1;
+        int signo = -1;
+        Error error (Host::RunShellCommand (raw_command_line, working_dir, &status, &signo, &output, 10));
+        if (!output.empty())
+            result.GetOutputStream().PutCString(output.c_str());
+        if (status > 0)
+        {
+            if (signo > 0)
+            {
+                const char *signo_cstr = Host::GetSignalAsCString(signo);
+                if (signo_cstr)
+                    result.GetOutputStream().Printf("error: command returned with status %i and signal %s\n", status, signo_cstr);
+                else
+                    result.GetOutputStream().Printf("error: command returned with status %i and signal %i\n", status, signo);
+            }
+            else
+                result.GetOutputStream().Printf("error: command returned with status %i\n", status);
+        }
+
+        if (error.Fail())
+        {
+            result.AppendError(error.AsCString());
+            result.SetStatus (eReturnStatusFailed);
+        }
+        else
+        {
+            result.SetStatus (eReturnStatusSuccessFinishResult);
+        }
+        return true;
+    }
+
+protected:
+};
+
 //----------------------------------------------------------------------
 // CommandObjectPlatform constructor
 //----------------------------------------------------------------------
@@ -885,6 +954,7 @@
     LoadSubCommand ("connect", CommandObjectSP (new CommandObjectPlatformConnect  (interpreter)));
     LoadSubCommand ("disconnect", CommandObjectSP (new CommandObjectPlatformDisconnect  (interpreter)));
     LoadSubCommand ("process", CommandObjectSP (new CommandObjectPlatformProcess  (interpreter)));
+    LoadSubCommand ("shell", CommandObjectSP (new CommandObjectPlatformShell  (interpreter)));
 }
 
 

Modified: lldb/trunk/source/Host/common/Host.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Host/common/Host.cpp?rev=154730&r1=154729&r2=154730&view=diff
==============================================================================
--- lldb/trunk/source/Host/common/Host.cpp (original)
+++ lldb/trunk/source/Host/common/Host.cpp Fri Apr 13 20:42:46 2012
@@ -1239,6 +1239,164 @@
     return dummy_target;
 }
 
+struct ShellInfo
+{
+    ShellInfo () :
+        process_reaped (false),
+        can_delete (false),
+        pid (LLDB_INVALID_PROCESS_ID),
+        signo(-1),
+        status(-1)
+    {
+    }
+
+    lldb_private::Predicate<bool> process_reaped;
+    lldb_private::Predicate<bool> can_delete;
+    lldb::pid_t pid;
+    int signo;
+    int status;
+};
+
+static bool
+MonitorShellCommand (void *callback_baton,
+                     lldb::pid_t pid,
+                     bool exited,       // True if the process did exit
+                     int signo,         // Zero for no signal
+                     int status)   // Exit value of process if signal is zero
+{
+    ShellInfo *shell_info = (ShellInfo *)callback_baton;
+    shell_info->pid = pid;
+    shell_info->signo = signo;
+    shell_info->status = status;
+    // Let the thread running Host::RunShellCommand() know that the process
+    // exited and that ShellInfo has been filled in by broadcasting to it
+    shell_info->process_reaped.SetValue(1, eBroadcastAlways);
+    // Now wait for a handshake back from that thread running Host::RunShellCommand
+    // so we know that we can delete shell_info_ptr
+    shell_info->can_delete.WaitForValueEqualTo(true);
+    // Sleep a bit to allow the shell_info->can_delete.SetValue() to complete...
+    usleep(1000);
+    // Now delete the shell info that was passed into this function
+    delete shell_info;
+    return true;
+}
+
+Error
+Host::RunShellCommand (const char *command,
+                       const char *working_dir,
+                       int *status_ptr,
+                       int *signo_ptr,
+                       std::string *command_output_ptr,
+                       uint32_t timeout_sec)
+{
+    Error error;
+    ProcessLaunchInfo launch_info;
+    launch_info.SetShell("/bin/bash");
+    launch_info.GetArguments().AppendArgument(command);
+    const bool localhost = true;
+    const bool will_debug = false;
+    const bool first_arg_is_full_shell_command = true;
+    launch_info.ConvertArgumentsForLaunchingInShell (error,
+                                                     localhost,
+                                                     will_debug,
+                                                     first_arg_is_full_shell_command);
+    
+    if (working_dir)
+        launch_info.SetWorkingDirectory(working_dir);
+    char output_file_path_buffer[L_tmpnam];
+    const char *output_file_path = NULL;
+    if (command_output_ptr)
+    {
+        // Create a temporary file to get the stdout/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"
+        output_file_path = ::tmpnam(output_file_path_buffer);
+        launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false);
+        launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_path, false, true);
+        launch_info.AppendDuplicateFileAction(STDERR_FILENO, STDOUT_FILENO);
+    }
+    else
+    {
+        launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false);
+        launch_info.AppendSuppressFileAction (STDOUT_FILENO, false, true);
+        launch_info.AppendSuppressFileAction (STDERR_FILENO, false, true);
+    }
+    
+    // The process monitor callback will delete the 'shell_info_ptr' below...
+    std::auto_ptr<ShellInfo> shell_info_ap (new ShellInfo());
+    
+    const bool monitor_signals = false;
+    launch_info.SetMonitorProcessCallback(MonitorShellCommand, shell_info_ap.get(), monitor_signals);
+    
+    error = LaunchProcess (launch_info);
+    const lldb::pid_t pid = launch_info.GetProcessID();
+    if (pid != LLDB_INVALID_PROCESS_ID)
+    {
+        // The process successfully launched, so we can defer ownership of
+        // "shell_info" to the MonitorShellCommand callback function that will
+        // get called when the process dies. We release the std::auto_ptr as it
+        // doesn't need to delete the ShellInfo anymore.
+        ShellInfo *shell_info = shell_info_ap.release();
+        TimeValue timeout_time(TimeValue::Now());
+        timeout_time.OffsetWithSeconds(timeout_sec);
+        bool timed_out = false;
+        shell_info->process_reaped.WaitForValueEqualTo(true, &timeout_time, &timed_out);
+        if (timed_out)
+        {
+            error.SetErrorString("timed out waiting for shell command to complete");
+            
+            // Kill the process since it didn't complete withint the timeout specified
+            ::kill (pid, SIGKILL);
+            // Wait for the monitor callback to get the message
+            timeout_time = TimeValue::Now();
+            timeout_time.OffsetWithSeconds(1);
+            timed_out = false;
+            shell_info->process_reaped.WaitForValueEqualTo(true, &timeout_time, &timed_out);
+        }
+        else
+        {
+            if (status_ptr)
+                *status_ptr = shell_info->status;
+
+            if (signo_ptr)
+                *signo_ptr = shell_info->signo;
+
+            if (command_output_ptr)
+            {
+                command_output_ptr->clear();
+                FileSpec file_spec(output_file_path, File::eOpenOptionRead);
+                uint64_t file_size = file_spec.GetByteSize();
+                if (file_size > 0)
+                {
+                    if (file_size > command_output_ptr->max_size())
+                    {
+                        error.SetErrorStringWithFormat("shell command output is too large to fit into a std::string");
+                    }
+                    else
+                    {
+                        command_output_ptr->resize(file_size);
+                        file_spec.ReadFileContents(0, &((*command_output_ptr)[0]), command_output_ptr->size(), &error);
+                    }
+                }
+            }
+        }
+        shell_info->can_delete.SetValue(true, eBroadcastAlways);
+    }
+    else
+    {
+        error.SetErrorString("failed to get process ID");
+    }
+
+    if (output_file_path)
+        ::unlink (output_file_path);
+    // Handshake with the monitor thread, or just let it know in advance that
+    // it can delete "shell_info" in case we timed out and were not able to kill
+    // the process...
+    return error;
+}
+
+
+
 #if !defined (__APPLE__)
 bool
 Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no)

Modified: lldb/trunk/source/Plugins/Platform/Linux/PlatformLinux.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Platform/Linux/PlatformLinux.cpp?rev=154730&r1=154729&r2=154730&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Platform/Linux/PlatformLinux.cpp (original)
+++ lldb/trunk/source/Plugins/Platform/Linux/PlatformLinux.cpp Fri Apr 13 20:42:46 2012
@@ -330,7 +330,12 @@
         if (launch_info.GetFlags().Test (eLaunchFlagLaunchInShell))
         {
             const bool is_localhost = true;
-            if (!launch_info.ConvertArgumentsForLaunchingInShell (error, is_localhost))
+            const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug);
+            const bool first_arg_is_full_shell_command = false;
+            if (!launch_info.ConvertArgumentsForLaunchingInShell (error,
+                                                                  is_localhost,
+                                                                  will_debug,
+                                                                  first_arg_is_full_shell_command))
                 return error;
         }
         error = Platform::LaunchProcess (launch_info);

Modified: lldb/trunk/source/Target/Platform.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/Platform.cpp?rev=154730&r1=154729&r2=154730&view=diff
==============================================================================
--- lldb/trunk/source/Target/Platform.cpp (original)
+++ lldb/trunk/source/Target/Platform.cpp Fri Apr 13 20:42:46 2012
@@ -574,7 +574,12 @@
         if (launch_info.GetFlags().Test (eLaunchFlagLaunchInShell))
         {
             const bool is_localhost = true;
-            if (!launch_info.ConvertArgumentsForLaunchingInShell (error, is_localhost))
+            const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug);
+            const bool first_arg_is_full_shell_command = false;
+            if (!launch_info.ConvertArgumentsForLaunchingInShell (error,
+                                                                  is_localhost,
+                                                                  will_debug,
+                                                                  first_arg_is_full_shell_command))
                 return error;
         }
 

Modified: lldb/trunk/source/Target/Process.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/Process.cpp?rev=154730&r1=154729&r2=154730&view=diff
==============================================================================
--- lldb/trunk/source/Target/Process.cpp (original)
+++ lldb/trunk/source/Target/Process.cpp Fri Apr 13 20:42:46 2012
@@ -303,7 +303,10 @@
 
 
 bool
-ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, bool localhost)
+ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error,
+                                                        bool localhost,
+                                                        bool will_debug,
+                                                        bool first_arg_is_full_shell_command)
 {
     error.Clear();
 
@@ -334,37 +337,56 @@
             Args shell_arguments;
             std::string safe_arg;
             shell_arguments.AppendArgument (shell_executable);
-            StreamString shell_command;
             shell_arguments.AppendArgument ("-c");
-            shell_command.PutCString ("exec");
-            if (GetArchitecture().IsValid())
-            {
-                shell_command.Printf(" /usr/bin/arch -arch %s", GetArchitecture().GetArchitectureName());
-                // Set the resume count to 2: 
-                // 1 - stop in shell
-                // 2 - stop in /usr/bin/arch
-                // 3 - then we will stop in our program
-                SetResumeCount(2);
-            }
-            else
+
+            StreamString shell_command;
+            if (will_debug)
             {
-                // Set the resume count to 1: 
-                // 1 - stop in shell
-                // 2 - then we will stop in our program
-                SetResumeCount(1);
+                shell_command.PutCString ("exec");
+                if (GetArchitecture().IsValid())
+                {
+                    shell_command.Printf(" /usr/bin/arch -arch %s", GetArchitecture().GetArchitectureName());
+                    // Set the resume count to 2: 
+                    // 1 - stop in shell
+                    // 2 - stop in /usr/bin/arch
+                    // 3 - then we will stop in our program
+                    SetResumeCount(2);
+                }
+                else
+                {
+                    // Set the resume count to 1: 
+                    // 1 - stop in shell
+                    // 2 - then we will stop in our program
+                    SetResumeCount(1);
+                }
             }
             
             const char **argv = GetArguments().GetConstArgumentVector ();
             if (argv)
             {
-                for (size_t i=0; argv[i] != NULL; ++i)
+                if (first_arg_is_full_shell_command)
                 {
-                    const char *arg = Args::GetShellSafeArgument (argv[i], safe_arg);
-                    shell_command.Printf(" %s", arg);
+                    // There should only be one argument that is the shell command itself to be used as is
+                    if (argv[0] && !argv[1])
+                        shell_command.Printf("%s", argv[0]);
+                    else
+                        return false;
+                }
+                else
+                {
+                    for (size_t i=0; argv[i] != NULL; ++i)
+                    {
+                        const char *arg = Args::GetShellSafeArgument (argv[i], safe_arg);
+                        shell_command.Printf(" %s", arg);
+                    }
                 }
+                shell_arguments.AppendArgument (shell_command.GetString().c_str());
             }
-            shell_arguments.AppendArgument (shell_command.GetString().c_str());
-            
+            else
+            {
+                return false;
+            }
+
             m_executable.SetFile(shell_executable, false);
             m_arguments = shell_arguments;
             return true;





More information about the lldb-commits mailing list