diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp index e824f702aba21d..010f9300aa2e5e 100644 --- a/lldb/source/Core/IOHandlerCursesGUI.cpp +++ b/lldb/source/Core/IOHandlerCursesGUI.cpp @@ -3768,8 +3768,13 @@ class TreeDelegate { virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0; virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0; + virtual void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index, + TreeItem *&selected_item) { + return; + } virtual bool TreeDelegateItemSelected( TreeItem &item) = 0; // Return true if we need to update views + virtual bool TreeDelegateExpandRootByDefault() { return false; } }; typedef std::shared_ptr TreeDelegateSP; @@ -3779,7 +3784,10 @@ class TreeItem { TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children) : m_parent(parent), m_delegate(delegate), m_user_data(nullptr), m_identifier(0), m_row_idx(-1), m_children(), - m_might_have_children(might_have_children), m_is_expanded(false) {} + m_might_have_children(might_have_children), m_is_expanded(false) { + if (m_parent == nullptr) + m_is_expanded = m_delegate.TreeDelegateExpandRootByDefault(); + } TreeItem &operator=(const TreeItem &rhs) { if (this != &rhs) { @@ -4008,6 +4016,8 @@ class TreeWindowDelegate : public WindowDelegate { const int num_visible_rows = NumVisibleRows(); m_num_rows = 0; m_root.CalculateRowIndexes(m_num_rows); + m_delegate_sp->TreeDelegateUpdateSelection(m_root, m_selected_row_idx, + m_selected_item); // If we unexpanded while having something selected our total number of // rows is less than the num visible rows, then make sure we show all the @@ -4309,7 +4319,7 @@ class ThreadsTreeDelegate : public TreeDelegate { public: ThreadsTreeDelegate(Debugger &debugger) : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger), - m_stop_id(UINT32_MAX) { + m_stop_id(UINT32_MAX), m_update_selection(false) { FormatEntity::Parse("process ${process.id}{, name = ${process.name}}", m_format); } @@ -4337,6 +4347,7 @@ class ThreadsTreeDelegate : public TreeDelegate { void TreeDelegateGenerateChildren(TreeItem &item) override { ProcessSP process_sp = GetProcess(); + m_update_selection = false; if (process_sp && process_sp->IsAlive()) { StateType state = process_sp->GetState(); if (StateIsStoppedState(state, true)) { @@ -4345,6 +4356,7 @@ class ThreadsTreeDelegate : public TreeDelegate { return; // Children are already up to date m_stop_id = stop_id; + m_update_selection = true; if (!m_thread_delegate_sp) { // Always expand the thread item the first time we show it @@ -4356,11 +4368,15 @@ class ThreadsTreeDelegate : public TreeDelegate { TreeItem t(&item, *m_thread_delegate_sp, false); ThreadList &threads = process_sp->GetThreadList(); std::lock_guard guard(threads.GetMutex()); + ThreadSP selected_thread = threads.GetSelectedThread(); size_t num_threads = threads.GetSize(); item.Resize(num_threads, t); for (size_t i = 0; i < num_threads; ++i) { - item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID()); + ThreadSP thread = threads.GetThreadAtIndex(i); + item[i].SetIdentifier(thread->GetID()); item[i].SetMightHaveChildren(true); + if (selected_thread->GetID() == thread->GetID()) + item[i].Expand(); } return; } @@ -4368,12 +4384,42 @@ class ThreadsTreeDelegate : public TreeDelegate { item.ClearChildren(); } + void TreeDelegateUpdateSelection(TreeItem &root, int &selection_index, + TreeItem *&selected_item) override { + if (!m_update_selection) + return; + + ProcessSP process_sp = GetProcess(); + if (!(process_sp && process_sp->IsAlive())) + return; + + StateType state = process_sp->GetState(); + if (!StateIsStoppedState(state, true)) + return; + + ThreadList &threads = process_sp->GetThreadList(); + std::lock_guard guard(threads.GetMutex()); + ThreadSP selected_thread = threads.GetSelectedThread(); + size_t num_threads = threads.GetSize(); + for (size_t i = 0; i < num_threads; ++i) { + ThreadSP thread = threads.GetThreadAtIndex(i); + if (selected_thread->GetID() == thread->GetID()) { + selected_item = &root[i][thread->GetSelectedFrameIndex()]; + selection_index = selected_item->GetRowIndex(); + return; + } + } + } + bool TreeDelegateItemSelected(TreeItem &item) override { return false; } + bool TreeDelegateExpandRootByDefault() override { return true; } + protected: std::shared_ptr m_thread_delegate_sp; Debugger &m_debugger; uint32_t m_stop_id; + bool m_update_selection; FormatEntity::Entry m_format; }; diff --git a/lldb/test/API/commands/gui/expand-threads-tree/Makefile b/lldb/test/API/commands/gui/expand-threads-tree/Makefile new file mode 100644 index 00000000000000..0c11fbdd8669c3 --- /dev/null +++ b/lldb/test/API/commands/gui/expand-threads-tree/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c +ENABLE_THREADS := YES +include Makefile.rules diff --git a/lldb/test/API/commands/gui/expand-threads-tree/TestGuiExpandThreadsTree.py b/lldb/test/API/commands/gui/expand-threads-tree/TestGuiExpandThreadsTree.py new file mode 100644 index 00000000000000..0e095661d3ec8b --- /dev/null +++ b/lldb/test/API/commands/gui/expand-threads-tree/TestGuiExpandThreadsTree.py @@ -0,0 +1,55 @@ +""" +Test the 'gui' default thread tree expansion. +The root process tree item and the tree item corresponding to the selected +thread should be expanded by default. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test.lldbpexpect import PExpectTest + +class TestGuiExpandThreadsTree(PExpectTest): + + mydir = TestBase.compute_mydir(__file__) + + # PExpect uses many timeouts internally and doesn't play well + # under ASAN on a loaded machine.. + @skipIfAsan + @skipIfCursesSupportMissing + def test_gui(self): + self.build() + + self.launch(executable=self.getBuildArtifact("a.out"), dimensions=(100,500)) + self.expect("breakpoint set -r thread_start_routine", substrs=["Breakpoint 1", "address ="]) + self.expect("run", substrs=["stop reason ="]) + + escape_key = chr(27).encode() + + # Start the GUI and close the welcome window. + self.child.sendline("gui") + self.child.send(escape_key) + self.child.expect_exact("Threads") + + # The thread running thread_start_routine should be expanded. + self.child.expect_exact("frame #0: thread_start_routine") + + # Exit GUI. + self.child.send(escape_key) + self.expect_prompt() + + # Select the main thread. + self.child.sendline("thread select 1") + + # Start the GUI. + self.child.sendline("gui") + self.child.expect_exact("Threads") + + # The main thread should be expanded. + self.child.expect("frame #\d+: main") + + # Quit the GUI + self.child.send(escape_key) + + self.expect_prompt() + self.quit() diff --git a/lldb/test/API/commands/gui/expand-threads-tree/main.c b/lldb/test/API/commands/gui/expand-threads-tree/main.c new file mode 100644 index 00000000000000..32e6d17c799a62 --- /dev/null +++ b/lldb/test/API/commands/gui/expand-threads-tree/main.c @@ -0,0 +1,10 @@ +#include + +void *thread_start_routine(void *arg) { return NULL; } + +int main() { + pthread_t thread; + pthread_create(&thread, NULL, thread_start_routine, NULL); + pthread_join(thread, NULL); + return 0; +}