Improve LLDB prompt handling

Summary:
There is an issue in lldb where the command prompt can appear at the wrong time. The partial fix
we have in for this is not working all the time and is introducing unnecessary delays. This
change does:
- Change Process:SyncIOHandler to use integer start id's for synchronization to avoid it being
  confused by quick start-stop cycles. I picked this up from a suggested patch by Greg to
  lldb-dev.
- coordinates printing of asynchronous text with the iohandlers. This is also based on a
  (different) Greg's patch, but I have added stronger synchronization to it to avoid races.

Together, these changes solve the prompt problem for me on linux (both with and without libedit).
I think they should behave similarly on Mac and FreeBSD and I think they will not make matters
worse for windows.

Test Plan: Prompt comes out alright. All tests still pass on linux.

Reviewers: clayborg, emaste, zturner

Subscribers: lldb-commits

Differential Revision: http://reviews.llvm.org/D9823

llvm-svn: 238313
This commit is contained in:
Pavel Labath 2015-05-27 12:40:32 +00:00
parent bed77de002
commit 4446487d71
13 changed files with 317 additions and 407 deletions

View file

@ -210,6 +210,9 @@ public:
bool
IsTopIOHandler (const lldb::IOHandlerSP& reader_sp);
void
PrintAsync (const char *s, size_t len, bool is_stdout);
ConstString
GetTopIOHandlerControlSequence(char ch);
@ -219,12 +222,6 @@ public:
const char *
GetIOHandlerHelpPrologue();
bool
HideTopIOHandler();
void
RefreshTopIOHandler();
static lldb::DebuggerSP
FindDebuggerWithID (lldb::user_id_t id);

View file

@ -67,18 +67,6 @@ namespace lldb_private {
virtual void
Run () = 0;
// Hide any characters that have been displayed so far so async
// output can be displayed. Refresh() will be called after the
// output has been displayed.
virtual void
Hide () = 0;
// Called when the async output has been received in order to update
// the input reader (refresh the prompt and redisplay any current
// line(s) that are being edited
virtual void
Refresh () = 0;
// Called when an input reader should relinquish its control so another
// can be pushed onto the IO handler stack, or so the current IO
// handler can pop itself off the stack
@ -246,7 +234,14 @@ namespace lldb_private {
void
WaitForPop ();
virtual void
PrintAsync (Stream *stream, const char *s, size_t len)
{
stream->Write (s, len);
stream->Flush();
}
protected:
Debugger &m_debugger;
lldb::StreamFileSP m_input_sp;
@ -448,17 +443,17 @@ namespace lldb_private {
{
}
virtual ConstString
IOHandlerGetControlSequence (char ch)
ConstString
IOHandlerGetControlSequence (char ch) override
{
if (ch == 'd')
return ConstString (m_end_line + "\n");
return ConstString();
}
virtual bool
bool
IOHandlerIsInputComplete (IOHandler &io_handler,
StringList &lines)
StringList &lines) override
{
// Determine whether the end of input signal has been entered
const size_t num_lines = lines.GetSize();
@ -507,53 +502,47 @@ namespace lldb_private {
virtual
~IOHandlerEditline ();
virtual void
Run ();
void
Run () override;
virtual void
Hide ();
void
Cancel () override;
virtual void
Refresh ();
virtual void
Cancel ();
virtual bool
Interrupt ();
bool
Interrupt () override;
virtual void
GotEOF();
void
GotEOF() override;
virtual void
Activate ();
void
Activate () override;
virtual void
Deactivate ();
void
Deactivate () override;
virtual ConstString
GetControlSequence (char ch)
ConstString
GetControlSequence (char ch) override
{
return m_delegate.IOHandlerGetControlSequence (ch);
}
virtual const char *
GetCommandPrefix ()
const char *
GetCommandPrefix () override
{
return m_delegate.IOHandlerGetCommandPrefix ();
}
virtual const char *
GetHelpPrologue ()
const char *
GetHelpPrologue () override
{
return m_delegate.IOHandlerGetHelpPrologue ();
}
virtual const char *
GetPrompt ();
const char *
GetPrompt () override;
virtual bool
SetPrompt (const char *prompt);
bool
SetPrompt (const char *prompt) override;
const char *
GetContinuationPrompt ();
@ -591,6 +580,9 @@ namespace lldb_private {
uint32_t
GetCurrentLineIndex () const;
void
PrintAsync (Stream *stream, const char *s, size_t len) override;
private:
#ifndef LLDB_DISABLE_LIBEDIT
static bool
@ -649,17 +641,17 @@ namespace lldb_private {
return m_user_response;
}
virtual int
int
IOHandlerComplete (IOHandler &io_handler,
const char *current_line,
const char *cursor,
const char *last_char,
int skip_first_n_matches,
int max_matches,
StringList &matches);
StringList &matches) override;
virtual void
IOHandlerInputComplete (IOHandler &io_handler, std::string &data);
void
IOHandlerInputComplete (IOHandler &io_handler, std::string &data) override;
protected:
const bool m_default_response;
@ -672,32 +664,25 @@ namespace lldb_private {
public:
IOHandlerCursesGUI (Debugger &debugger);
virtual
~IOHandlerCursesGUI ();
~IOHandlerCursesGUI () override;
virtual void
Run ();
void
Run () override;
virtual void
Hide ();
virtual void
Refresh ();
void
Cancel () override;
virtual void
Cancel ();
virtual bool
Interrupt ();
bool
Interrupt () override;
virtual void
GotEOF();
void
GotEOF() override;
virtual void
Activate ();
void
Activate () override;
virtual void
Deactivate ();
void
Deactivate () override;
protected:
curses::ApplicationAP m_app_ap;
@ -712,20 +697,11 @@ namespace lldb_private {
virtual
~IOHandlerCursesValueObjectList ();
virtual void
Run ();
void
Run () override;
virtual void
Hide ();
virtual void
Refresh ();
virtual bool
HandleInterrupt ();
virtual void
GotEOF();
void
GotEOF() override;
protected:
ValueObjectList m_valobj_list;
};
@ -850,7 +826,10 @@ namespace lldb_private {
return NULL;
}
protected:
void
PrintAsync (Stream *stream, const char *s, size_t len);
protected:
typedef std::vector<lldb::IOHandlerSP> collection;
collection m_stack;

View file

@ -20,7 +20,7 @@ class StreamAsynchronousIO :
public Stream
{
public:
StreamAsynchronousIO (Broadcaster &broadcaster, uint32_t broadcast_event_type);
StreamAsynchronousIO (Debugger &debugger, bool for_stdout);
virtual ~StreamAsynchronousIO ();
@ -32,9 +32,9 @@ public:
private:
Broadcaster &m_broadcaster;
uint32_t m_broadcast_event_type;
std::string m_accumulated_data;
Debugger &m_debugger;
std::string m_data;
bool m_for_stdout;
};
} // namespace lldb_private

View file

@ -171,17 +171,13 @@ namespace lldb_private {
uint32_t
GetCurrentLine();
/// Hides the current input session in preparation for output
void
Hide();
/// Prepare to return to editing after a call to Hide()
void
Refresh();
/// Interrupt the current edit as if ^C was pressed
bool
Interrupt();
/// Cancel this edit and oblitarate all trace of it
bool
Cancel();
/// Register a callback for the tab key
void
@ -207,6 +203,9 @@ namespace lldb_private {
bool
GetLines (int first_line_number, StringList &lines, bool &interrupted);
void
PrintAsync (Stream *stream, const char *s, size_t len);
private:
/// Sets the lowest line number for multi-line editing sessions. A value of zero suppresses
@ -335,7 +334,6 @@ namespace lldb_private {
bool m_multiline_enabled = false;
std::vector<EditLineStringType> m_input_lines;
EditorStatus m_editor_status;
bool m_editor_getting_char = false;
bool m_color_prompts = true;
int m_terminal_width = 0;
int m_base_line_number = 0;
@ -359,6 +357,8 @@ namespace lldb_private {
const char * m_fix_indentation_callback_chars = nullptr;
CompleteCallbackType m_completion_callback = nullptr;
void * m_completion_callback_baton = nullptr;
Mutex m_output_mutex;
};
}

View file

@ -2746,6 +2746,11 @@ public:
Listener *hijack_listener = NULL,
Stream *stream = NULL);
uint32_t
GetIOHandlerID () const
{
return m_iohandler_sync.GetValue();
}
//--------------------------------------------------------------------------------------
/// Waits for the process state to be running within a given msec timeout.
@ -2756,14 +2761,9 @@ public:
/// @param[in] timeout_msec
/// The maximum time length to wait for the process to transition to the
/// eStateRunning state, specified in milliseconds.
///
/// @return
/// true if successfully signalled that process started and IOHandler pushes, false
/// if it timed out.
//--------------------------------------------------------------------------------------
bool
SyncIOHandler (uint64_t timeout_msec);
void
SyncIOHandler (uint32_t iohandler_id, uint64_t timeout_msec);
lldb::StateType
WaitForStateChangedEvents (const TimeValue *timeout,
@ -3186,7 +3186,7 @@ protected:
std::string m_stderr_data;
Mutex m_profile_data_comm_mutex;
std::vector<std::string> m_profile_data;
Predicate<bool> m_iohandler_sync;
Predicate<uint32_t> m_iohandler_sync;
MemoryCache m_memory_cache;
AllocatedMemoryCache m_allocated_memory_cache;
bool m_should_detach; /// Should we detach if the process object goes away with an explicit call to Kill or Detach?

View file

@ -270,7 +270,7 @@ protected:
// There is a race condition where this thread will return up the call stack to the main command
// handler and show an (lldb) prompt before HandlePrivateEvent (from PrivateStateThread) has
// a chance to call PushProcessIOHandler().
process_sp->SyncIOHandler(2000);
process_sp->SyncIOHandler (0, 2000);
const char *data = stream.GetData();
if (data && strlen(data) > 0)
@ -762,6 +762,8 @@ protected:
}
}
const uint32_t iohandler_id = process->GetIOHandlerID();
StreamString stream;
Error error;
if (synchronous_execution)
@ -772,9 +774,9 @@ protected:
if (error.Success())
{
// There is a race condition where this thread will return up the call stack to the main command
// handler and show an (lldb) prompt before HandlePrivateEvent (from PrivateStateThread) has
// a chance to call PushProcessIOHandler().
process->SyncIOHandler(2000);
// handler and show an (lldb) prompt before HandlePrivateEvent (from PrivateStateThread) has
// a chance to call PushProcessIOHandler().
process->SyncIOHandler(iohandler_id, 2000);
result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID());
if (synchronous_execution)

View file

@ -675,6 +675,8 @@ protected:
process->GetThreadList().SetSelectedThreadByID (thread->GetID());
const uint32_t iohandler_id = process->GetIOHandlerID();
StreamString stream;
Error error;
if (synchronous_execution)
@ -685,7 +687,7 @@ protected:
// There is a race condition where this thread will return up the call stack to the main command handler
// and show an (lldb) prompt before HandlePrivateEvent (from PrivateStateThread) has
// a chance to call PushProcessIOHandler().
process->SyncIOHandler(2000);
process->SyncIOHandler(iohandler_id, 2000);
if (synchronous_execution)
{

View file

@ -885,34 +885,27 @@ Debugger::ClearIOHandlers ()
{
IOHandlerSP reader_sp (m_input_reader_stack.Top());
if (reader_sp)
{
m_input_reader_stack.Pop();
reader_sp->SetIsDone(true);
reader_sp->Cancel();
}
PopIOHandler (reader_sp);
}
}
void
Debugger::ExecuteIOHandlers()
{
while (1)
{
IOHandlerSP reader_sp(m_input_reader_stack.Top());
if (!reader_sp)
break;
reader_sp->Activate();
reader_sp->Run();
reader_sp->Deactivate();
// Remove all input readers that are done from the top of the stack
while (1)
{
IOHandlerSP top_reader_sp = m_input_reader_stack.Top();
if (top_reader_sp && top_reader_sp->GetIsDone())
m_input_reader_stack.Pop();
PopIOHandler (top_reader_sp);
else
break;
}
@ -926,6 +919,12 @@ Debugger::IsTopIOHandler (const lldb::IOHandlerSP& reader_sp)
return m_input_reader_stack.IsTop (reader_sp);
}
void
Debugger::PrintAsync (const char *s, size_t len, bool is_stdout)
{
lldb::StreamFileSP stream = is_stdout ? GetOutputFile() : GetErrorFile();
m_input_reader_stack.PrintAsync(stream.get(), s, len);
}
ConstString
Debugger::GetTopIOHandlerControlSequence(char ch)
@ -949,25 +948,23 @@ void
Debugger::RunIOHandler (const IOHandlerSP& reader_sp)
{
PushIOHandler (reader_sp);
IOHandlerSP top_reader_sp = reader_sp;
while (top_reader_sp)
{
top_reader_sp->Activate();
top_reader_sp->Run();
top_reader_sp->Deactivate();
if (top_reader_sp.get() == reader_sp.get())
{
if (PopIOHandler (reader_sp))
break;
}
while (1)
{
top_reader_sp = m_input_reader_stack.Top();
if (top_reader_sp && top_reader_sp->GetIsDone())
m_input_reader_stack.Pop();
PopIOHandler (top_reader_sp);
else
break;
}
@ -1030,87 +1027,67 @@ Debugger::PushIOHandler (const IOHandlerSP& reader_sp)
if (!reader_sp)
return;
// Got the current top input reader...
Mutex::Locker locker (m_input_reader_stack.GetMutex());
// Get the current top input reader...
IOHandlerSP top_reader_sp (m_input_reader_stack.Top());
// Don't push the same IO handler twice...
if (reader_sp.get() != top_reader_sp.get())
{
// Push our new input reader
m_input_reader_stack.Push (reader_sp);
if (reader_sp == top_reader_sp)
return;
// Interrupt the top input reader to it will exit its Run() function
// and let this new input reader take over
if (top_reader_sp)
top_reader_sp->Deactivate();
// Push our new input reader
m_input_reader_stack.Push (reader_sp);
reader_sp->Activate();
// Interrupt the top input reader to it will exit its Run() function
// and let this new input reader take over
if (top_reader_sp)
{
top_reader_sp->Deactivate();
top_reader_sp->Cancel();
}
}
bool
Debugger::PopIOHandler (const IOHandlerSP& pop_reader_sp)
{
bool result = false;
if (! pop_reader_sp)
return false;
Mutex::Locker locker (m_input_reader_stack.GetMutex());
// The reader on the stop of the stack is done, so let the next
// read on the stack refresh its prompt and if there is one...
if (!m_input_reader_stack.IsEmpty())
{
IOHandlerSP reader_sp(m_input_reader_stack.Top());
if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get())
{
reader_sp->Deactivate();
reader_sp->Cancel();
m_input_reader_stack.Pop ();
reader_sp = m_input_reader_stack.Top();
if (reader_sp)
reader_sp->Activate();
if (m_input_reader_stack.IsEmpty())
return false;
result = true;
}
}
return result;
}
bool
Debugger::HideTopIOHandler()
{
Mutex::Locker locker;
if (locker.TryLock(m_input_reader_stack.GetMutex()))
{
IOHandlerSP reader_sp(m_input_reader_stack.Top());
if (reader_sp)
reader_sp->Hide();
return true;
}
return false;
}
void
Debugger::RefreshTopIOHandler()
{
IOHandlerSP reader_sp(m_input_reader_stack.Top());
if (reader_sp)
reader_sp->Refresh();
}
if (pop_reader_sp != reader_sp)
return false;
reader_sp->Deactivate();
reader_sp->Cancel();
m_input_reader_stack.Pop ();
reader_sp = m_input_reader_stack.Top();
if (reader_sp)
reader_sp->Activate();
return true;
}
StreamSP
Debugger::GetAsyncOutputStream ()
{
return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(),
CommandInterpreter::eBroadcastBitAsynchronousOutputData));
return StreamSP (new StreamAsynchronousIO (*this, true));
}
StreamSP
Debugger::GetAsyncErrorStream ()
{
return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(),
CommandInterpreter::eBroadcastBitAsynchronousErrorData));
return StreamSP (new StreamAsynchronousIO (*this, false));
}
size_t
@ -1396,14 +1373,14 @@ Debugger::HandleBreakpointEvent (const EventSP &event_sp)
if (num_new_locations > 0)
{
BreakpointSP breakpoint = Breakpoint::BreakpointEventData::GetBreakpointFromEvent(event_sp);
StreamFileSP output_sp (GetOutputFile());
StreamSP output_sp (GetAsyncOutputStream());
if (output_sp)
{
output_sp->Printf("%d location%s added to breakpoint %d\n",
num_new_locations,
num_new_locations == 1 ? "" : "s",
breakpoint->GetID());
RefreshTopIOHandler();
output_sp->Flush();
}
}
}
@ -1490,8 +1467,8 @@ Debugger::HandleProcessEvent (const EventSP &event_sp)
const uint32_t event_type = event_sp->GetType();
ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get());
StreamString output_stream;
StreamString error_stream;
StreamSP output_stream_sp = GetAsyncOutputStream();
StreamSP error_stream_sp = GetAsyncErrorStream();
const bool gui_enabled = IsForwardingEvents();
if (!gui_enabled)
@ -1499,47 +1476,43 @@ Debugger::HandleProcessEvent (const EventSP &event_sp)
bool pop_process_io_handler = false;
assert (process_sp);
if (event_type & Process::eBroadcastBitSTDOUT || event_type & Process::eBroadcastBitStateChanged)
bool state_is_stopped = false;
const bool got_state_changed = (event_type & Process::eBroadcastBitStateChanged) != 0;
const bool got_stdout = (event_type & Process::eBroadcastBitSTDOUT) != 0;
const bool got_stderr = (event_type & Process::eBroadcastBitSTDERR) != 0;
if (got_state_changed)
{
GetProcessSTDOUT (process_sp.get(), &output_stream);
StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get());
state_is_stopped = StateIsStoppedState(event_state, false);
}
if (event_type & Process::eBroadcastBitSTDERR || event_type & Process::eBroadcastBitStateChanged)
// Display running state changes first before any STDIO
if (got_state_changed && !state_is_stopped)
{
GetProcessSTDERR (process_sp.get(), &error_stream);
Process::HandleProcessStateChangedEvent (event_sp, output_stream_sp.get(), pop_process_io_handler);
}
if (event_type & Process::eBroadcastBitStateChanged)
// Now display and STDOUT
if (got_stdout || got_state_changed)
{
Process::HandleProcessStateChangedEvent (event_sp, &output_stream, pop_process_io_handler);
GetProcessSTDOUT (process_sp.get(), output_stream_sp.get());
}
if (output_stream.GetSize() || error_stream.GetSize())
// Now display and STDERR
if (got_stderr || got_state_changed)
{
StreamFileSP error_stream_sp (GetOutputFile());
bool top_io_handler_hid = false;
if (process_sp->ProcessIOHandlerExists() && process_sp->ProcessIOHandlerIsActive() == false)
top_io_handler_hid = HideTopIOHandler();
if (output_stream.GetSize())
{
StreamFileSP output_stream_sp (GetOutputFile());
if (output_stream_sp)
output_stream_sp->Write (output_stream.GetData(), output_stream.GetSize());
}
if (error_stream.GetSize())
{
StreamFileSP error_stream_sp (GetErrorFile());
if (error_stream_sp)
error_stream_sp->Write (error_stream.GetData(), error_stream.GetSize());
}
if (top_io_handler_hid)
RefreshTopIOHandler();
GetProcessSTDERR (process_sp.get(), error_stream_sp.get());
}
// Now display any stopped state changes after any STDIO
if (got_state_changed && state_is_stopped)
{
Process::HandleProcessStateChangedEvent (event_sp, output_stream_sp.get(), pop_process_io_handler);
}
output_stream_sp->Flush();
error_stream_sp->Flush();
if (pop_process_io_handler)
process_sp->PopProcessIOHandler();
}
@ -1558,10 +1531,7 @@ Debugger::HandleThreadEvent (const EventSP &event_sp)
ThreadSP thread_sp (Thread::ThreadEventData::GetThreadFromEvent (event_sp.get()));
if (thread_sp)
{
HideTopIOHandler();
StreamFileSP stream_sp (GetOutputFile());
thread_sp->GetStatus(*stream_sp, 0, 1, 1);
RefreshTopIOHandler();
thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1);
}
}
}
@ -1655,13 +1625,11 @@ Debugger::DefaultEventHandler()
const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get()));
if (data && data[0])
{
StreamFileSP error_sp (GetErrorFile());
StreamSP error_sp (GetAsyncErrorStream());
if (error_sp)
{
HideTopIOHandler();
error_sp->PutCString(data);
error_sp->Flush();
RefreshTopIOHandler();
}
}
}
@ -1670,13 +1638,11 @@ Debugger::DefaultEventHandler()
const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get()));
if (data && data[0])
{
StreamFileSP output_sp (GetOutputFile());
StreamSP output_sp (GetAsyncOutputStream());
if (output_sp)
{
HideTopIOHandler();
output_sp->PutCString(data);
output_sp->Flush();
RefreshTopIOHandler();
}
}
}

