kpdev42 created this revision. kpdev42 added reviewers: clayborg, davide, k8stone. kpdev42 added a project: LLDB. Herald added a subscriber: JDevlieghere. Herald added a project: All. kpdev42 requested review of this revision. Herald added a subscriber: lldb-commits.
Before this patch, any time TreeItem is copied in Resize method, its parent is not updated, which can cause crashes when, for example, thread window with multiple hierarchy levels is updated. Makes TreeItem move-only, removes TreeItem's m_delegate extra self-assignment by making it a pointer, adds code to fix up children's parent on move constructor and operator= ~~~ Huawei RRI, OS Lab Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D157960 Files: lldb/source/Core/IOHandlerCursesGUI.cpp lldb/test/API/commands/gui/spawn-threads/Makefile lldb/test/API/commands/gui/spawn-threads/TestGuiSpawnThreads.py lldb/test/API/commands/gui/spawn-threads/main.cpp
Index: lldb/test/API/commands/gui/spawn-threads/main.cpp =================================================================== --- /dev/null +++ lldb/test/API/commands/gui/spawn-threads/main.cpp @@ -0,0 +1,25 @@ +#include <iostream> +#include <thread> +#include <vector> + +#include "pseudo_barrier.h" + +pseudo_barrier_t barrier_inside; + +void thread_func() { pseudo_barrier_wait(barrier_inside); } + +void test_thread() { + std::vector<std::thread> thrs; + for (int i = 0; i < 5; i++) + thrs.push_back(std::thread(thread_func)); // break here + + pseudo_barrier_wait(barrier_inside); // break before join + for (auto &t : thrs) + t.join(); +} + +int main() { + pseudo_barrier_init(barrier_inside, 6); + test_thread(); + return 0; +} Index: lldb/test/API/commands/gui/spawn-threads/TestGuiSpawnThreads.py =================================================================== --- /dev/null +++ lldb/test/API/commands/gui/spawn-threads/TestGuiSpawnThreads.py @@ -0,0 +1,47 @@ +""" +Test that 'gui' does not crash when adding new threads, which +populate TreeItem's children and may be reallocated elsewhere. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test.lldbpexpect import PExpectTest + +import sys + +class TestGuiSpawnThreadsTest(PExpectTest): + # 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 -f main.cpp -p "break here"', substrs=['Breakpoint 1', 'address ='] + ) + self.expect( + 'breakpoint set -f main.cpp -p "before join"', substrs=['Breakpoint 2', 'address ='] + ) + self.expect("run", substrs=["stop reason ="]) + + escape_key = chr(27).encode() + + # Start the GUI + self.child.sendline("gui") + self.child.expect_exact("Threads") + self.child.expect_exact(f"thread #1: tid =") + + for i in range(5): + # Stopped at the breakpoit, continue over the thread creation + self.child.send("c") + # Check the newly created thread + self.child.expect_exact(f"thread #{i + 2}: tid =") + + # Exit GUI. + self.child.send(escape_key) + self.expect_prompt() + + self.quit() Index: lldb/test/API/commands/gui/spawn-threads/Makefile =================================================================== --- /dev/null +++ lldb/test/API/commands/gui/spawn-threads/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include Makefile.rules Index: lldb/source/Core/IOHandlerCursesGUI.cpp =================================================================== --- lldb/source/Core/IOHandlerCursesGUI.cpp +++ lldb/source/Core/IOHandlerCursesGUI.cpp @@ -4614,30 +4614,48 @@ typedef std::shared_ptr<TreeDelegate> TreeDelegateSP; -class TreeItem { +struct TreeItemData { + TreeItemData(TreeItem *parent, TreeDelegate &delegate, + bool might_have_children, bool is_expanded) + : m_parent(parent), m_delegate(&delegate), + m_might_have_children(might_have_children), m_is_expanded(is_expanded) { + } + +protected: + TreeItem *m_parent; + TreeDelegate *m_delegate; + void *m_user_data = nullptr; + uint64_t m_identifier = 0; + std::string m_text; + int m_row_idx = -1; // Zero based visible row index, -1 if not visible or for + // the root item + bool m_might_have_children; + bool m_is_expanded = false; +}; + +class TreeItem : public TreeItemData { public: TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children) - : m_parent(parent), m_delegate(delegate), m_children(), - m_might_have_children(might_have_children) { - if (m_parent == nullptr) - m_is_expanded = m_delegate.TreeDelegateExpandRootByDefault(); - } + : TreeItemData(parent, delegate, might_have_children, + parent == nullptr + ? delegate.TreeDelegateExpandRootByDefault() + : false), + m_children() {} + + TreeItem(const TreeItem &) = delete; + TreeItem &operator=(const TreeItem &rhs) = delete; - TreeItem &operator=(const TreeItem &rhs) { + TreeItem &operator=(TreeItem &&rhs) { if (this != &rhs) { - m_parent = rhs.m_parent; - m_delegate = rhs.m_delegate; - m_user_data = rhs.m_user_data; - m_identifier = rhs.m_identifier; - m_row_idx = rhs.m_row_idx; - m_children = rhs.m_children; - m_might_have_children = rhs.m_might_have_children; - m_is_expanded = rhs.m_is_expanded; + TreeItemData::operator=(std::move(rhs)); + AdoptChildren(rhs.m_children); } return *this; } - TreeItem(const TreeItem &) = default; + TreeItem(TreeItem &&rhs) : TreeItemData(std::move(rhs)) { + AdoptChildren(rhs.m_children); + } size_t GetDepth() const { if (m_parent) @@ -4649,18 +4667,28 @@ void ClearChildren() { m_children.clear(); } - void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); } + void Resize(size_t n, TreeDelegate &delegate, bool might_have_children) { + if (m_children.size() >= n) { + m_children.erase(m_children.begin() + n, m_children.end()); + return; + } + m_children.reserve(n); + std::generate_n(std::back_inserter(m_children), n - m_children.size(), + [&, parent = this]() { + return TreeItem(parent, delegate, might_have_children); + }); + } TreeItem &operator[](size_t i) { return m_children[i]; } void SetRowIndex(int row_idx) { m_row_idx = row_idx; } size_t GetNumChildren() { - m_delegate.TreeDelegateGenerateChildren(*this); + m_delegate->TreeDelegateGenerateChildren(*this); return m_children.size(); } - void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); } + void ItemWasSelected() { m_delegate->TreeDelegateItemSelected(*this); } void CalculateRowIndexes(int &row_idx) { SetRowIndex(row_idx); @@ -4727,7 +4755,7 @@ if (highlight) window.AttributeOn(A_REVERSE); - m_delegate.TreeDelegateDrawTreeItem(*this, window); + m_delegate->TreeDelegateDrawTreeItem(*this, window); if (highlight) window.AttributeOff(A_REVERSE); @@ -4811,16 +4839,13 @@ void SetMightHaveChildren(bool b) { m_might_have_children = b; } protected: - TreeItem *m_parent; - TreeDelegate &m_delegate; - void *m_user_data = nullptr; - uint64_t m_identifier = 0; - std::string m_text; - int m_row_idx = -1; // Zero based visible row index, -1 if not visible or for - // the root item + void AdoptChildren(std::vector<TreeItem> &children) { + m_children = std::move(children); + for (auto &child : m_children) + child.m_parent = this; + } + std::vector<TreeItem> m_children; - bool m_might_have_children; - bool m_is_expanded = false; }; class TreeWindowDelegate : public WindowDelegate { @@ -5117,9 +5142,8 @@ m_stop_id = process_sp->GetStopID(); m_tid = thread_sp->GetID(); - TreeItem t(&item, *m_frame_delegate_sp, false); size_t num_frames = thread_sp->GetStackFrameCount(); - item.Resize(num_frames, t); + item.Resize(num_frames, *m_frame_delegate_sp, false); for (size_t i = 0; i < num_frames; ++i) { item[i].SetUserData(thread_sp.get()); item[i].SetIdentifier(i); @@ -5219,12 +5243,11 @@ std::make_shared<ThreadTreeDelegate>(m_debugger); } - TreeItem t(&item, *m_thread_delegate_sp, false); ThreadList &threads = process_sp->GetThreadList(); std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); ThreadSP selected_thread = threads.GetSelectedThread(); size_t num_threads = threads.GetSize(); - item.Resize(num_threads, t); + item.Resize(num_threads, *m_thread_delegate_sp, false); for (size_t i = 0; i < num_threads; ++i) { ThreadSP thread = threads.GetThreadAtIndex(i); item[i].SetIdentifier(thread->GetID()); @@ -5404,9 +5427,8 @@ if (!m_string_delegate_sp) m_string_delegate_sp = std::make_shared<TextTreeDelegate>(); - TreeItem details_tree_item(&item, *m_string_delegate_sp, false); - item.Resize(details.GetSize(), details_tree_item); + item.Resize(details.GetSize(), *m_string_delegate_sp, false); for (size_t i = 0; i < details.GetSize(); i++) { item[i].SetText(details.GetStringAtIndex(i)); } @@ -5448,10 +5470,9 @@ if (!m_breakpoint_location_delegate_sp) m_breakpoint_location_delegate_sp = std::make_shared<BreakpointLocationTreeDelegate>(m_debugger); - TreeItem breakpoint_location_tree_item( - &item, *m_breakpoint_location_delegate_sp, true); - item.Resize(breakpoint->GetNumLocations(), breakpoint_location_tree_item); + item.Resize(breakpoint->GetNumLocations(), + *m_breakpoint_location_delegate_sp, true); for (size_t i = 0; i < breakpoint->GetNumLocations(); i++) { item[i].SetIdentifier(i); item[i].SetUserData(breakpoint.get()); @@ -5495,9 +5516,8 @@ if (!m_breakpoint_delegate_sp) m_breakpoint_delegate_sp = std::make_shared<BreakpointTreeDelegate>(m_debugger); - TreeItem breakpoint_tree_item(&item, *m_breakpoint_delegate_sp, true); - item.Resize(breakpoints.GetSize(), breakpoint_tree_item); + item.Resize(breakpoints.GetSize(), *m_breakpoint_delegate_sp, true); for (size_t i = 0; i < breakpoints.GetSize(); i++) { item[i].SetIdentifier(i); }
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits