Index: include/lldb/Core/Debugger.h =================================================================== --- include/lldb/Core/Debugger.h +++ include/lldb/Core/Debugger.h @@ -210,6 +210,9 @@ 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 @@ const char * GetIOHandlerHelpPrologue(); - bool - HideTopIOHandler(); - - void - RefreshTopIOHandler(); - static lldb::DebuggerSP FindDebuggerWithID (lldb::user_id_t id); Index: include/lldb/Core/IOHandler.h =================================================================== --- include/lldb/Core/IOHandler.h +++ include/lldb/Core/IOHandler.h @@ -67,18 +67,6 @@ 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 @@ 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 @@ { } - 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 @@ 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 @@ uint32_t GetCurrentLineIndex () const; + void + PrintAsync (Stream *stream, const char *s, size_t len) override; + private: #ifndef LLDB_DISABLE_LIBEDIT static bool @@ -648,17 +640,17 @@ 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; @@ -671,32 +663,25 @@ 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; @@ -711,20 +696,11 @@ 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; }; @@ -849,7 +825,10 @@ return NULL; } - protected: + void + PrintAsync (Stream *stream, const char *s, size_t len); + + protected: typedef std::vector collection; collection m_stack; Index: include/lldb/Core/StreamAsynchronousIO.h =================================================================== --- include/lldb/Core/StreamAsynchronousIO.h +++ include/lldb/Core/StreamAsynchronousIO.h @@ -20,7 +20,7 @@ public Stream { public: - StreamAsynchronousIO (Broadcaster &broadcaster, uint32_t broadcast_event_type); + StreamAsynchronousIO (Debugger &debugger, bool for_stdout); virtual ~StreamAsynchronousIO (); @@ -32,9 +32,9 @@ 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 Index: include/lldb/Host/Editline.h =================================================================== --- include/lldb/Host/Editline.h +++ include/lldb/Host/Editline.h @@ -171,17 +171,13 @@ 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 @@ 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 @@ bool m_multiline_enabled = false; std::vector 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 @@ const char * m_fix_indentation_callback_chars = nullptr; CompleteCallbackType m_completion_callback = nullptr; void * m_completion_callback_baton = nullptr; + + Mutex m_output_mutex; }; } Index: include/lldb/Target/Process.h =================================================================== --- include/lldb/Target/Process.h +++ include/lldb/Target/Process.h @@ -2726,6 +2726,11 @@ 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. @@ -2736,14 +2741,9 @@ /// @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, @@ -3166,7 +3166,7 @@ std::string m_stderr_data; Mutex m_profile_data_comm_mutex; std::vector m_profile_data; - Predicate m_iohandler_sync; + Predicate 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? Index: source/Commands/CommandObjectProcess.cpp =================================================================== --- source/Commands/CommandObjectProcess.cpp +++ source/Commands/CommandObjectProcess.cpp @@ -270,7 +270,7 @@ // 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 @@ } } + const uint32_t iohandler_id = process->GetIOHandlerID(); + StreamString stream; Error error; if (synchronous_execution) @@ -772,9 +774,9 @@ 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) Index: source/Commands/CommandObjectThread.cpp =================================================================== --- source/Commands/CommandObjectThread.cpp +++ source/Commands/CommandObjectThread.cpp @@ -675,6 +675,8 @@ process->GetThreadList().SetSelectedThreadByID (thread->GetID()); + const uint32_t iohandler_id = process->GetIOHandlerID(); + StreamString stream; Error error; if (synchronous_execution) @@ -685,7 +687,7 @@ // 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) { Index: source/Core/Debugger.cpp =================================================================== --- source/Core/Debugger.cpp +++ source/Core/Debugger.cpp @@ -885,34 +885,27 @@ { 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ const char *data = reinterpret_cast(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 @@ const char *data = reinterpret_cast(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(); } } } Index: source/Core/IOHandler.cpp =================================================================== --- source/Core/IOHandler.cpp +++ source/Core/IOHandler.cpp @@ -169,6 +169,17 @@ 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) : @@ -737,47 +748,11 @@ } 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 - { -#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 } @@ -804,6 +779,17 @@ #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 @@ -5615,17 +5601,6 @@ } void -IOHandlerCursesGUI::Hide () -{ -} - - -void -IOHandlerCursesGUI::Refresh () -{ -} - -void IOHandlerCursesGUI::Cancel () { } Index: source/Core/StreamAsynchronousIO.cpp =================================================================== --- source/Core/StreamAsynchronousIO.cpp +++ source/Core/StreamAsynchronousIO.cpp @@ -7,22 +7,20 @@ // //===----------------------------------------------------------------------===// -#include +#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 @@ void StreamAsynchronousIO::Flush () { - if (!m_accumulated_data.empty()) + if (!m_data.empty()) { - std::unique_ptr 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; } Index: source/Host/common/Editline.cpp =================================================================== --- source/Host/common/Editline.cpp +++ 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 @@ { 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 @@ { 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 @@ 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 @@ m_input_lines = std::vector(); 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 @@ m_input_lines = std::vector(); 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 @@ } 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); + } +} Index: source/Interpreter/ScriptInterpreterPython.cpp =================================================================== --- source/Interpreter/ScriptInterpreterPython.cpp +++ source/Interpreter/ScriptInterpreterPython.cpp @@ -734,22 +734,21 @@ } - 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 @@ 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 { } Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -740,7 +740,7 @@ 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 @@ 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 @@ -4454,12 +4442,15 @@ // as this means the curses GUI is in use... // Or don't push it if we are launching since it will come up stopped. if (!GetTarget().GetDebugger().IsForwardingEvents() && new_state != eStateLaunching) + { 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 @@ -5102,8 +5093,8 @@ // 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()) { @@ -5181,33 +5172,16 @@ 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 @@ -5240,8 +5214,8 @@ return false; } - virtual void - GotEOF() + void + GotEOF() override { } @@ -5290,6 +5264,10 @@ 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;