View file

@ -169,6 +169,17 @@ IOHandler::WaitForPop ()
m_popped.WaitForValueEqualTo(true);
}
void
IOHandlerStack::PrintAsync (Stream *stream, const char *s, size_t len)
{
if (stream)
{
Mutex::Locker locker (m_mutex);
if (m_top)
m_top->PrintAsync (stream, s, len);
}
}
IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
const char *prompt,
bool default_response) :
@ -739,48 +750,12 @@ IOHandlerEditline::Run ()
}
}
void
IOHandlerEditline::Hide ()
{
#ifndef LLDB_DISABLE_LIBEDIT
if (m_editline_ap)
m_editline_ap->Hide();
#endif
}
void
IOHandlerEditline::Refresh ()
{
#ifndef LLDB_DISABLE_LIBEDIT
if (m_editline_ap)
{
m_editline_ap->Refresh();
}
else if (m_editing)
{
#endif
const char *prompt = GetPrompt();
if (prompt && prompt[0])
{
FILE *out = GetOutputFILE();
if (out)
{
::fprintf(out, "%s", prompt);
::fflush(out);
}
}
#ifndef LLDB_DISABLE_LIBEDIT
}
#endif
}
void
IOHandlerEditline::Cancel ()
{
#ifndef LLDB_DISABLE_LIBEDIT
if (m_editline_ap)
m_editline_ap->Interrupt ();
m_editline_ap->Cancel ();
#endif
}
@ -807,6 +782,17 @@ IOHandlerEditline::GotEOF()
#endif
}
void
IOHandlerEditline::PrintAsync (Stream *stream, const char *s, size_t len)
{
#ifndef LLDB_DISABLE_LIBEDIT
if (m_editline_ap)
m_editline_ap->PrintAsync(stream, s, len);
else
#endif
IOHandler::PrintAsync(stream, s, len);
}
// we may want curses to be disabled for some builds
// for instance, windows
#ifndef LLDB_DISABLE_CURSES
@ -5617,17 +5603,6 @@ IOHandlerCursesGUI::~IOHandlerCursesGUI ()
}
void
IOHandlerCursesGUI::Hide ()
{
}
void
IOHandlerCursesGUI::Refresh ()
{
}
void
IOHandlerCursesGUI::Cancel ()
{

View file

@ -7,22 +7,20 @@
//
//===----------------------------------------------------------------------===//
#include <stdio.h>
#include "lldb/Core/StreamAsynchronousIO.h"
#include "lldb/lldb-private.h"
#include "lldb/Core/Broadcaster.h"
#include "lldb/Core/Event.h"
#include "lldb/Core/StreamAsynchronousIO.h"
#include "lldb/Core/Debugger.h"
using namespace lldb;
using namespace lldb_private;
StreamAsynchronousIO::StreamAsynchronousIO (Broadcaster &broadcaster, uint32_t broadcast_event_type) :
StreamAsynchronousIO::StreamAsynchronousIO (Debugger &debugger, bool for_stdout) :
Stream (0, 4, eByteOrderBig),
m_broadcaster (broadcaster),
m_broadcast_event_type (broadcast_event_type),
m_accumulated_data ()
m_debugger (debugger),
m_data (),
m_for_stdout (for_stdout)
{
}
@ -35,19 +33,16 @@ StreamAsynchronousIO::~StreamAsynchronousIO ()
void
StreamAsynchronousIO::Flush ()
{
if (!m_accumulated_data.empty())
if (!m_data.empty())
{
std::unique_ptr<EventDataBytes> data_bytes_ap (new EventDataBytes);
// Let's swap the bytes to avoid LARGE string copies.
data_bytes_ap->SwapBytes (m_accumulated_data);
EventSP new_event_sp (new Event (m_broadcast_event_type, data_bytes_ap.release()));
m_broadcaster.BroadcastEvent (new_event_sp);
m_debugger.PrintAsync (m_data.data(), m_data.size(), m_for_stdout);
m_data = std::move(std::string());
}
}
size_t
StreamAsynchronousIO::Write (const void *s, size_t length)
{
m_accumulated_data.append ((const char *)s, length);
m_data.append ((const char *)s, length);
return length;
}

View file

@ -20,6 +20,7 @@
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/Mutex.h"
#include "lldb/Utility/LLDBAssert.h"
using namespace lldb_private;
using namespace lldb_private::line_editor;
@ -581,9 +582,22 @@ Editline::GetCharacter (EditLineCharType * c)
{
lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
char ch = 0;
m_editor_getting_char = true;
// This mutex is locked by our caller (GetLine). Unlock it while we read a character
// (blocking operation), so we do not hold the mutex indefinitely. This gives a chance
// for someone to interrupt us. After Read returns, immediately lock the mutex again and
// check if we were interrupted.
m_output_mutex.Unlock();
int read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL);
m_editor_getting_char = false;
m_output_mutex.Lock();
if (m_editor_status == EditorStatus::Interrupted)
{
while (read_count > 0 && status == lldb::eConnectionStatusSuccess)
read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL);
lldbassert(status == lldb::eConnectionStatusInterrupted);
return 0;
}
if (read_count)
{
#if LLDB_EDITLINE_USE_WCHAR
@ -602,14 +616,12 @@ Editline::GetCharacter (EditLineCharType * c)
{
switch (status)
{
case lldb::eConnectionStatusInterrupted:
m_editor_status = EditorStatus::Interrupted;
printf ("^C\n");
return 0;
case lldb::eConnectionStatusSuccess: // Success
break;
case lldb::eConnectionStatusInterrupted:
lldbassert(0 && "Interrupts should have been handled above.");
case lldb::eConnectionStatusError: // Check GetError() for details
case lldb::eConnectionStatusTimedOut: // Request timed out
case lldb::eConnectionStatusEndOfFile: // End-of-file encountered
@ -1252,41 +1264,31 @@ Editline::GetCurrentLine()
return m_current_line_index;
}
void
Editline::Hide()
{
// Make sure we're at a stable location waiting for input
while (m_editor_status == EditorStatus::Editing && !m_editor_getting_char)
{
usleep(100000);
}
// Clear the existing input
if (m_editor_status == EditorStatus::Editing)
{
MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
fprintf(m_output_file, ANSI_CLEAR_BELOW);
}
}
void
Editline::Refresh()
{
if (m_editor_status == EditorStatus::Editing)
{
DisplayInput();
MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
}
}
bool
Editline::Interrupt()
{
if (m_editor_status == EditorStatus::Editing)
{
return m_input_connection.InterruptRead();
bool result = true;
Mutex::Locker locker(m_output_mutex);
if (m_editor_status == EditorStatus::Editing) {
fprintf(m_output_file, "^C\n");
result = m_input_connection.InterruptRead();
}
return false; // Interrupt not handled as we weren't getting a line or lines
m_editor_status = EditorStatus::Interrupted;
return result;
}
bool
Editline::Cancel()
{
bool result = true;
Mutex::Locker locker(m_output_mutex);
if (m_editor_status == EditorStatus::Editing) {
MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
fprintf(m_output_file, ANSI_CLEAR_BELOW);
result = m_input_connection.InterruptRead();
}
m_editor_status = EditorStatus::Interrupted;
return result;
}
void
@ -1321,14 +1323,23 @@ Editline::GetLine (std::string &line, bool &interrupted)
m_input_lines = std::vector<EditLineStringType>();
m_input_lines.insert (m_input_lines.begin(), EditLineConstString(""));
Mutex::Locker locker(m_output_mutex);
lldbassert(m_editor_status != EditorStatus::Editing);
if (m_editor_status == EditorStatus::Interrupted)
{
m_editor_status = EditorStatus::Complete;
interrupted = true;
return true;
}
SetCurrentLine (0);
m_in_history = false;
m_editor_status = EditorStatus::Editing;
m_editor_getting_char = false;
m_revert_cursor_index = -1;
#ifdef USE_SETUPTERM_WORKAROUND
setupterm((char *)0, fileno(m_output_file), (int *)0);
setupterm((char *)0, fileno(m_output_file), (int *)0);
#endif
int count;
@ -1366,12 +1377,12 @@ Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted)
m_input_lines = std::vector<EditLineStringType>();
m_input_lines.insert (m_input_lines.begin(), EditLineConstString(""));
Mutex::Locker locker(m_output_mutex);
// Begin the line editing loop
DisplayInput();
SetCurrentLine (0);
MoveCursor (CursorLocation::BlockEnd, CursorLocation::BlockStart);
m_editor_status = EditorStatus::Editing;
m_editor_getting_char = false;
m_in_history = false;
m_revert_cursor_index = -1;
@ -1396,3 +1407,21 @@ Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted)
}
return m_editor_status != EditorStatus::EndOfInput;
}
void
Editline::PrintAsync (Stream *stream, const char *s, size_t len)
{
Mutex::Locker locker(m_output_mutex);
if (m_editor_status == EditorStatus::Editing)
{
MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
fprintf(m_output_file, ANSI_CLEAR_BELOW);
}
stream->Write (s, len);
stream->Flush();
if (m_editor_status == EditorStatus::Editing)
{
DisplayInput();
MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
}
}

