summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/include/lldb/API/SBCommandInterpreter.h19
-rw-r--r--lldb/include/lldb/Interpreter/CommandInterpreter.h30
-rw-r--r--lldb/lit/Quit/TestQuitExitCode-30.test3
-rw-r--r--lldb/lit/Quit/TestQuitExitCode0.test3
-rw-r--r--lldb/lit/Quit/TestQuitExitCode30.test3
-rw-r--r--lldb/lit/Quit/TestQuitExitCodeHex0.test3
-rw-r--r--lldb/lit/Quit/TestQuitExitCodeHexA.test3
-rw-r--r--lldb/lit/Quit/TestQuitExitCodeImplicit0.test3
-rw-r--r--lldb/lit/Quit/TestQuitExitCodeNonInt.test4
-rw-r--r--lldb/lit/Quit/TestQuitExitCodeTooManyArgs.test4
-rwxr-xr-xlldb/lit/Quit/expect_exit_code.py16
-rw-r--r--lldb/lit/Quit/lit.local.cfg1
-rw-r--r--lldb/packages/Python/lldbsuite/test/quit/TestQuit.py32
-rw-r--r--lldb/scripts/interface/SBCommandInterpreter.i9
-rw-r--r--lldb/source/API/SBCommandInterpreter.cpp17
-rw-r--r--lldb/source/Commands/CommandObjectQuit.cpp38
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp20
-rw-r--r--lldb/tools/driver/Driver.cpp14
-rw-r--r--lldb/tools/driver/Driver.h5
19 files changed, 222 insertions, 5 deletions
diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h
index 928e40bad28..8b9f0659936 100644
--- a/lldb/include/lldb/API/SBCommandInterpreter.h
+++ b/lldb/include/lldb/API/SBCommandInterpreter.h
@@ -208,6 +208,25 @@ public:
void SetPromptOnQuit(bool b);
//----------------------------------------------------------------------
+ /// Sets whether the command interpreter should allow custom exit codes
+ /// for the 'quit' command.
+ //----------------------------------------------------------------------
+ void AllowExitCodeOnQuit(bool allow);
+
+ //----------------------------------------------------------------------
+ /// Returns true if the user has called the 'quit' command with a custom exit
+ /// code.
+ //----------------------------------------------------------------------
+ bool HasCustomQuitExitCode();
+
+ //----------------------------------------------------------------------
+ /// Returns the exit code that the user has specified when running the
+ /// 'quit' command. Returns 0 if the user hasn't called 'quit' at all or
+ /// without a custom exit code.
+ //----------------------------------------------------------------------
+ int GetQuitStatus();
+
+ //----------------------------------------------------------------------
/// Resolve the command just as HandleCommand would, expanding abbreviations
/// and aliases. If successful, result->GetOutput has the full expansion.
//----------------------------------------------------------------------
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 60e67f2271f..0a556cc6b3f 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -455,6 +455,30 @@ public:
void SetPromptOnQuit(bool b);
+ //------------------------------------------------------------------
+ /// Specify if the command interpreter should allow that the user can
+ /// specify a custom exit code when calling 'quit'.
+ //------------------------------------------------------------------
+ void AllowExitCodeOnQuit(bool allow);
+
+ //------------------------------------------------------------------
+ /// Sets the exit code for the quit command.
+ /// @param[in] exit_code
+ /// The exit code that the driver should return on exit.
+ /// @return True if the exit code was successfully set; false if the
+ /// interpreter doesn't allow custom exit codes.
+ /// @see AllowExitCodeOnQuit
+ //------------------------------------------------------------------
+ LLVM_NODISCARD bool SetQuitExitCode(int exit_code);
+
+ //------------------------------------------------------------------
+ /// Returns the exit code that the user has specified when running the
+ /// 'quit' command.
+ /// @param[out] exited
+ /// Set to true if the user has called quit with a custom exit code.
+ //------------------------------------------------------------------
+ int GetQuitExitCode(bool &exited) const;
+
void ResolveCommand(const char *command_line, CommandReturnObject &result);
bool GetStopCmdSourceOnError() const;
@@ -558,6 +582,12 @@ private:
uint32_t m_num_errors;
bool m_quit_requested;
bool m_stopped_for_crash;
+
+ // The exit code the user has requested when calling the 'quit' command.
+ // No value means the user hasn't set a custom exit code so far.
+ llvm::Optional<int> m_quit_exit_code;
+ // If the driver is accepts custom exit codes for the 'quit' command.
+ bool m_allow_exit_code = false;
};
} // namespace lldb_private
diff --git a/lldb/lit/Quit/TestQuitExitCode-30.test b/lldb/lit/Quit/TestQuitExitCode-30.test
new file mode 100644
index 00000000000..0f6eff927f0
--- /dev/null
+++ b/lldb/lit/Quit/TestQuitExitCode-30.test
@@ -0,0 +1,3 @@
+# UNSUPPORTED: windows
+# RUN: python %S/expect_exit_code.py 226 %lldb -b -s %s
+q -30
diff --git a/lldb/lit/Quit/TestQuitExitCode0.test b/lldb/lit/Quit/TestQuitExitCode0.test
new file mode 100644
index 00000000000..c15cb5e9ea7
--- /dev/null
+++ b/lldb/lit/Quit/TestQuitExitCode0.test
@@ -0,0 +1,3 @@
+# UNSUPPORTED: windows
+# RUN: %lldb -b -s %s
+q 0
diff --git a/lldb/lit/Quit/TestQuitExitCode30.test b/lldb/lit/Quit/TestQuitExitCode30.test
new file mode 100644
index 00000000000..b5249400ec2
--- /dev/null
+++ b/lldb/lit/Quit/TestQuitExitCode30.test
@@ -0,0 +1,3 @@
+# UNSUPPORTED: windows
+# RUN: python %S/expect_exit_code.py 30 %lldb -b -s %s
+q 30
diff --git a/lldb/lit/Quit/TestQuitExitCodeHex0.test b/lldb/lit/Quit/TestQuitExitCodeHex0.test
new file mode 100644
index 00000000000..3e1fc5dbeeb
--- /dev/null
+++ b/lldb/lit/Quit/TestQuitExitCodeHex0.test
@@ -0,0 +1,3 @@
+# UNSUPPORTED: windows
+# RUN: %lldb -b -s %s
+q 0x0
diff --git a/lldb/lit/Quit/TestQuitExitCodeHexA.test b/lldb/lit/Quit/TestQuitExitCodeHexA.test
new file mode 100644
index 00000000000..e06c25b0619
--- /dev/null
+++ b/lldb/lit/Quit/TestQuitExitCodeHexA.test
@@ -0,0 +1,3 @@
+# UNSUPPORTED: windows
+# RUN: python %S/expect_exit_code.py 10 %lldb -b -s %s
+q 0xA
diff --git a/lldb/lit/Quit/TestQuitExitCodeImplicit0.test b/lldb/lit/Quit/TestQuitExitCodeImplicit0.test
new file mode 100644
index 00000000000..1a95e8dc0bf
--- /dev/null
+++ b/lldb/lit/Quit/TestQuitExitCodeImplicit0.test
@@ -0,0 +1,3 @@
+# UNSUPPORTED: windows
+# RUN: %lldb -b -s %s
+q
diff --git a/lldb/lit/Quit/TestQuitExitCodeNonInt.test b/lldb/lit/Quit/TestQuitExitCodeNonInt.test
new file mode 100644
index 00000000000..2b54163d648
--- /dev/null
+++ b/lldb/lit/Quit/TestQuitExitCodeNonInt.test
@@ -0,0 +1,4 @@
+# UNSUPPORTED: windows
+# RUN: %lldb -b -s %s 2>&1 | FileCheck %s
+q str
+// CHECK: Couldn't parse 'str'
diff --git a/lldb/lit/Quit/TestQuitExitCodeTooManyArgs.test b/lldb/lit/Quit/TestQuitExitCodeTooManyArgs.test
new file mode 100644
index 00000000000..05be357fd57
--- /dev/null
+++ b/lldb/lit/Quit/TestQuitExitCodeTooManyArgs.test
@@ -0,0 +1,4 @@
+# UNSUPPORTED: windows
+# RUN: %lldb -b -s %s 2>&1 | FileCheck %s
+q 1 2
+// CHECK: Too many arguments for 'quit'
diff --git a/lldb/lit/Quit/expect_exit_code.py b/lldb/lit/Quit/expect_exit_code.py
new file mode 100755
index 00000000000..f4a7590b7d5
--- /dev/null
+++ b/lldb/lit/Quit/expect_exit_code.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python2
+
+import subprocess
+import sys
+
+args = sys.argv
+
+expected_exit_code = args[1]
+
+args = args[2:]
+print("Running " + (" ".join(args)))
+real_exit_code = subprocess.call(args)
+
+if str(real_exit_code) != expected_exit_code:
+ print("Got exit code %d but expected %s" % (real_exit_code, expected_exit_code))
+ exit(1)
diff --git a/lldb/lit/Quit/lit.local.cfg b/lldb/lit/Quit/lit.local.cfg
new file mode 100644
index 00000000000..df9b335dd13
--- /dev/null
+++ b/lldb/lit/Quit/lit.local.cfg
@@ -0,0 +1 @@
+config.suffixes = ['.test']
diff --git a/lldb/packages/Python/lldbsuite/test/quit/TestQuit.py b/lldb/packages/Python/lldbsuite/test/quit/TestQuit.py
new file mode 100644
index 00000000000..28c05b833d6
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/quit/TestQuit.py
@@ -0,0 +1,32 @@
+"""
+Test lldb's quit command.
+"""
+
+from __future__ import print_function
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class QuitCommandTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ @no_debug_info_test
+ def test_quit_exit_code_disallow(self):
+ self.ci.AllowExitCodeOnQuit(False)
+ self.expect(
+ "quit 20",
+ substrs=[
+ "error: The current driver doesn't allow custom exit codes for the quit command"],
+ error=True)
+ self.assertFalse(self.ci.HasCustomQuitExitCode())
+
+ @no_debug_info_test
+ def test_quit_exit_code_allow(self):
+ self.ci.AllowExitCodeOnQuit(True)
+ self.runCmd("quit 10", check=False)
+ self.assertTrue(self.ci.HasCustomQuitExitCode())
+ self.assertEqual(self.ci.GetQuitStatus(), 10)
diff --git a/lldb/scripts/interface/SBCommandInterpreter.i b/lldb/scripts/interface/SBCommandInterpreter.i
index 255e8ba4496..09e7c9df736 100644
--- a/lldb/scripts/interface/SBCommandInterpreter.i
+++ b/lldb/scripts/interface/SBCommandInterpreter.i
@@ -156,6 +156,15 @@ public:
SetPromptOnQuit(bool b);
void
+ AllowExitCodeOnQuit(bool b);
+
+ bool
+ HasCustomQuitExitCode();
+
+ int
+ GetQuitStatus();
+
+ void
ResolveCommand(const char *command_line, SBCommandReturnObject &result);
bool
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index 1f4f6b886a1..cbb514abb6f 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -379,6 +379,23 @@ void SBCommandInterpreter::SetPromptOnQuit(bool b) {
m_opaque_ptr->SetPromptOnQuit(b);
}
+void SBCommandInterpreter::AllowExitCodeOnQuit(bool allow) {
+ if (m_opaque_ptr)
+ m_opaque_ptr->AllowExitCodeOnQuit(allow);
+}
+
+bool SBCommandInterpreter::HasCustomQuitExitCode() {
+ bool exited = false;
+ if (m_opaque_ptr)
+ m_opaque_ptr->GetQuitExitCode(exited);
+ return exited;
+}
+
+int SBCommandInterpreter::GetQuitStatus() {
+ bool exited = false;
+ return (m_opaque_ptr ? m_opaque_ptr->GetQuitExitCode(exited) : 0);
+}
+
void SBCommandInterpreter::ResolveCommand(const char *command_line,
SBCommandReturnObject &result) {
result.Clear();
diff --git a/lldb/source/Commands/CommandObjectQuit.cpp b/lldb/source/Commands/CommandObjectQuit.cpp
index 071deba6c5a..37ed12be358 100644
--- a/lldb/source/Commands/CommandObjectQuit.cpp
+++ b/lldb/source/Commands/CommandObjectQuit.cpp
@@ -16,6 +16,7 @@
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Target/Process.h"
+#include "lldb/Utility/StreamString.h"
using namespace lldb;
using namespace lldb_private;
@@ -26,7 +27,7 @@ using namespace lldb_private;
CommandObjectQuit::CommandObjectQuit(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "quit", "Quit the LLDB debugger.",
- "quit") {}
+ "quit [exit-code]") {}
CommandObjectQuit::~CommandObjectQuit() {}
@@ -77,6 +78,41 @@ bool CommandObjectQuit::DoExecute(Args &command, CommandReturnObject &result) {
return false;
}
}
+
+ if (command.GetArgumentCount() > 1) {
+ result.AppendError("Too many arguments for 'quit'. Only an optional exit "
+ "code is allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() > 1) {
+ result.AppendError("Too many arguments for 'quit'. Only an optional exit "
+ "code is allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // We parse the exit code argument if there is one.
+ if (command.GetArgumentCount() == 1) {
+ llvm::StringRef arg = command.GetArgumentAtIndex(0);
+ int exit_code;
+ if (arg.getAsInteger(/*autodetect radix*/ 0, exit_code)) {
+ lldb_private::StreamString s;
+ std::string arg_str = arg.str();
+ s.Printf("Couldn't parse '%s' as integer for exit code.", arg_str.data());
+ result.AppendError(s.GetString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (!m_interpreter.SetQuitExitCode(exit_code)) {
+ result.AppendError("The current driver doesn't allow custom exit codes"
+ " for the quit command.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
const uint32_t event_type =
CommandInterpreter::eBroadcastBitQuitCommandReceived;
m_interpreter.BroadcastEvent(event_type);
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index d2014afa869..d6d271acc35 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -144,6 +144,26 @@ void CommandInterpreter::SetPromptOnQuit(bool b) {
m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
}
+void CommandInterpreter::AllowExitCodeOnQuit(bool allow) {
+ m_allow_exit_code = allow;
+ if (!allow)
+ m_quit_exit_code.reset();
+}
+
+bool CommandInterpreter::SetQuitExitCode(int exit_code) {
+ if (!m_allow_exit_code)
+ return false;
+ m_quit_exit_code = exit_code;
+ return true;
+}
+
+int CommandInterpreter::GetQuitExitCode(bool &exited) const {
+ exited = m_quit_exit_code.hasValue();
+ if (exited)
+ return *m_quit_exit_code;
+ return 0;
+}
+
void CommandInterpreter::ResolveCommand(const char *command_line,
CommandReturnObject &result) {
std::string command = command_line;
diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp
index 58296b2eee8..e28093482f3 100644
--- a/lldb/tools/driver/Driver.cpp
+++ b/lldb/tools/driver/Driver.cpp
@@ -962,7 +962,7 @@ std::string EscapeString(std::string arg) {
return '"' + arg + '"';
}
-void Driver::MainLoop() {
+int Driver::MainLoop() {
if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) {
g_old_stdin_termios_is_valid = true;
atexit(reset_stdin_termios);
@@ -1001,6 +1001,10 @@ void Driver::MainLoop() {
result.PutOutput(m_debugger.GetOutputFileHandle());
}
+ // We allow the user to specify an exit code when calling quit which we will
+ // return when exiting.
+ m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true);
+
// Now we handle options we got from the command line
SBStream commands_stream;
@@ -1159,7 +1163,9 @@ void Driver::MainLoop() {
reset_stdin_termios();
fclose(stdin);
+ int exit_code = sb_interpreter.GetQuitStatus();
SBDebugger::Destroy(m_debugger);
+ return exit_code;
}
void Driver::ResizeWindow(unsigned short col) {
@@ -1237,6 +1243,7 @@ main(int argc, char const *argv[])
signal(SIGCONT, sigcont_handler);
#endif
+ int exit_code = 0;
// Create a scope for driver so that the driver object will destroy itself
// before SBDebugger::Terminate() is called.
{
@@ -1245,14 +1252,15 @@ main(int argc, char const *argv[])
bool exiting = false;
SBError error(driver.ParseArgs(argc, argv, stdout, exiting));
if (error.Fail()) {
+ exit_code = 1;
const char *error_cstr = error.GetCString();
if (error_cstr)
::fprintf(stderr, "error: %s\n", error_cstr);
} else if (!exiting) {
- driver.MainLoop();
+ exit_code = driver.MainLoop();
}
}
SBDebugger::Terminate();
- return 0;
+ return exit_code;
}
diff --git a/lldb/tools/driver/Driver.h b/lldb/tools/driver/Driver.h
index 2be697ccc44..34746a23999 100644
--- a/lldb/tools/driver/Driver.h
+++ b/lldb/tools/driver/Driver.h
@@ -37,7 +37,10 @@ public:
virtual ~Driver();
- void MainLoop();
+ /// Runs the main loop.
+ ///
+ /// @return The exit code that the process should return.
+ int MainLoop();
lldb::SBError ParseArgs(int argc, const char *argv[], FILE *out_fh,
bool &do_exit);
OpenPOWER on IntegriCloud