[Lldb-commits] [lldb] b26e1ef - [LLDB][GUI] Add Breakpoints window
Greg Clayton via lldb-commits
lldb-commits at lists.llvm.org
Tue Aug 17 17:38:25 PDT 2021
Author: Omar Emara
Date: 2021-08-17T17:38:16-07:00
New Revision: b26e1efc424ad840143f02a96246cc666ee99e72
URL: https://github.com/llvm/llvm-project/commit/b26e1efc424ad840143f02a96246cc666ee99e72
DIFF: https://github.com/llvm/llvm-project/commit/b26e1efc424ad840143f02a96246cc666ee99e72.diff
LOG: [LLDB][GUI] Add Breakpoints window
This patch adds a breakpoints window that lists all breakpoints and
breakpoints locations. The window is implemented as a tree, where the
first level is the breakpoints and the second level is breakpoints
locations.
The tree delegate was hardcoded to only draw when there is a process,
which is not necessary for breakpoints, so the relevant logic was
abstracted in the TreeDelegateShouldDraw method.
Reviewed By: clayborg
Differential Revision: https://reviews.llvm.org/D107386
Added:
Modified:
lldb/source/Core/IOHandlerCursesGUI.cpp
Removed:
################################################################################
diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp
index f1872af65b92..0ae41dee7f9d 100644
--- a/lldb/source/Core/IOHandlerCursesGUI.cpp
+++ b/lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -45,6 +45,7 @@
#include "lldb/Core/ValueObject.h"
#include "lldb/Core/ValueObjectRegister.h"
#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/VariableList.h"
@@ -3764,9 +3765,14 @@ class TreeDelegate {
TreeItem *&selected_item) {
return;
}
- virtual bool TreeDelegateItemSelected(
- TreeItem &item) = 0; // Return true if we need to update views
+ // This is invoked when a tree item is selected. If true is returned, the
+ // views are updated.
+ virtual bool TreeDelegateItemSelected(TreeItem &item) = 0;
virtual bool TreeDelegateExpandRootByDefault() { return false; }
+ // This is mostly useful for root tree delegates. If false is returned,
+ // drawing will be skipped completely. This is needed, for instance, in
+ // skipping drawing of the threads tree if there is no running process.
+ virtual bool TreeDelegateShouldDraw() { return true; }
};
typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
@@ -3956,6 +3962,16 @@ class TreeItem {
void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
+ const std::string &GetText() const { return m_text; }
+
+ void SetText(const char *text) {
+ if (text == nullptr) {
+ m_text.clear();
+ return;
+ }
+ m_text = text;
+ }
+
void SetMightHaveChildren(bool b) { m_might_have_children = b; }
protected:
@@ -3963,6 +3979,7 @@ class TreeItem {
TreeDelegate &m_delegate;
void *m_user_data;
uint64_t m_identifier;
+ std::string m_text;
int m_row_idx; // Zero based visible row index, -1 if not visible or for the
// root item
std::vector<TreeItem> m_children;
@@ -3981,21 +3998,6 @@ class TreeWindowDelegate : public WindowDelegate {
int NumVisibleRows() const { return m_max_y - m_min_y; }
bool WindowDelegateDraw(Window &window, bool force) override {
- ExecutionContext exe_ctx(
- m_debugger.GetCommandInterpreter().GetExecutionContext());
- Process *process = exe_ctx.GetProcessPtr();
-
- bool display_content = false;
- if (process) {
- StateType state = process->GetState();
- if (StateIsStoppedState(state, true)) {
- // We are stopped, so it is ok to
- display_content = true;
- } else if (StateIsRunningState(state)) {
- return true; // Don't do any updating when we are running
- }
- }
-
m_min_x = 2;
m_min_y = 1;
m_max_x = window.GetWidth() - 1;
@@ -4004,35 +4006,36 @@ class TreeWindowDelegate : public WindowDelegate {
window.Erase();
window.DrawTitleBox(window.GetName());
- if (display_content) {
- 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
- // rows by setting the first visible row accordingly.
- if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
- m_first_visible_row = 0;
-
- // Make sure the selected row is always visible
- if (m_selected_row_idx < m_first_visible_row)
- m_first_visible_row = m_selected_row_idx;
- else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
- m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
-
- int row_idx = 0;
- int num_rows_left = num_visible_rows;
- m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
- num_rows_left);
- // Get the selected row
- m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
- } else {
+ if (!m_delegate_sp->TreeDelegateShouldDraw()) {
m_selected_item = nullptr;
+ return true;
}
+ 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
+ // rows by setting the first visible row accordingly.
+ if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
+ m_first_visible_row = 0;
+
+ // Make sure the selected row is always visible
+ if (m_selected_row_idx < m_first_visible_row)
+ m_first_visible_row = m_selected_row_idx;
+ else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
+ m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
+
+ int row_idx = 0;
+ int num_rows_left = num_visible_rows;
+ m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
+ num_rows_left);
+ // Get the selected row
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+
return true; // Drawing handled
}
@@ -4160,6 +4163,23 @@ class TreeWindowDelegate : public WindowDelegate {
int m_max_y;
};
+// A tree delegate that just draws the text member of the tree item, it doesn't
+// have any children or actions.
+class TextTreeDelegate : public TreeDelegate {
+public:
+ TextTreeDelegate() : TreeDelegate() {}
+
+ ~TextTreeDelegate() override = default;
+
+ void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
+ window.PutCStringTruncated(1, item.GetText().c_str());
+ }
+
+ void TreeDelegateGenerateChildren(TreeItem &item) override {}
+
+ bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
+};
+
class FrameTreeDelegate : public TreeDelegate {
public:
FrameTreeDelegate() : TreeDelegate() {
@@ -4324,6 +4344,17 @@ class ThreadsTreeDelegate : public TreeDelegate {
.GetProcessSP();
}
+ bool TreeDelegateShouldDraw() override {
+ ProcessSP process = GetProcess();
+ if (!process)
+ return false;
+
+ if (StateIsRunningState(process->GetState()))
+ return false;
+
+ return true;
+ }
+
void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
ProcessSP process_sp = GetProcess();
if (process_sp && process_sp->IsAlive()) {
@@ -4415,6 +4446,240 @@ class ThreadsTreeDelegate : public TreeDelegate {
FormatEntity::Entry m_format;
};
+class BreakpointLocationTreeDelegate : public TreeDelegate {
+public:
+ BreakpointLocationTreeDelegate(Debugger &debugger)
+ : TreeDelegate(), m_debugger(debugger) {}
+
+ ~BreakpointLocationTreeDelegate() override = default;
+
+ Process *GetProcess() {
+ ExecutionContext exe_ctx(
+ m_debugger.GetCommandInterpreter().GetExecutionContext());
+ return exe_ctx.GetProcessPtr();
+ }
+
+ BreakpointLocationSP GetBreakpointLocation(const TreeItem &item) {
+ Breakpoint *breakpoint = (Breakpoint *)item.GetUserData();
+ return breakpoint->GetLocationAtIndex(item.GetIdentifier());
+ }
+
+ void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
+ BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
+ Process *process = GetProcess();
+ StreamString stream;
+ stream.Printf("%i.%i: ", breakpoint_location->GetBreakpoint().GetID(),
+ breakpoint_location->GetID());
+ Address address = breakpoint_location->GetAddress();
+ address.Dump(&stream, process, Address::DumpStyleResolvedDescription,
+ Address::DumpStyleInvalid);
+ window.PutCStringTruncated(1, stream.GetString().str().c_str());
+ }
+
+ StringList ComputeDetailsList(BreakpointLocationSP breakpoint_location) {
+ StringList details;
+
+ Address address = breakpoint_location->GetAddress();
+ SymbolContext symbol_context;
+ address.CalculateSymbolContext(&symbol_context);
+
+ if (symbol_context.module_sp) {
+ StreamString module_stream;
+ module_stream.PutCString("module = ");
+ symbol_context.module_sp->GetFileSpec().Dump(
+ module_stream.AsRawOstream());
+ details.AppendString(module_stream.GetString());
+ }
+
+ if (symbol_context.comp_unit != nullptr) {
+ StreamString compile_unit_stream;
+ compile_unit_stream.PutCString("compile unit = ");
+ symbol_context.comp_unit->GetPrimaryFile().GetFilename().Dump(
+ &compile_unit_stream);
+ details.AppendString(compile_unit_stream.GetString());
+
+ if (symbol_context.function != nullptr) {
+ StreamString function_stream;
+ function_stream.PutCString("function = ");
+ function_stream.PutCString(
+ symbol_context.function->GetName().AsCString("<unknown>"));
+ details.AppendString(function_stream.GetString());
+ }
+
+ if (symbol_context.line_entry.line > 0) {
+ StreamString location_stream;
+ location_stream.PutCString("location = ");
+ symbol_context.line_entry.DumpStopContext(&location_stream, true);
+ details.AppendString(location_stream.GetString());
+ }
+
+ } else {
+ if (symbol_context.symbol) {
+ StreamString symbol_stream;
+ if (breakpoint_location->IsReExported())
+ symbol_stream.PutCString("re-exported target = ");
+ else
+ symbol_stream.PutCString("symbol = ");
+ symbol_stream.PutCString(
+ symbol_context.symbol->GetName().AsCString("<unknown>"));
+ details.AppendString(symbol_stream.GetString());
+ }
+ }
+
+ Process *process = GetProcess();
+
+ StreamString address_stream;
+ address.Dump(&address_stream, process, Address::DumpStyleLoadAddress,
+ Address::DumpStyleModuleWithFileAddress);
+ details.AppendString(address_stream.GetString());
+
+ BreakpointSiteSP breakpoint_site = breakpoint_location->GetBreakpointSite();
+ if (breakpoint_location->IsIndirect() && breakpoint_site) {
+ Address resolved_address;
+ resolved_address.SetLoadAddress(breakpoint_site->GetLoadAddress(),
+ &breakpoint_location->GetTarget());
+ Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
+ if (resolved_symbol) {
+ StreamString indirect_target_stream;
+ indirect_target_stream.PutCString("indirect target = ");
+ indirect_target_stream.PutCString(
+ resolved_symbol->GetName().GetCString());
+ details.AppendString(indirect_target_stream.GetString());
+ }
+ }
+
+ bool is_resolved = breakpoint_location->IsResolved();
+ StreamString resolved_stream;
+ resolved_stream.Printf("resolved = %s", is_resolved ? "true" : "false");
+ details.AppendString(resolved_stream.GetString());
+
+ bool is_hardware = is_resolved && breakpoint_site->IsHardware();
+ StreamString hardware_stream;
+ hardware_stream.Printf("hardware = %s", is_hardware ? "true" : "false");
+ details.AppendString(hardware_stream.GetString());
+
+ StreamString hit_count_stream;
+ hit_count_stream.Printf("hit count = %-4u",
+ breakpoint_location->GetHitCount());
+ details.AppendString(hit_count_stream.GetString());
+
+ return details;
+ }
+
+ void TreeDelegateGenerateChildren(TreeItem &item) override {
+ BreakpointLocationSP breakpoint_location = GetBreakpointLocation(item);
+ StringList details = ComputeDetailsList(breakpoint_location);
+
+ 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);
+ for (size_t i = 0; i < details.GetSize(); i++) {
+ item[i].SetText(details.GetStringAtIndex(i));
+ }
+ }
+
+ bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
+
+protected:
+ Debugger &m_debugger;
+ std::shared_ptr<TextTreeDelegate> m_string_delegate_sp;
+};
+
+class BreakpointTreeDelegate : public TreeDelegate {
+public:
+ BreakpointTreeDelegate(Debugger &debugger)
+ : TreeDelegate(), m_debugger(debugger),
+ m_breakpoint_location_delegate_sp() {}
+
+ ~BreakpointTreeDelegate() override = default;
+
+ BreakpointSP GetBreakpoint(const TreeItem &item) {
+ TargetSP target = m_debugger.GetSelectedTarget();
+ BreakpointList &breakpoints = target->GetBreakpointList(false);
+ return breakpoints.GetBreakpointAtIndex(item.GetIdentifier());
+ }
+
+ void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
+ BreakpointSP breakpoint = GetBreakpoint(item);
+ StreamString stream;
+ stream.Format("{0}: ", breakpoint->GetID());
+ breakpoint->GetResolverDescription(&stream);
+ breakpoint->GetFilterDescription(&stream);
+ window.PutCStringTruncated(1, stream.GetString().str().c_str());
+ }
+
+ void TreeDelegateGenerateChildren(TreeItem &item) override {
+ BreakpointSP breakpoint = GetBreakpoint(item);
+
+ 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);
+ for (size_t i = 0; i < breakpoint->GetNumLocations(); i++) {
+ item[i].SetIdentifier(i);
+ item[i].SetUserData(breakpoint.get());
+ }
+ }
+
+ bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
+
+protected:
+ Debugger &m_debugger;
+ std::shared_ptr<BreakpointLocationTreeDelegate>
+ m_breakpoint_location_delegate_sp;
+};
+
+class BreakpointsTreeDelegate : public TreeDelegate {
+public:
+ BreakpointsTreeDelegate(Debugger &debugger)
+ : TreeDelegate(), m_debugger(debugger), m_breakpoint_delegate_sp() {}
+
+ ~BreakpointsTreeDelegate() override = default;
+
+ bool TreeDelegateShouldDraw() override {
+ TargetSP target = m_debugger.GetSelectedTarget();
+ if (!target)
+ return false;
+
+ return true;
+ }
+
+ void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
+ window.PutCString("Breakpoints");
+ }
+
+ void TreeDelegateGenerateChildren(TreeItem &item) override {
+ TargetSP target = m_debugger.GetSelectedTarget();
+
+ BreakpointList &breakpoints = target->GetBreakpointList(false);
+ std::unique_lock<std::recursive_mutex> lock;
+ breakpoints.GetListMutex(lock);
+
+ 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);
+ for (size_t i = 0; i < breakpoints.GetSize(); i++) {
+ item[i].SetIdentifier(i);
+ }
+ }
+
+ bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
+
+ bool TreeDelegateExpandRootByDefault() override { return true; }
+
+protected:
+ Debugger &m_debugger;
+ std::shared_ptr<BreakpointTreeDelegate> m_breakpoint_delegate_sp;
+};
+
class ValueObjectListDelegate : public WindowDelegate {
public:
ValueObjectListDelegate() : m_rows() {}
@@ -5216,6 +5481,7 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
eMenuID_ViewRegisters,
eMenuID_ViewSource,
eMenuID_ViewVariables,
+ eMenuID_ViewBreakpoints,
eMenuID_Help,
eMenuID_HelpGUIHelp
@@ -5430,8 +5696,8 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
// previously added
submenus.erase(submenus.begin() + 7, submenus.end());
}
- // Since we are adding and removing items we need to recalculate the name
- // lengths
+ // Since we are adding and removing items we need to recalculate the
+ // name lengths
menu.RecalculateNameLengths();
}
return MenuActionResult::Handled;
@@ -5539,6 +5805,39 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
}
return MenuActionResult::Handled;
+ case eMenuID_ViewBreakpoints: {
+ WindowSP main_window_sp = m_app.GetMainWindow();
+ WindowSP threads_window_sp = main_window_sp->FindSubWindow("Threads");
+ WindowSP breakpoints_window_sp =
+ main_window_sp->FindSubWindow("Breakpoints");
+ const Rect threads_bounds = threads_window_sp->GetBounds();
+
+ // If a breakpoints window already exists, remove it and give the area
+ // it used to occupy to the threads window. If it doesn't exist, split
+ // the threads window horizontally into two windows where the top window
+ // is the threads window and the bottom window is a newly added
+ // breakpoints window.
+ if (breakpoints_window_sp) {
+ threads_window_sp->Resize(threads_bounds.size.width,
+ threads_bounds.size.height +
+ breakpoints_window_sp->GetHeight());
+ main_window_sp->RemoveSubWindow(breakpoints_window_sp.get());
+ } else {
+ Rect new_threads_bounds, breakpoints_bounds;
+ threads_bounds.HorizontalSplitPercentage(0.70, new_threads_bounds,
+ breakpoints_bounds);
+ threads_window_sp->SetBounds(new_threads_bounds);
+ breakpoints_window_sp = main_window_sp->CreateSubWindow(
+ "Breakpoints", breakpoints_bounds, false);
+ TreeDelegateSP breakpoints_delegate_sp(
+ new BreakpointsTreeDelegate(m_debugger));
+ breakpoints_window_sp->SetDelegate(WindowDelegateSP(
+ new TreeWindowDelegate(m_debugger, breakpoints_delegate_sp)));
+ }
+ touchwin(stdscr);
+ return MenuActionResult::Handled;
+ }
+
case eMenuID_HelpGUIHelp:
m_app.GetMainWindow()->CreateHelpSubwindow();
return MenuActionResult::Handled;
@@ -5731,8 +6030,8 @@ class SourceFileWindowDelegate : public WindowDelegate {
m_selected_line = m_pc_line;
if (m_file_sp && m_file_sp->GetFileSpec() == m_sc.line_entry.file) {
- // Same file, nothing to do, we should either have the lines or not
- // (source file missing)
+ // Same file, nothing to do, we should either have the lines or
+ // not (source file missing)
if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
if (m_selected_line >= m_first_visible_line + num_visible_lines)
m_first_visible_line = m_selected_line - 10;
@@ -5854,8 +6153,8 @@ class SourceFileWindowDelegate : public WindowDelegate {
window.MoveCursor(1, line_y);
const bool is_pc_line = curr_line == m_pc_line;
const bool line_is_selected = m_selected_line == curr_line;
- // Highlight the line as the PC line first, then if the selected line
- // isn't the same as the PC line, highlight it
diff erently
+ // Highlight the line as the PC line first, then if the selected
+ // line isn't the same as the PC line, highlight it
diff erently
attr_t highlight_attr = 0;
attr_t bp_attr = 0;
if (is_pc_line)
@@ -5994,8 +6293,8 @@ class SourceFileWindowDelegate : public WindowDelegate {
window.MoveCursor(1, line_y);
const bool is_pc_line = frame_sp && inst_idx == pc_idx;
const bool line_is_selected = m_selected_line == inst_idx;
- // Highlight the line as the PC line first, then if the selected line
- // isn't the same as the PC line, highlight it
diff erently
+ // Highlight the line as the PC line first, then if the selected
+ // line isn't the same as the PC line, highlight it
diff erently
attr_t highlight_attr = 0;
attr_t bp_attr = 0;
if (is_pc_line)
@@ -6459,7 +6758,7 @@ void IOHandlerCursesGUI::Activate() {
MenuSP view_menu_sp(
new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
view_menu_sp->AddSubmenu(
- MenuSP(new Menu("Backtrace", nullptr, 'b',
+ MenuSP(new Menu("Backtrace", nullptr, 't',
ApplicationDelegate::eMenuID_ViewBacktrace)));
view_menu_sp->AddSubmenu(
MenuSP(new Menu("Registers", nullptr, 'r',
@@ -6469,6 +6768,9 @@ void IOHandlerCursesGUI::Activate() {
view_menu_sp->AddSubmenu(
MenuSP(new Menu("Variables", nullptr, 'v',
ApplicationDelegate::eMenuID_ViewVariables)));
+ view_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Breakpoints", nullptr, 'b',
+ ApplicationDelegate::eMenuID_ViewBreakpoints)));
MenuSP help_menu_sp(
new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
@@ -6529,7 +6831,8 @@ void IOHandlerCursesGUI::Activate() {
status_window_sp->SetDelegate(
WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
- // Show the main help window once the first time the curses GUI is launched
+ // Show the main help window once the first time the curses GUI is
+ // launched
static bool g_showed_help = false;
if (!g_showed_help) {
g_showed_help = true;
More information about the lldb-commits
mailing list