diff --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp --- a/lldb/source/Commands/CommandObjectPlatform.cpp +++ b/lldb/source/Commands/CommandObjectPlatform.cpp @@ -1567,6 +1567,9 @@ const char short_option = (char)GetDefinitions()[option_idx].short_option; switch (short_option) { + case 'h': + m_use_host_platform = true; + break; case 't': uint32_t timeout_sec; if (option_arg.getAsInteger(10, timeout_sec)) @@ -1574,7 +1577,7 @@ "could not convert \"%s\" to a numeric value.", option_arg.str().c_str()); else - timeout = std::chrono::seconds(timeout_sec); + m_timeout = std::chrono::seconds(timeout_sec); break; default: llvm_unreachable("Unimplemented option"); @@ -1583,9 +1586,12 @@ return error; } - void OptionParsingStarting(ExecutionContext *execution_context) override {} + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_use_host_platform = false; + } - Timeout timeout = std::chrono::seconds(10); + Timeout m_timeout = std::chrono::seconds(10); + bool m_use_host_platform; }; CommandObjectPlatformShell(CommandInterpreter &interpreter) @@ -1616,8 +1622,14 @@ if (!ParseOptions(args.GetArgs(), result)) return false; + // When called from the `shell` alias with no argument, exit. + if (args.GetRawPart().empty()) + return false; + PlatformSP platform_sp( - GetDebugger().GetPlatformList().GetSelectedPlatform()); + m_options.m_use_host_platform + ? Platform::GetHostPlatform() + : GetDebugger().GetPlatformList().GetSelectedPlatform()); Status error; if (platform_sp) { FileSpec working_dir{}; @@ -1625,7 +1637,7 @@ int status = -1; int signo = -1; error = (platform_sp->RunShellCommand(expr, working_dir, &status, &signo, - &output, m_options.timeout)); + &output, m_options.m_timeout)); if (!output.empty()) result.GetOutputStream().PutCString(output); if (status > 0) { diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -622,6 +622,8 @@ } let Command = "platform shell" in { + def platform_shell_host : Option<"host", "h">, + 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.">; } @@ -701,6 +703,7 @@ Desc<"Set the synchronicity of this command's executions with regard to " "LLDB event system.">; } + let Command = "source info" in { def source_info_count : Option<"count", "c">, Arg<"Count">, Desc<"The number of line entries to display.">; diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -381,6 +381,16 @@ } } + cmd_obj_sp = GetCommandSPExact("platform shell", false); + if (cmd_obj_sp) { + CommandAlias *shell_alias = AddAlias("shell", cmd_obj_sp, " --host --"); + if (shell_alias) { + shell_alias->SetHelp("Run a shell command on the host."); + shell_alias->SetHelpLong(""); + shell_alias->SetSyntax("shell "); + } + } + cmd_obj_sp = GetCommandSPExact("process kill", false); if (cmd_obj_sp) { AddAlias("kill", cmd_obj_sp); diff --git a/lldb/test/API/commands/shell/TestShellCommand.py b/lldb/test/API/commands/shell/TestShellCommand.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/shell/TestShellCommand.py @@ -0,0 +1,45 @@ +""" +Test the lldb host shell command. +""" + + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class ShellCommandTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_help_platform(self): + self.expect("help shell", substrs=["shell ", + "Run a shell command on the host."]) + + @expectedFailureAll(oslist=["windows"]) + @no_debug_info_test + def test_shell(self): + """ Test that the shell command can invoke ls. """ + triple = self.dbg.GetSelectedPlatform().GetTriple() + if re.match(".*-.*-windows", triple): + self.expect("shell dir c:\\", substrs=["Windows", "Program Files"]) + elif re.match(".*-.*-.*-android", triple): + self.expect("shell ls /", + substrs=["cache", "dev", "system"]) + else: + self.expect("shell ls /", substrs=["dev", "tmp", "usr"]) + + @no_debug_info_test + def test_shell_builtin(self): + """ Test a shell built-in command (echo) """ + self.expect("shell echo hello lldb", + substrs=["hello lldb"]) + + @no_debug_info_test + def test_shell_timeout(self): + """ Test a shell built-in command (sleep) that times out """ + self.expect("shell -t 1 -- sleep 3", error=True, substrs=[ + "error: timed out waiting for shell command to complete"])