Add the ability to catch and do the right thing with Interrupts (often control-c)
and end-of-file (often control-d).
git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@119837 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/API/SBDebugger.cpp b/source/API/SBDebugger.cpp
index 1721d24..2777741 100644
--- a/source/API/SBDebugger.cpp
+++ b/source/API/SBDebugger.cpp
@@ -659,6 +659,20 @@
}
void
+SBDebugger::DispatchInputInterrupt ()
+{
+ if (m_opaque_sp)
+ m_opaque_sp->DispatchInputInterrupt ();
+}
+
+void
+SBDebugger::DispatchInputEndOfFile ()
+{
+ if (m_opaque_sp)
+ m_opaque_sp->DispatchInputEndOfFile ();
+}
+
+void
SBDebugger::PushInputReader (SBInputReader &reader)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
@@ -837,3 +851,11 @@
return true;
}
+
+lldb::user_id_t
+SBDebugger::GetID()
+{
+ if (m_opaque_sp)
+ return m_opaque_sp->GetID();
+ return LLDB_INVALID_UID;
+}
diff --git a/source/Commands/CommandObjectBreakpointCommand.cpp b/source/Commands/CommandObjectBreakpointCommand.cpp
index e8bd399..aad7158 100644
--- a/source/Commands/CommandObjectBreakpointCommand.cpp
+++ b/source/Commands/CommandObjectBreakpointCommand.cpp
@@ -468,6 +468,29 @@
}
break;
+ case eInputReaderInterrupt:
+ {
+ // Finish, and cancel the breakpoint command.
+ reader.SetIsDone (true);
+ BreakpointOptions *bp_options = (BreakpointOptions *) baton;
+ if (bp_options)
+ {
+ Baton *bp_options_baton = bp_options->GetBaton ();
+ if (bp_options_baton)
+ {
+ ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->user_source.Clear();
+ ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->script_source.Clear();
+ }
+ }
+ ::fprintf (out_fh, "Warning: No command attached to breakpoint.\n");
+ ::fflush (out_fh);
+ }
+ break;
+
+ case eInputReaderEndOfFile:
+ reader.SetIsDone (true);
+ break;
+
case eInputReaderDone:
break;
}
diff --git a/source/Commands/CommandObjectExpression.cpp b/source/Commands/CommandObjectExpression.cpp
index 436b2e7..32236f8 100644
--- a/source/Commands/CommandObjectExpression.cpp
+++ b/source/Commands/CommandObjectExpression.cpp
@@ -199,7 +199,18 @@
// ::fprintf (out_fh, "%3u: ", cmd_object_expr->m_expr_line_count);
break;
+ case eInputReaderInterrupt:
+ cmd_object_expr->m_expr_lines.clear();
+ reader.SetIsDone (true);
+ reader.GetDebugger().GetOutputStream().Printf("%s\n", "Expression evaluation cancelled.");
+ break;
+
+ case eInputReaderEndOfFile:
+ reader.SetIsDone (true);
+ break;
+
case eInputReaderDone:
+ if (cmd_object_expr->m_expr_lines.size() > 0)
{
cmd_object_expr->EvaluateExpression (cmd_object_expr->m_expr_lines.c_str(),
reader.GetDebugger().GetOutputStream(),
diff --git a/source/Core/Communication.cpp b/source/Core/Communication.cpp
index f6c689c..22a9f9a 100644
--- a/source/Core/Communication.cpp
+++ b/source/Core/Communication.cpp
@@ -260,12 +260,13 @@
}
void
-Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast)
+Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast, ConnectionStatus status)
{
lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION,
"%p Communication::AppendBytesToCache (src = %p, src_len = %zu, broadcast = %i)",
this, bytes, len, broadcast);
- if (bytes == NULL || len == 0)
+ if ((bytes == NULL || len == 0)
+ && (status != eConnectionStatusEndOfFile))
return;
if (m_callback)
{
@@ -319,12 +320,16 @@
{
size_t bytes_read = comm->ReadFromConnection (buf, sizeof(buf), status, &error);
if (bytes_read > 0)
- comm->AppendBytesToCache (buf, bytes_read, true);
+ comm->AppendBytesToCache (buf, bytes_read, true, status);
+ else if ((bytes_read == 0)
+ && status == eConnectionStatusEndOfFile)
+ comm->AppendBytesToCache (buf, bytes_read, true, status);
}
switch (status)
{
case eConnectionStatusSuccess:
+ case eConnectionStatusEndOfFile:
break;
case eConnectionStatusNoConnection: // No connection
diff --git a/source/Core/ConnectionFileDescriptor.cpp b/source/Core/ConnectionFileDescriptor.cpp
index 96d70bc..bdf550e 100644
--- a/source/Core/ConnectionFileDescriptor.cpp
+++ b/source/Core/ConnectionFileDescriptor.cpp
@@ -156,8 +156,18 @@
ssize_t bytes_read = ::read (m_fd, dst, dst_len);
if (bytes_read == 0)
{
- error.SetErrorStringWithFormat("End-of-file.\n");
- status = eConnectionStatusLostConnection;
+ // 'read' did not return an error, but it didn't return any bytes either ==> End-of-File.
+ // If the file descriptor is still valid, then we don't return an error; otherwise we do.
+ // This allows whoever called us to act on the end-of-file, with a valid file descriptor, if they wish.
+ if (fcntl (m_fd, F_GETFL, 0) >= 0)
+ {
+ error.Clear(); // End-of-file, but not an error. Pass along for the end-of-file handlers.
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("End-of-file.\n");
+ }
+ status = eConnectionStatusEndOfFile;
}
else if (bytes_read < 0)
{
diff --git a/source/Core/Debugger.cpp b/source/Core/Debugger.cpp
index 617edd2..a06095f 100644
--- a/source/Core/Debugger.cpp
+++ b/source/Core/Debugger.cpp
@@ -338,20 +338,57 @@
void
Debugger::DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len)
{
- ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len);
-}
+ if (bytes_len > 0)
+ ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len);
+ else
+ ((Debugger *)baton)->DispatchInputEndOfFile ();
+}
void
Debugger::DispatchInput (const char *bytes, size_t bytes_len)
{
-// if (bytes == NULL || bytes_len == 0)
-// return;
+ if (bytes == NULL || bytes_len == 0)
+ return;
WriteToDefaultReader (bytes, bytes_len);
}
void
+Debugger::DispatchInputInterrupt ()
+{
+ m_input_reader_data.clear();
+
+ if (!m_input_readers.empty())
+ {
+ while (CheckIfTopInputReaderIsDone ()) ;
+
+ InputReaderSP reader_sp(m_input_readers.top());
+ if (reader_sp)
+ reader_sp->Notify (eInputReaderInterrupt);
+
+ while (CheckIfTopInputReaderIsDone ()) ;
+ }
+}
+
+void
+Debugger::DispatchInputEndOfFile ()
+{
+ m_input_reader_data.clear();
+
+ if (!m_input_readers.empty())
+ {
+ while (CheckIfTopInputReaderIsDone ()) ;
+
+ InputReaderSP reader_sp(m_input_readers.top());
+ if (reader_sp)
+ reader_sp->Notify (eInputReaderEndOfFile);
+
+ while (CheckIfTopInputReaderIsDone ()) ;
+ }
+}
+
+void
Debugger::WriteToDefaultReader (const char *bytes, size_t bytes_len)
{
if (bytes && bytes_len)
diff --git a/source/Core/InputReader.cpp b/source/Core/InputReader.cpp
index 06cfb5b..b8a61c2 100644
--- a/source/Core/InputReader.cpp
+++ b/source/Core/InputReader.cpp
@@ -324,6 +324,10 @@
m_active = false;
break;
+ case eInputReaderInterrupt:
+ case eInputReaderEndOfFile:
+ break;
+
case eInputReaderGotToken:
return; // We don't notify the tokens here, it is done in HandleRawBytes
}
diff --git a/source/Interpreter/CommandInterpreter.cpp b/source/Interpreter/CommandInterpreter.cpp
index 0dc4a34..86a635a 100644
--- a/source/Interpreter/CommandInterpreter.cpp
+++ b/source/Interpreter/CommandInterpreter.cpp
@@ -860,6 +860,12 @@
}
break;
+ case eInputReaderInterrupt:
+ case eInputReaderEndOfFile:
+ *response_ptr = false; // Assume ^C or ^D means cancel the proposed action
+ reader.SetIsDone (true);
+ break;
+
case eInputReaderDone:
break;
}
diff --git a/source/Interpreter/ScriptInterpreterPython.cpp b/source/Interpreter/ScriptInterpreterPython.cpp
index 1927531..1e3a308 100644
--- a/source/Interpreter/ScriptInterpreterPython.cpp
+++ b/source/Interpreter/ScriptInterpreterPython.cpp
@@ -272,11 +272,6 @@
PyRun_SimpleString (run_string.GetData());
PyRun_SimpleString ("sys.stdin = new_stdin");
- PyRun_SimpleString ("new_mode = tcgetattr(new_stdin)");
- PyRun_SimpleString ("new_mode[3] = new_mode[3] | ECHO | ICANON");
- PyRun_SimpleString ("new_mode[6][VEOF] = 255");
- PyRun_SimpleString ("tcsetattr (new_stdin, TCSANOW, new_mode)");
-
run_string.Clear();
run_string.Printf ("lldb.debugger_unique_id = %d", interpreter.GetDebugger().GetID());
PyRun_SimpleString (run_string.GetData());
@@ -302,26 +297,10 @@
{
int success;
-
- // Save the current input file handle state before executing the command.
- int input_fd;
- struct termios tmp_termios;
- bool valid_termios = false;
- FILE *input_fh = m_interpreter.GetDebugger().GetInputFileHandle();
- if (input_fh != NULL)
- {
- input_fd = ::fileno (input_fh);
- valid_termios = ::tcgetattr (input_fd, &tmp_termios) == 0;
- }
-
success = PyRun_SimpleString (command);
if (success == 0)
return true;
- // Restore the input file handle state after executing the command.
- if (valid_termios)
- ::tcsetattr (input_fd, TCSANOW, &tmp_termios);
-
// The one-liner failed. Append the error message.
if (result)
result->AppendErrorWithFormat ("python failed attempting to evaluate '%s'\n", command);
@@ -366,12 +345,6 @@
script_interpreter->m_termios_valid = ::tcgetattr (input_fd, &script_interpreter->m_termios) == 0;
- if (script_interpreter->m_termios_valid)
- {
- struct termios tmp_termios = script_interpreter->m_termios;
- tmp_termios.c_cc[VEOF] = _POSIX_VDISABLE;
- ::tcsetattr (input_fd, TCSANOW, &tmp_termios);
- }
char error_str[1024];
if (script_interpreter->m_embedded_python_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, error_str,
sizeof(error_str)))
@@ -411,6 +384,14 @@
case eInputReaderReactivate:
break;
+
+ case eInputReaderInterrupt:
+ ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "raise KeyboardInterrupt\n", 24);
+ break;
+
+ case eInputReaderEndOfFile:
+ ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "quit()\n", 7);
+ break;
case eInputReaderGotToken:
if (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor() != -1)
@@ -724,6 +705,17 @@
}
break;
+ case eInputReaderEndOfFile:
+ case eInputReaderInterrupt:
+ // Control-c (SIGINT) & control-d both mean finish & exit.
+ reader.SetIsDone(true);
+
+ // Control-c (SIGINT) ALSO means cancel; do NOT create a breakpoint command.
+ if (notification == eInputReaderInterrupt)
+ commands_in_progress.Clear();
+
+ // Fall through here...
+
case eInputReaderDone:
{
BreakpointOptions *bp_options = (BreakpointOptions *)baton;
@@ -843,6 +835,10 @@
int num_lines = user_input.GetSize ();
StreamString sstr;
+ // Check to see if we have any data; if not, just return.
+ if (user_input.GetSize() == 0)
+ return false;
+
// Take what the user wrote, wrap it all up inside one big auto-generated Python function, passing in the
// frame and breakpoint location as parameters to the function.
@@ -933,10 +929,6 @@
PyRun_SimpleString ("save_stdin = sys.stdin");
run_string.Printf ("sys.stdin = open ('%s', 'r')", pty_slave_name);
PyRun_SimpleString (run_string.GetData());
- PyRun_SimpleString ("new_mode = tcgetattr(sys.stdin)");
- PyRun_SimpleString ("new_mode[3] = new_mode[3] | ECHO | ICANON");
- PyRun_SimpleString ("new_mode[6][VEOF] = 255");
- PyRun_SimpleString ("tcsetattr (sys.stdin, TCSANOW, new_mode)");
// The following call drops into the embedded interpreter loop and stays there until the
// user chooses to exit from the Python interpreter.
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
index ee03424..63fb357 100644
--- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -551,7 +551,8 @@
}
void
-GDBRemoteCommunication::AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast)
+GDBRemoteCommunication::AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast,
+ ConnectionStatus status)
{
// Put the packet data into the buffer in a thread safe fashion
Mutex::Locker locker(m_bytes_mutex);
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
index ac49bf2..e7ef38a 100644
--- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -110,7 +110,7 @@
// Communication overrides
//------------------------------------------------------------------
virtual void
- AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast);
+ AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast, lldb::ConnectionStatus status);
lldb::pid_t
diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp
index e50ecf2..cebec5b 100644
--- a/source/Target/Process.cpp
+++ b/source/Target/Process.cpp
@@ -1478,10 +1478,6 @@
}
else
{
- // Since we are eating the event, we need to update our state
- // otherwise the process state will not match reality...
- SetPublicState(state);
-
if (StateIsStoppedState (state))
{
// We caused the process to interrupt itself, so mark this
@@ -1508,7 +1504,7 @@
// stopped the process, intercepted the event and set the interrupted
// bool in the event.
if (event_sp)
- BroadcastEvent(event_sp);
+ m_private_state_broadcaster.BroadcastEvent(event_sp);
}
return error;
@@ -2175,6 +2171,14 @@
}
break;
+ case eInputReaderInterrupt:
+ process->Halt ();
+ break;
+
+ case eInputReaderEndOfFile:
+ process->AppendSTDOUT ("^D", 2);
+ break;
+
case eInputReaderDone:
break;