summaryrefslogtreecommitdiff
path: root/lldb
diff options
context:
space:
mode:
authorPavel Labath <labath@google.com>2015-05-27 12:40:32 +0000
committerPavel Labath <labath@google.com>2015-05-27 12:40:32 +0000
commit2d2f8b0f728b543abc436d90abbc7a6f9244c6c5 (patch)
treece58346ab669f38824f55ad606755bc799245085 /lldb
parentff17e9f4a2c1a943df4d7d4f2d232ca0e974a266 (diff)
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
Diffstat (limited to 'lldb')
-rw-r--r--lldb/include/lldb/Core/Debugger.h9
-rw-r--r--lldb/include/lldb/Core/IOHandler.h145
-rw-r--r--lldb/include/lldb/Core/StreamAsynchronousIO.h8
-rw-r--r--lldb/include/lldb/Host/Editline.h18
-rw-r--r--lldb/include/lldb/Target/Process.h16
-rw-r--r--lldb/source/Commands/CommandObjectProcess.cpp10
-rw-r--r--lldb/source/Commands/CommandObjectThread.cpp4
-rw-r--r--lldb/source/Core/Debugger.cpp188
-rw-r--r--lldb/source/Core/IOHandler.cpp71
-rw-r--r--lldb/source/Core/StreamAsynchronousIO.cpp25
-rw-r--r--lldb/source/Host/common/Editline.cpp105
-rw-r--r--lldb/source/Interpreter/ScriptInterpreterPython.cpp35
-rw-r--r--lldb/source/Target/Process.cpp80
13 files changed, 312 insertions, 402 deletions
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index d18e896a8b4..0d9b90c8919 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -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);
diff --git a/lldb/include/lldb/Core/IOHandler.h b/lldb/include/lldb/Core/IOHandler.h
index d750a23533d..8abdeabbb19 100644
--- a/lldb/include/lldb/Core/IOHandler.h
+++ b/lldb/include/lldb/Core/IOHandler.h
@@ -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 ();
-
- virtual void
- Refresh ();
-
- virtual void
- Cancel ();
+ void
+ Cancel () override;
- 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 ();
-
- virtual void
- Run ();
+ ~IOHandlerCursesGUI () override;
- virtual void
- Hide ();
+ void
+ Run () override;
- virtual void
- Refresh ();
-
- virtual void
- Cancel ();
+ void
+ Cancel () override;
- 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 ();
-
- virtual void
- Hide ();
-
- virtual void
- Refresh ();
-
- virtual bool
- HandleInterrupt ();
+ void
+ Run () override;
- 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;
diff --git a/lldb/include/lldb/Core/StreamAsynchronousIO.h b/lldb/include/lldb/Core/StreamAsynchronousIO.h
index a73a9567fe8..d3b054463fa 100644
--- a/lldb/include/lldb/Core/StreamAsynchronousIO.h
+++ b/lldb/include/lldb/Core/StreamAsynchronousIO.h
@@ -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
diff --git a/lldb/include/lldb/Host/Editline.h b/lldb/include/lldb/Host/Editline.h
index 9fce193efc9..697be4cd8e7 100644
--- a/lldb/include/lldb/Host/Editline.h
+++ b/lldb/include/lldb/Host/Editline.h
@@ -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;
};
}
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index 34e188dadc9..0be09b3ef33 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -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?
diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp
index 14b7e2f74c5..e9ca8026b0e 100644
--- a/lldb/source/Commands/CommandObjectProcess.cpp
+++ b/lldb/source/Commands/CommandObjectProcess.cpp
@@ -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)
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 78271f25c66..8fb962948c0 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -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)
{
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index ee2354e7b50..2f7dd9f42a5 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -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();
-
- result = true;
- }
- }
- return result;
-}
+ if (m_input_reader_stack.IsEmpty())
+ return false;
-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 (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->Refresh();
-}
+ 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());
- }
+ GetProcessSTDERR (process_sp.get(), error_stream_sp.get());
+ }
- if (top_io_handler_hid)
- RefreshTopIOHandler();
+ // 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();
}
}
}
diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp
index b1ee57268ef..9cc280b6d70 100644
--- a/lldb/source/Core/IOHandler.cpp
+++ b/lldb/source/Core/IOHandler.cpp
@@ -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) :
@@ -740,47 +751,11 @@ 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
@@ -5618,17 +5604,6 @@ IOHandlerCursesGUI::~IOHandlerCursesGUI ()
}
void
-IOHandlerCursesGUI::Hide ()
-{
-}
-
-
-void
-IOHandlerCursesGUI::Refresh ()
-{
-}
-
-void
IOHandlerCursesGUI::Cancel ()
{
}
diff --git a/lldb/source/Core/StreamAsynchronousIO.cpp b/lldb/source/Core/StreamAsynchronousIO.cpp
index 257982ab8b2..ccfde0c9a01 100644
--- a/lldb/source/Core/StreamAsynchronousIO.cpp
+++ b/lldb/source/Core/StreamAsynchronousIO.cpp
@@ -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;
}
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp
index 3d0d4d3dfb8..0264c4b3309 100644
--- a/lldb/source/Host/common/Editline.cpp
+++ b/lldb/source/Host/common/Editline.cpp
@@ -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()
+bool
+Editline::Interrupt()
{
- if (m_editor_status == EditorStatus::Editing)
- {
- DisplayInput();
- MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+ 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();
}
+ m_editor_status = EditorStatus::Interrupted;
+ return result;
}
bool
-Editline::Interrupt()
+Editline::Cancel()
{
- 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) {
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ fprintf(m_output_file, ANSI_CLEAR_BELOW);
+ 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;
}
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);
+ }
+}
diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp
index 1e16fd3bdc6..188f7285776 100644
--- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp
@@ -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 ()
- {
-
- }
-
- virtual void
- Cancel ()
+ void
+ Cancel () override
{
}
- virtual bool
- Interrupt ()
+ bool
+ Interrupt () override
{
return m_python->Interrupt();
}
- virtual void
- GotEOF()
+ void
+ GotEOF() override
{
}
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 896fb3fedda..f0bd9e3260e 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -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);
-
- m_iohandler_sync.WaitForValueEqualTo(true, &timeout, &timed_out);
-
- 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 ());
- }
+ if (! m_process_input_reader)
+ return;
- // reset sync one-shot so it will be ready for next launch
- m_iohandler_sync.SetValue(false, eBroadcastNever);
- }
+ 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);
- 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;