55 changes: 39 additions & 16 deletions lldb/source/Core/IOHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,11 +378,11 @@ IOHandlerEditline::~IOHandlerEditline ()


bool
IOHandlerEditline::GetLine (std::string &line)
IOHandlerEditline::GetLine (std::string &line, bool &interrupted)
{
if (m_editline_ap)
{
return m_editline_ap->GetLine(line).Success();
return m_editline_ap->GetLine(line, interrupted).Success();
}
else
{
Expand Down Expand Up @@ -518,13 +518,13 @@ IOHandlerEditline::SetBaseLineNumber (uint32_t line)

}
bool
IOHandlerEditline::GetLines (StringList &lines)
IOHandlerEditline::GetLines (StringList &lines, bool &interrupted)
{
bool success = false;
if (m_editline_ap)
{
std::string end_token;
success = m_editline_ap->GetLines(end_token, lines).Success();
success = m_editline_ap->GetLines(end_token, lines, interrupted).Success();
}
else
{
Expand All @@ -541,11 +541,19 @@ IOHandlerEditline::GetLines (StringList &lines)
::fprintf(out, "%u", m_base_line_number + (uint32_t)lines.GetSize());
}

if (GetLine(line))
bool interrupted = false;
if (GetLine(line, interrupted))
{
lines.AppendString(line);
Error error;
lines_status = m_delegate.IOHandlerLinesUpdated(*this, lines, lines.GetSize() - 1, error);
if (interrupted)
{
lines_status = LineStatus::Done;
}
else
{
lines.AppendString(line);
Error error;
lines_status = m_delegate.IOHandlerLinesUpdated(*this, lines, lines.GetSize() - 1, error);
}
}
else
{
Expand All @@ -566,13 +574,21 @@ IOHandlerEditline::Run ()
std::string line;
while (IsActive())
{
bool interrupted = false;
if (m_multi_line)
{
StringList lines;
if (GetLines (lines))
if (GetLines (lines, interrupted))
{
line = lines.CopyList();
m_delegate.IOHandlerInputComplete(*this, line);
if (interrupted)
{
m_done = true;
}
else
{
line = lines.CopyList();
m_delegate.IOHandlerInputComplete(*this, line);
}
}
else
{
Expand All @@ -581,9 +597,10 @@ IOHandlerEditline::Run ()
}
else
{
if (GetLine(line))
if (GetLine(line, interrupted))
{
m_delegate.IOHandlerInputComplete(*this, line);
if (!interrupted)
m_delegate.IOHandlerInputComplete(*this, line);
}
else
{
Expand Down Expand Up @@ -628,11 +645,16 @@ IOHandlerEditline::Cancel ()
m_editline_ap->Interrupt ();
}

void
bool
IOHandlerEditline::Interrupt ()
{
// Let the delgate handle it first
if (m_delegate.IOHandlerInterrupt(*this))
return true;

if (m_editline_ap)
m_editline_ap->Interrupt();
return m_editline_ap->Interrupt();
return false;
}

void
Expand Down Expand Up @@ -5454,9 +5476,10 @@ IOHandlerCursesGUI::Cancel ()
{
}

void
bool
IOHandlerCursesGUI::Interrupt ()
{
return false;
}


Expand Down
92 changes: 65 additions & 27 deletions lldb/source/Host/common/Editline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ Editline::Editline (const char *prog, // prog can't be NULL
m_line_offset (0),
m_lines_curr_line (0),
m_lines_max_line (0),
m_file (fileno(fin), false),
m_prompt_with_line_numbers (false),
m_getting_line (false),
m_got_eof (false),
Expand All @@ -190,6 +191,7 @@ Editline::Editline (const char *prog, // prog can't be NULL
{
m_editline = ::el_init("lldb-tmp", fin, fout, ferr);
}

if (prompt && prompt[0])
SetPrompt (prompt);

Expand Down Expand Up @@ -324,7 +326,7 @@ Editline::PrivateGetLine(std::string &line)
llvm::StringRef line_ref (line_cstr);
line_ref = line_ref.rtrim("\n\r");

if (!line_ref.empty())
if (!line_ref.empty() && !m_interrupted)
{
// We didn't strip the newlines, we just adjusted the length, and
// we want to add the history item with the newlines
Expand All @@ -345,9 +347,10 @@ Editline::PrivateGetLine(std::string &line)


Error
Editline::GetLine(std::string &line)
Editline::GetLine(std::string &line, bool &interrupted)
{
Error error;
interrupted = false;
line.clear();

// Set arrow key bindings for up and down arrows for single line
Expand All @@ -371,6 +374,8 @@ Editline::GetLine(std::string &line)
m_getting_line = false;
}

interrupted = m_interrupted;

if (m_got_eof && line.empty())
{
// Only set the error if we didn't get an error back from PrivateGetLine()
Expand All @@ -397,9 +402,10 @@ Editline::Push (const char *bytes, size_t len)


Error
Editline::GetLines(const std::string &end_line, StringList &lines)
Editline::GetLines(const std::string &end_line, StringList &lines, bool &interrupted)
{
Error error;
interrupted = false;
if (m_getting_line)
{
error.SetErrorString("already getting a line");
Expand Down Expand Up @@ -436,6 +442,11 @@ Editline::GetLines(const std::string &end_line, StringList &lines)
{
line_status = LineStatus::Error;
}
else if (m_interrupted)
{
interrupted = true;
line_status = LineStatus::Done;
}
else
{
switch (m_lines_command)
Expand Down Expand Up @@ -500,7 +511,7 @@ Editline::GetLines(const std::string &end_line, StringList &lines)

// If we have a callback, call it one more time to let the
// user know the lines are complete
if (m_line_complete_callback)
if (m_line_complete_callback && !interrupted)
m_line_complete_callback (this,
lines,
UINT32_MAX,
Expand Down Expand Up @@ -755,39 +766,65 @@ Editline::GetCharFromInputFileCallback (EditLine *e, char *c)
Editline *editline = GetClientData (e);
if (editline && editline->m_got_eof == false)
{
FILE *f = editline->GetInputFile();
if (f == NULL)
{
editline->m_got_eof = true;
return 0;
}


while (1)
{
errno = 0;
char ch = ::fgetc(editline->GetInputFile());
if (ch == '\x04')
lldb::ConnectionStatus status = eConnectionStatusSuccess;
char ch = 0;
if (editline->m_file.Read(&ch, 1, UINT32_MAX, status, NULL))
{
// Only turn a CTRL+D into a EOF if we receive the
// CTRL+D an empty line, otherwise it will forward
// delete the character at the cursor
const LineInfo *line_info = ::el_line(e);
if (line_info != NULL &&
line_info->buffer == line_info->cursor &&
line_info->cursor == line_info->lastchar)
if (ch == '\x04')
{
ch = EOF;
errno = 0;
// Only turn a CTRL+D into a EOF if we receive the
// CTRL+D an empty line, otherwise it will forward
// delete the character at the cursor
const LineInfo *line_info = ::el_line(e);
if (line_info != NULL &&
line_info->buffer == line_info->cursor &&
line_info->cursor == line_info->lastchar)
{
ch = EOF;
}
}
}

if (ch == EOF)
{
if (errno == EINTR)
continue;
else

if (ch == EOF)
{
editline->m_got_eof = true;
break;
}
else
{
*c = ch;
return 1;
}
}
else
{
*c = ch;
return 1;
switch (status)
{
case eConnectionStatusInterrupted:
editline->m_interrupted = true;
*c = '\n';
return 1;

case eConnectionStatusSuccess: // Success
break;

case eConnectionStatusError: // Check GetError() for details
case eConnectionStatusTimedOut: // Request timed out
case eConnectionStatusEndOfFile: // End-of-file encountered
case eConnectionStatusNoConnection: // No connection
case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection
editline->m_got_eof = true;
break;
}
}
}
}
Expand All @@ -813,10 +850,11 @@ Editline::Refresh()
::el_set (m_editline, EL_REFRESH);
}

void
bool
Editline::Interrupt ()
{
m_interrupted = true;
if (m_getting_line || m_lines_curr_line > 0)
el_insertstr(m_editline, "\n"); // True to force the line to complete itself so we get exit from el_gets()
return m_file.InterruptRead();
return false; // Interrupt not handled as we weren't getting a line or lines
}
19 changes: 19 additions & 0 deletions lldb/source/Interpreter/CommandInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

#include "lldb/Core/Debugger.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/State.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/Timer.h"
Expand Down Expand Up @@ -3051,6 +3052,24 @@ CommandInterpreter::IOHandlerInputComplete (IOHandler &io_handler, std::string &
}
}

bool
CommandInterpreter::IOHandlerInterrupt (IOHandler &io_handler)
{
ExecutionContext exe_ctx (GetExecutionContext());
Process *process = exe_ctx.GetProcessPtr();

if (process)
{
StateType state = process->GetState();
if (StateIsRunningState(state))
{
process->Halt();
return true; // Don't do any updating when we are running
}
}
return false;
}

void
CommandInterpreter::GetLLDBCommandsFromIOHandler (const char *prompt,
IOHandlerDelegate &delegate,
Expand Down
37 changes: 26 additions & 11 deletions lldb/source/Interpreter/ScriptInterpreterPython.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ ScriptInterpreterPython::Locker::DoAcquireLock()
m_GILState = PyGILState_Ensure();
if (log)
log->Printf("Ensured PyGILState. Previous state = %slocked\n", m_GILState == PyGILState_UNLOCKED ? "un" : "");

// we need to save the thread state when we first start the command
// because we might decide to interrupt it while some action is taking
// place outside of Python (e.g. printing to screen, waiting for the network, ...)
// in that case, _PyThreadState_Current will be NULL - and we would be unable
// to set the asynchronous exception - not a desirable situation
m_python_interpreter->SetThreadState (_PyThreadState_Current);
return true;
}

Expand Down Expand Up @@ -781,10 +788,27 @@ class IOHandlerPythonInterpreter :

}

virtual void
virtual bool
Interrupt ()
{

Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT));

PyThreadState* state = _PyThreadState_Current;
if (!state)
state = m_python->GetThreadState();
if (state)
{
long tid = state->thread_id;
_PyThreadState_Current = state;
int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt);
if (log)
log->Printf("ScriptInterpreterPython::NonInteractiveInputReaderCallback, eInputReaderInterrupt, tid = %ld, num_threads = %d, state = %p",
tid,num_threads,state);
}
else if (log)
log->Printf("ScriptInterpreterPython::NonInteractiveInputReaderCallback, eInputReaderInterrupt, state = NULL");

return false;
}

virtual void
Expand Down Expand Up @@ -2433,15 +2457,6 @@ ScriptInterpreterPython::RunScriptBasedCommand(const char* impl_function,
SynchronicityHandler synch_handler(debugger_sp,
synchronicity);

// we need to save the thread state when we first start the command
// because we might decide to interrupt it while some action is taking
// place outside of Python (e.g. printing to screen, waiting for the network, ...)
// in that case, _PyThreadState_Current will be NULL - and we would be unable
// to set the asynchronous exception - not a desirable situation
m_command_thread_state = _PyThreadState_Current;

//PythonInputReaderManager py_input(this);

ret_val = g_swig_call_command (impl_function,
m_dictionary_name.c_str(),
debugger_sp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ CommunicationKDP::WaitForPacketWithTimeoutMicroSecondsNoLock (DataExtractor &pac
{
switch (status)
{
case eConnectionStatusInterrupted:
case eConnectionStatusTimedOut:
timed_out = true;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtrac
switch (status)
{
case eConnectionStatusTimedOut:
case eConnectionStatusInterrupted:
timed_out = true;
break;
case eConnectionStatusSuccess:
Expand Down
3 changes: 2 additions & 1 deletion lldb/source/Target/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4935,7 +4935,7 @@ class IOHandlerProcessSTDIO :
m_pipe_write.Write (&ch, n);
}

virtual void
virtual bool
Interrupt ()
{
#ifdef _MSC_VER
Expand All @@ -4953,6 +4953,7 @@ class IOHandlerProcessSTDIO :
char ch = 'i'; // Send 'i' for interrupt
m_pipe_write.Write (&ch, n);
#endif
return true;
}

virtual void
Expand Down