summaryrefslogtreecommitdiffstats
path: root/lldb
diff options
context:
space:
mode:
authorGreg Clayton <gclayton@apple.com>2012-04-14 01:42:46 +0000
committerGreg Clayton <gclayton@apple.com>2012-04-14 01:42:46 +0000
commitd1cf11a74df14374c8be20dd43e805d2dfb1de07 (patch)
tree8d93d9fd95aa1825bf1ae394233bee05208c16f4 /lldb
parentf5c87882a01b733a98a0138688d2ff0c9e586848 (diff)
downloadbcm5719-llvm-d1cf11a74df14374c8be20dd43e805d2dfb1de07.tar.gz
bcm5719-llvm-d1cf11a74df14374c8be20dd43e805d2dfb1de07.zip
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. llvm-svn: 154730
Diffstat (limited to 'lldb')
-rw-r--r--lldb/include/lldb/Host/Host.h8
-rw-r--r--lldb/include/lldb/Target/Process.h5
-rw-r--r--lldb/source/Commands/CommandObjectPlatform.cpp70
-rw-r--r--lldb/source/Host/common/Host.cpp158
-rw-r--r--lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp7
-rw-r--r--lldb/source/Target/Platform.cpp7
-rw-r--r--lldb/source/Target/Process.cpp66
7 files changed, 296 insertions, 25 deletions
diff --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h
index aa1967613b6..e41f15ac3b4 100644
--- a/lldb/include/lldb/Host/Host.h
+++ b/lldb/include/lldb/Host/Host.h
@@ -414,6 +414,14 @@ public:
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);
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index d5804d9c92b..cca72d1043e 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -733,7 +733,10 @@ public:
}
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,
diff --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp
index 9618c6cecb4..e4d02733276 100644
--- a/lldb/source/Commands/CommandObjectPlatform.cpp
+++ b/lldb/source/Commands/CommandObjectPlatform.cpp
@@ -870,6 +870,75 @@ private:
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 @@ CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter) :
LoadSubCommand ("connect", CommandObjectSP (new CommandObjectPlatformConnect (interpreter)));
LoadSubCommand ("disconnect", CommandObjectSP (new CommandObjectPlatformDisconnect (interpreter)));
LoadSubCommand ("process", CommandObjectSP (new CommandObjectPlatformProcess (interpreter)));
+ LoadSubCommand ("shell", CommandObjectSP (new CommandObjectPlatformShell (interpreter)));
}
diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp
index f1fc24f85fd..aeff1fff659 100644
--- a/lldb/source/Host/common/Host.cpp
+++ b/lldb/source/Host/common/Host.cpp
@@ -1239,6 +1239,164 @@ Host::GetDummyTarget (lldb_private::Debugger &debugger)
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)
diff --git a/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp b/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp
index 58200da5182..c1dc25de277 100644
--- a/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp
+++ b/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp
@@ -330,7 +330,12 @@ PlatformLinux::LaunchProcess (ProcessLaunchInfo &launch_info)
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);
diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp
index dcb8ba10c52..03b9d91aed3 100644
--- a/lldb/source/Target/Platform.cpp
+++ b/lldb/source/Target/Platform.cpp
@@ -574,7 +574,12 @@ Platform::LaunchProcess (ProcessLaunchInfo &launch_info)
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;
}
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 93619b062cd..c32b4b1305f 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -303,7 +303,10 @@ ProcessLaunchInfo::FinalizeFileActions (Target *target, bool default_to_use_pty)
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 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, bool local
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;
OpenPOWER on IntegriCloud