View file

@ -734,22 +734,21 @@ public:
}
virtual
~IOHandlerPythonInterpreter()
~IOHandlerPythonInterpreter() override
{
}
virtual ConstString
GetControlSequence (char ch)
ConstString
GetControlSequence (char ch) override
{
if (ch == 'd')
return ConstString("quit()\n");
return ConstString();
}
virtual void
Run ()
void
Run () override
{
if (m_python)
{
@ -795,32 +794,20 @@ public:
SetIsDone(true);
}
virtual void
Hide ()
{
}
virtual void
Refresh ()
void
Cancel () override
{
}
virtual void
Cancel ()
{
}
virtual bool
Interrupt ()
bool
Interrupt () override
{
return m_python->Interrupt();
}
virtual void
GotEOF()
void
GotEOF() override
{
}

View file

@ -740,7 +740,7 @@ Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_s
m_stderr_data (),
m_profile_data_comm_mutex (Mutex::eMutexTypeRecursive),
m_profile_data (),
m_iohandler_sync (false),
m_iohandler_sync (0),
m_memory_cache (*this),
m_allocated_memory_cache (*this),
m_should_detach (false),
@ -949,33 +949,21 @@ Process::GetNextEvent (EventSP &event_sp)
return state;
}
bool
Process::SyncIOHandler (uint64_t timeout_msec)
void
Process::SyncIOHandler (uint32_t iohandler_id, uint64_t timeout_msec)
{
bool timed_out = false;
// don't sync (potentially context switch) in case where there is no process IO
if (m_process_input_reader)
{
TimeValue timeout = TimeValue::Now();
timeout.OffsetWithMicroSeconds(timeout_msec*1000);
if (! m_process_input_reader)
return;
m_iohandler_sync.WaitForValueEqualTo(true, &timeout, &timed_out);
TimeValue timeout = TimeValue::Now();
timeout.OffsetWithMicroSeconds(timeout_msec*1000);
uint32_t new_iohandler_id = 0;
m_iohandler_sync.WaitForValueNotEqualTo(iohandler_id, new_iohandler_id, &timeout);
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
if(log)
{
if(timed_out)
log->Printf ("Process::%s pid %" PRIu64 " (timeout=%" PRIu64 "ms): FAIL", __FUNCTION__, GetID (), timeout_msec);
else
log->Printf ("Process::%s pid %" PRIu64 ": SUCCESS", __FUNCTION__, GetID ());
}
// reset sync one-shot so it will be ready for next launch
m_iohandler_sync.SetValue(false, eBroadcastNever);
}
return !timed_out;
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
if (log)
log->Printf("Process::%s waited for m_iohandler_sync to change from %u, new value is %u", __FUNCTION__, iohandler_id, new_iohandler_id);
}
StateType
@ -4477,12 +4465,15 @@ Process::HandlePrivateEvent (EventSP &event_sp)
// Or don't push it if we are launching since it will come up stopped.
if (!GetTarget().GetDebugger().IsForwardingEvents() && new_state != eStateLaunching &&
new_state != eStateAttaching)
{
PushProcessIOHandler ();
m_iohandler_sync.SetValue(true, eBroadcastAlways);
m_iohandler_sync.SetValue(m_iohandler_sync.GetValue()+1, eBroadcastAlways);
if (log)
log->Printf("Process::%s updated m_iohandler_sync to %d", __FUNCTION__, m_iohandler_sync.GetValue());
}
}
else if (StateIsStoppedState(new_state, false))
{
m_iohandler_sync.SetValue(false, eBroadcastNever);
if (!Process::ProcessEventData::GetRestartedFromEvent(event_sp.get()))
{
// If the lldb_private::Debugger is handling the events, we don't
@ -5125,8 +5116,8 @@ public:
// Each IOHandler gets to run until it is done. It should read data
// from the "in" and place output into "out" and "err and return
// when done.
virtual void
Run ()
void
Run () override
{
if (m_read_file.IsValid() && m_write_file.IsValid())
{
@ -5204,33 +5195,16 @@ public:
SetIsDone(true);
}
// Hide any characters that have been displayed so far so async
// output can be displayed. Refresh() will be called after the
// output has been displayed.
virtual void
Hide ()
{
}
// Called when the async output has been received in order to update
// the input reader (refresh the prompt and redisplay any current
// line(s) that are being edited
virtual void
Refresh ()
{
}
virtual void
Cancel ()
void
Cancel () override
{
char ch = 'q'; // Send 'q' for quit
size_t bytes_written = 0;
m_pipe.Write(&ch, 1, bytes_written);
}
virtual bool
Interrupt ()
bool
Interrupt () override
{
// Do only things that are safe to do in an interrupt context (like in
// a SIGINT handler), like write 1 byte to a file descriptor. This will
@ -5263,8 +5237,8 @@ public:
return false;
}
virtual void
GotEOF()
void
GotEOF() override
{
}
@ -5313,6 +5287,10 @@ Process::PushProcessIOHandler ()
IOHandlerSP io_handler_sp (m_process_input_reader);
if (io_handler_sp)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
if (log)
log->Printf("Process::%s pushing IO handler", __FUNCTION__);
io_handler_sp->SetIsDone(false);
m_target.GetDebugger().PushIOHandler (io_handler_sp);
return true;