diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 5281f290d96ca..013838df2f25d 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -181,7 +181,15 @@ class Debugger : public std::enable_shared_from_this, return m_target_list.GetSelectedTarget(); } + /// Get the execution context representing the selected entities in the + /// selected target. ExecutionContext GetSelectedExecutionContext(); + + /// Similar to GetSelectedExecutionContext but returns a + /// ExecutionContextRef, and will hold the dummy target if no target is + /// currently selected. + ExecutionContextRef GetSelectedExecutionContextRef(); + /// Get accessor for the target list. /// /// The target list is part of the global debugger object. This the single @@ -427,7 +435,7 @@ class Debugger : public std::enable_shared_from_this, void CancelInterruptRequest(); /// Redraw the statusline if enabled. - void RedrawStatusline(bool update = true); + void RedrawStatusline(std::optional exe_ctx_ref); /// This is the correct way to query the state of Interruption. /// If you are on the RunCommandInterpreter thread, it will check the @@ -710,9 +718,9 @@ class Debugger : public std::enable_shared_from_this, void HandleBreakpointEvent(const lldb::EventSP &event_sp); - void HandleProcessEvent(const lldb::EventSP &event_sp); + lldb::ProcessSP HandleProcessEvent(const lldb::EventSP &event_sp); - void HandleThreadEvent(const lldb::EventSP &event_sp); + lldb::ThreadSP HandleThreadEvent(const lldb::EventSP &event_sp); void HandleProgressEvent(const lldb::EventSP &event_sp); diff --git a/lldb/include/lldb/Core/Statusline.h b/lldb/include/lldb/Core/Statusline.h index 6bda153f822d2..a5ab1927b57f5 100644 --- a/lldb/include/lldb/Core/Statusline.h +++ b/lldb/include/lldb/Core/Statusline.h @@ -9,6 +9,8 @@ #ifndef LLDB_CORE_STATUSLINE_H #define LLDB_CORE_STATUSLINE_H +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ExecutionContext.h" #include "lldb/lldb-forward.h" #include #include @@ -19,15 +21,16 @@ class Statusline { Statusline(Debugger &debugger); ~Statusline(); + using Context = std::pair; + /// Reduce the scroll window and draw the statusline. - void Enable(); + void Enable(std::optional exe_ctx_ref); /// Hide the statusline and extend the scroll window. void Disable(); - /// Redraw the statusline. If update is false, this will redraw the last - /// string. - void Redraw(bool update = true); + /// Redraw the statusline. + void Redraw(std::optional exe_ctx_ref); /// Inform the statusline that the terminal dimensions have changed. void TerminalSizeChanged(); @@ -46,7 +49,11 @@ class Statusline { void UpdateScrollWindow(ScrollWindowMode mode); Debugger &m_debugger; - std::string m_last_str; + + /// Cached copy of the execution context that allows us to redraw the + /// statusline. + ExecutionContextRef m_exe_ctx_ref; + uint64_t m_terminal_width = 0; uint64_t m_terminal_height = 0; }; diff --git a/lldb/include/lldb/Target/ExecutionContext.h b/lldb/include/lldb/Target/ExecutionContext.h index f105e38fa69aa..fe8bce7f69713 100644 --- a/lldb/include/lldb/Target/ExecutionContext.h +++ b/lldb/include/lldb/Target/ExecutionContext.h @@ -92,10 +92,21 @@ class ExecutionContextRef { /// Construct using the target and all the selected items inside of it (the /// process and its selected thread, and the thread's selected frame). If - /// there is no selected thread, default to the first thread If there is no + /// there is no selected thread, default to the first thread. If there is no /// selected frame, default to the first frame. ExecutionContextRef(Target *target, bool adopt_selected); + /// Construct using the process and all the selected items inside of it ( + /// the selected thread, and the thread's selected frame). If + /// there is no selected thread, default to the first thread. If there is no + /// selected frame, default to the first frame. + ExecutionContextRef(Process *process, bool adopt_selected); + + /// Construct using the thread and all the selected items inside of it ( the + /// selected frame). If there is no selected frame, default to the first + /// frame. + ExecutionContextRef(Thread *thread, bool adopt_selected); + /// Construct using an execution context scope. /// /// If the ExecutionContextScope object is valid and refers to a frame, make @@ -199,9 +210,9 @@ class ExecutionContextRef { void SetTargetPtr(Target *target, bool adopt_selected); - void SetProcessPtr(Process *process); + void SetProcessPtr(Process *process, bool adopt_selected = false); - void SetThreadPtr(Thread *thread); + void SetThreadPtr(Thread *thread, bool adopt_selected = false); void SetFramePtr(StackFrame *frame); diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 0c7066c42c027..b2c6f4d2451d6 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -253,16 +253,18 @@ Status Debugger::SetPropertyValue(const ExecutionContext *exe_ctx, // Statusline setting changed. If we have a statusline instance, update it // now. Otherwise it will get created in the default event handler. std::lock_guard guard(m_statusline_mutex); - if (StatuslineSupported()) + if (StatuslineSupported()) { m_statusline.emplace(*this); - else + m_statusline->Enable(GetSelectedExecutionContextRef()); + } else { m_statusline.reset(); + } } else if (property_path == g_debugger_properties[ePropertyStatuslineFormat].name || property_path == g_debugger_properties[ePropertySeparator].name) { // Statusline format changed. Redraw the statusline. - RedrawStatusline(); + RedrawStatusline(std::nullopt); } else if (property_path == g_debugger_properties[ePropertyUseSourceCache].name) { // use-source-cache changed. Wipe out the cache contents if it was @@ -501,7 +503,7 @@ FormatEntity::Entry Debugger::GetStatuslineFormat() const { bool Debugger::SetStatuslineFormat(const FormatEntity::Entry &format) { constexpr uint32_t idx = ePropertyStatuslineFormat; bool ret = SetPropertyAtIndex(idx, format); - RedrawStatusline(); + RedrawStatusline(std::nullopt); return ret; } @@ -526,7 +528,7 @@ llvm::StringRef Debugger::GetDisabledAnsiSuffix() const { bool Debugger::SetSeparator(llvm::StringRef s) { constexpr uint32_t idx = ePropertySeparator; bool ret = SetPropertyAtIndex(idx, s); - RedrawStatusline(); + RedrawStatusline(std::nullopt); return ret; } @@ -1210,14 +1212,18 @@ void Debugger::RestoreInputTerminalState() { { std::lock_guard guard(m_statusline_mutex); if (m_statusline) - m_statusline->Enable(); + m_statusline->Enable(GetSelectedExecutionContext()); } } -void Debugger::RedrawStatusline(bool update) { +void Debugger::RedrawStatusline( + std::optional exe_ctx_ref) { std::lock_guard guard(m_statusline_mutex); - if (m_statusline) - m_statusline->Redraw(update); + + if (!m_statusline) + return; + + m_statusline->Redraw(exe_ctx_ref); } ExecutionContext Debugger::GetSelectedExecutionContext() { @@ -1226,6 +1232,13 @@ ExecutionContext Debugger::GetSelectedExecutionContext() { return ExecutionContext(exe_ctx_ref); } +ExecutionContextRef Debugger::GetSelectedExecutionContextRef() { + if (TargetSP selected_target_sp = GetSelectedTarget()) + return ExecutionContextRef(selected_target_sp.get(), + /*adopt_selected=*/true); + return ExecutionContextRef(m_dummy_target_sp.get(), /*adopt_selected=*/false); +} + void Debugger::DispatchInputInterrupt() { std::lock_guard guard(m_io_handler_stack.GetMutex()); IOHandlerSP reader_sp(m_io_handler_stack.Top()); @@ -1980,8 +1993,7 @@ void Debugger::FlushProcessOutput(Process &process, bool flush_stdout, } // This function handles events that were broadcast by the process. -void Debugger::HandleProcessEvent(const EventSP &event_sp) { - using namespace lldb; +ProcessSP Debugger::HandleProcessEvent(const EventSP &event_sp) { const uint32_t event_type = event_sp->GetType(); ProcessSP process_sp = (event_type == Process::eBroadcastBitStructuredData) @@ -2076,23 +2088,24 @@ void Debugger::HandleProcessEvent(const EventSP &event_sp) { process_sp->PopProcessIOHandler(pop_command_interpreter); // END SWIFT } + return process_sp; } -void Debugger::HandleThreadEvent(const EventSP &event_sp) { +ThreadSP Debugger::HandleThreadEvent(const EventSP &event_sp) { // At present the only thread event we handle is the Frame Changed event, and // all we do for that is just reprint the thread status for that thread. - using namespace lldb; const uint32_t event_type = event_sp->GetType(); const bool stop_format = true; + ThreadSP thread_sp; if (event_type == Thread::eBroadcastBitStackChanged || event_type == Thread::eBroadcastBitThreadSelected) { - ThreadSP thread_sp( - Thread::ThreadEventData::GetThreadFromEvent(event_sp.get())); + thread_sp = Thread::ThreadEventData::GetThreadFromEvent(event_sp.get()); if (thread_sp) { thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1, stop_format, /*show_hidden*/ true); } } + return thread_sp; } bool Debugger::IsForwardingEvents() { return (bool)m_forward_listener_sp; } @@ -2120,6 +2133,11 @@ bool Debugger::StatuslineSupported() { return false; } +static bool RequiresFollowChildWorkaround(const Process &process) { + // FIXME: https://github.com/llvm/llvm-project/issues/160216 + return process.GetFollowForkMode() == eFollowChild; +} + lldb::thread_result_t Debugger::DefaultEventHandler() { ListenerSP listener_sp(GetListener()); ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); @@ -2161,28 +2179,37 @@ lldb::thread_result_t Debugger::DefaultEventHandler() { if (StatuslineSupported()) { std::lock_guard guard(m_statusline_mutex); - if (!m_statusline) + if (!m_statusline) { m_statusline.emplace(*this); + m_statusline->Enable(GetSelectedExecutionContextRef()); + } } bool done = false; while (!done) { EventSP event_sp; if (listener_sp->GetEvent(event_sp, std::nullopt)) { + std::optional exe_ctx_ref = std::nullopt; if (event_sp) { Broadcaster *broadcaster = event_sp->GetBroadcaster(); if (broadcaster) { uint32_t event_type = event_sp->GetType(); ConstString broadcaster_class(broadcaster->GetBroadcasterClass()); if (broadcaster_class == broadcaster_class_process) { - HandleProcessEvent(event_sp); + if (ProcessSP process_sp = HandleProcessEvent(event_sp)) + if (!RequiresFollowChildWorkaround(*process_sp)) + exe_ctx_ref = ExecutionContextRef(process_sp.get(), + /*adopt_selected=*/true); } else if (broadcaster_class == broadcaster_class_target) { if (Breakpoint::BreakpointEventData::GetEventDataFromEvent( event_sp.get())) { HandleBreakpointEvent(event_sp); } } else if (broadcaster_class == broadcaster_class_thread) { - HandleThreadEvent(event_sp); + if (ThreadSP thread_sp = HandleThreadEvent(event_sp)) + if (!RequiresFollowChildWorkaround(*thread_sp->GetProcess())) + exe_ctx_ref = ExecutionContextRef(thread_sp.get(), + /*adopt_selected=*/true); } else if (broadcaster == m_command_interpreter_up.get()) { if (event_type & CommandInterpreter::eBroadcastBitQuitCommandReceived) { @@ -2220,7 +2247,7 @@ lldb::thread_result_t Debugger::DefaultEventHandler() { if (m_forward_listener_sp) m_forward_listener_sp->AddEvent(event_sp); } - RedrawStatusline(); + RedrawStatusline(exe_ctx_ref); } } diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp index 61de2f6ccc465..1561d27a4f74f 100644 --- a/lldb/source/Core/IOHandler.cpp +++ b/lldb/source/Core/IOHandler.cpp @@ -442,7 +442,7 @@ void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) { } void IOHandlerEditline::RedrawCallback() { - m_debugger.RedrawStatusline(/*update=*/false); + m_debugger.RedrawStatusline(std::nullopt); } #endif diff --git a/lldb/source/Core/Statusline.cpp b/lldb/source/Core/Statusline.cpp index 393d427241021..bfbd190fba27c 100644 --- a/lldb/source/Core/Statusline.cpp +++ b/lldb/source/Core/Statusline.cpp @@ -35,9 +35,7 @@ using namespace lldb_private; Statusline::Statusline(Debugger &debugger) : m_debugger(debugger), m_terminal_width(m_debugger.GetTerminalWidth()), - m_terminal_height(m_debugger.GetTerminalHeight()) { - Enable(); -} + m_terminal_height(m_debugger.GetTerminalHeight()) {} Statusline::~Statusline() { Disable(); } @@ -47,16 +45,16 @@ void Statusline::TerminalSizeChanged() { UpdateScrollWindow(ResizeStatusline); - // Draw the old statusline. - Redraw(/*update=*/false); + // Redraw the old statusline. + Redraw(std::nullopt); } -void Statusline::Enable() { +void Statusline::Enable(std::optional exe_ctx_ref) { // Reduce the scroll window to make space for the status bar below. UpdateScrollWindow(EnableStatusline); // Draw the statusline. - Redraw(/*update=*/true); + Redraw(exe_ctx_ref); } void Statusline::Disable() { @@ -69,8 +67,6 @@ void Statusline::Draw(std::string str) { if (!stream_sp) return; - m_last_str = str; - str = ansi::TrimAndPad(str, m_terminal_width); LockedStreamFile locked_stream = stream_sp->Lock(); @@ -127,33 +123,32 @@ void Statusline::UpdateScrollWindow(ScrollWindowMode mode) { m_debugger.RefreshIOHandler(); } -void Statusline::Redraw(bool update) { - if (!update) { - Draw(m_last_str); - return; - } - - ExecutionContext exe_ctx = m_debugger.GetSelectedExecutionContext(); - - // For colors and progress events, the format entity needs access to the - // debugger, which requires a target in the execution context. - if (!exe_ctx.HasTargetScope()) - exe_ctx.SetTargetPtr(&m_debugger.GetSelectedOrDummyTarget()); - - SymbolContext symbol_ctx; - if (ProcessSP process_sp = exe_ctx.GetProcessSP()) { - // Check if the process is stopped, and if it is, make sure it remains - // stopped until we've computed the symbol context. - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process_sp->GetRunLock())) { - if (auto frame_sp = exe_ctx.GetFrameSP()) - symbol_ctx = frame_sp->GetSymbolContext(eSymbolContextEverything); - } +void Statusline::Redraw(std::optional exe_ctx_ref) { + // Update the cached execution context. + if (exe_ctx_ref) + m_exe_ctx_ref = *exe_ctx_ref; + + // Lock the execution context. + ExecutionContext exe_ctx = + m_exe_ctx_ref.Lock(/*thread_and_frame_only_if_stopped=*/false); + + // Compute the symbol context if we're stopped. + SymbolContext sym_ctx; + llvm::Expected stopped_exe_ctx = + GetStoppedExecutionContext(&m_exe_ctx_ref); + if (stopped_exe_ctx) { + // The StoppedExecutionContext only ensures that we hold the run lock. + // The process could be in an exited or unloaded state and have no frame. + if (auto frame_sp = stopped_exe_ctx->GetFrameSP()) + sym_ctx = frame_sp->GetSymbolContext(eSymbolContextEverything); + } else { + // We can draw the statusline without being stopped. + llvm::consumeError(stopped_exe_ctx.takeError()); } StreamString stream; FormatEntity::Entry format = m_debugger.GetStatuslineFormat(); - FormatEntity::Format(format, stream, &symbol_ctx, &exe_ctx, nullptr, nullptr, + FormatEntity::Format(format, stream, &sym_ctx, &exe_ctx, nullptr, nullptr, false, false); Draw(stream.GetString().str()); diff --git a/lldb/source/Target/ExecutionContext.cpp b/lldb/source/Target/ExecutionContext.cpp index 9d232e420f71c..a795913047639 100644 --- a/lldb/source/Target/ExecutionContext.cpp +++ b/lldb/source/Target/ExecutionContext.cpp @@ -429,6 +429,16 @@ ExecutionContextRef::ExecutionContextRef(Target *target, bool adopt_selected) SetTargetPtr(target, adopt_selected); } +ExecutionContextRef::ExecutionContextRef(Process *process, bool adopt_selected) + : m_target_wp(), m_process_wp(), m_thread_wp(), m_stack_id() { + SetProcessPtr(process, adopt_selected); +} + +ExecutionContextRef::ExecutionContextRef(Thread *thread, bool adopt_selected) + : m_target_wp(), m_process_wp(), m_thread_wp(), m_stack_id() { + SetThreadPtr(thread, adopt_selected); +} + ExecutionContextRef::ExecutionContextRef(const ExecutionContextRef &rhs) = default; @@ -513,55 +523,66 @@ void ExecutionContextRef::SetFrameSP(const lldb::StackFrameSP &frame_sp) { void ExecutionContextRef::SetTargetPtr(Target *target, bool adopt_selected) { Clear(); if (target) { - lldb::TargetSP target_sp(target->shared_from_this()); - if (target_sp) { - m_target_wp = target_sp; - if (adopt_selected) { - lldb::ProcessSP process_sp(target_sp->GetProcessSP()); - if (process_sp) { - m_process_wp = process_sp; - if (process_sp) { - // Only fill in the thread and frame if our process is stopped - // Don't just check the state, since we might be in the middle of - // resuming. - Process::StopLocker stop_locker; - - if (stop_locker.TryLock(&process_sp->GetRunLock()) && - StateIsStoppedState(process_sp->GetState(), true)) { - lldb::ThreadSP thread_sp( - process_sp->GetThreadList().GetSelectedThread()); - if (!thread_sp) - thread_sp = process_sp->GetThreadList().GetThreadAtIndex(0); - - if (thread_sp) { - SetThreadSP(thread_sp); - lldb::StackFrameSP frame_sp( - thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame)); - if (!frame_sp) - frame_sp = thread_sp->GetStackFrameAtIndex(0); - if (frame_sp) - SetFrameSP(frame_sp); - } - } - } - } - } + lldb::TargetSP target_sp = target->shared_from_this(); + SetTargetSP(target_sp); + if (adopt_selected) { + if (lldb::ProcessSP process_sp = target_sp->GetProcessSP()) + SetProcessPtr(process_sp.get(), adopt_selected); } } } -void ExecutionContextRef::SetProcessPtr(Process *process) { +void ExecutionContextRef::SetProcessPtr(Process *process, bool adopt_selected) { if (process) { - SetProcessSP(process->shared_from_this()); + lldb::ProcessSP process_sp = process->shared_from_this(); + SetProcessSP(process_sp); + if (adopt_selected) { + // Only fill in the thread if our process is stopped. + // Don't just check the state, since we might be in the middle of + // resuming. + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock()) && + StateIsStoppedState(process_sp->GetState(), true)) { + lldb::ThreadSP thread_sp( + process_sp->GetThreadList().GetSelectedThread()); + if (!thread_sp) + thread_sp = process_sp->GetThreadList().GetThreadAtIndex(0); + if (thread_sp) { + SetThreadSP(thread_sp); + lldb::StackFrameSP frame_sp = + thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame); + if (!frame_sp) + frame_sp = thread_sp->GetStackFrameAtIndex(0); + if (frame_sp) + SetFrameSP(frame_sp); + } + } + } } else { m_process_wp.reset(); m_target_wp.reset(); } } -void ExecutionContextRef::SetThreadPtr(Thread *thread) { +void ExecutionContextRef::SetThreadPtr(Thread *thread, bool adopt_selected) { if (thread) { - SetThreadSP(thread->shared_from_this()); + lldb::ThreadSP thread_sp = thread->shared_from_this(); + SetThreadSP(thread_sp); + if (adopt_selected) { + // Only fill in the frame if our process is stopped. + // Don't just check the state, since we might be in the middle of + // resuming. + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&thread->GetProcess()->GetRunLock()) && + StateIsStoppedState(thread->GetProcess()->GetState(), true)) { + lldb::StackFrameSP frame_sp = + thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame); + if (!frame_sp) + frame_sp = thread_sp->GetStackFrameAtIndex(0); + if (frame_sp) + SetFrameSP(frame_sp); + } + } } else { ClearThread(); m_process_wp.reset();