[Lldb-commits] [lldb] [lldb] Real-time console pane for output in lldb tui (PR #177160)
Nagesh Nazare via lldb-commits
lldb-commits at lists.llvm.org
Sat Feb 14 09:28:07 PST 2026
https://github.com/nageshnnazare updated https://github.com/llvm/llvm-project/pull/177160
>From 5b756054e45a6efc6792e031cd3d1ab33e416ac2 Mon Sep 17 00:00:00 2001
From: Nagesh Nazare <nageshnnazare at gmail.com>
Date: Wed, 21 Jan 2026 01:30:23 +0530
Subject: [PATCH 1/6] Adding Console window to lldb gui
This change implements the console window to output stdout / stderr
messages. Native lldb gui masks the output printed by the application
and it is difficult to debug if there are any required debug info
messages in the app.
---
lldb/source/Core/IOHandlerCursesGUI.cpp | 360 ++++++++++++++++++++++--
1 file changed, 342 insertions(+), 18 deletions(-)
diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp
index 53d71db9b3b0c..437584f2361c2 100644
--- a/lldb/source/Core/IOHandlerCursesGUI.cpp
+++ b/lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -6308,6 +6308,247 @@ HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
return eKeyHandled;
}
+class ConsoleOutputWindowDelegate : public WindowDelegate {
+private:
+ void PollProcessOutput() {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (!process || !process->IsAlive()) return;
+
+ // Buffer for reading output
+ char buffer[1024];
+ Status error;
+
+ // Read stdout
+ size_t stdout_bytes = process->GetSTDOUT(buffer, sizeof(buffer) - 1, error);
+ if (stdout_bytes > 0) {
+ buffer[stdout_bytes] = '\0';
+ AppendOutput(buffer, false);
+ }
+
+ // Read stderr
+ size_t stderr_bytes = process->GetSTDERR(buffer, sizeof(buffer) - 1, error);
+ if (stderr_bytes > 0) {
+ buffer[stderr_bytes] = '\0';
+ AppendOutput(buffer, true);
+ }
+ }
+
+ void AppendOutput(const char *text, bool is_stderr) {
+ if (!text || text[0] == '\0') return;
+
+ std::lock_guard<std::mutex> lock(m_output_mutex);
+
+ // Split text into lines and add to buffer
+ std::string remaining = m_partial_line;
+ remaining += text;
+
+ size_t start = 0, pos = 0;
+ while ((pos = remaining.find('\n', start)) != std::string::npos) {
+ std::string line = remaining.substr(start, pos - start);
+ if (is_stderr) {
+ line = "[stderr] " + line;
+ }
+ m_output_lines.push_back(line);
+
+ // Keep buffer size under limit
+ while (m_output_lines.size() > m_max_lines) {
+ m_output_lines.pop_front();
+ if (m_first_visible_line > 0) {
+ --m_first_visible_line;
+ }
+ }
+
+ start = pos + 1;
+ }
+
+ // Save any remaining partial line
+ m_partial_line = remaining.substr(start);
+
+ // Auto-scroll to bottom if enabled
+ if (m_auto_scroll && !m_output_lines.empty()) {
+ m_first_visible_line =
+ m_output_lines.size() > 0 ? m_output_lines.size() - 1 : 0;
+ }
+ }
+
+public:
+ ConsoleOutputWindowDelegate(Debugger &debugger)
+ : m_debugger(debugger), m_first_visible_line(0),
+ m_auto_scroll(true), m_max_lines(10000) {}
+
+ ~ConsoleOutputWindowDelegate() override = default;
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ // Poll for new output
+ PollProcessOutput();
+
+ std::lock_guard<std::mutex> lock(m_output_mutex);
+
+ window.Erase();
+ window.DrawTitleBox(window.GetName());
+
+ const int width = window.GetWidth();
+ const int height = window.GetHeight();
+
+ // Calculate the visible range
+ size_t total_lines = m_output_lines.size();
+ if (total_lines == 0) {
+ window.MoveCursor(2, 1);
+ window.PutCString("(no output yet)");
+ return true;
+ }
+
+ // Adjust scroll pos if needed
+ if (m_first_visible_line >= total_lines) {
+ m_first_visible_line =
+ total_lines > 0 ? total_lines - 1 : 0;
+ }
+
+ // Draw visible line
+ int visible_height = height - 2;
+ size_t start_line = m_first_visible_line;
+
+ // If we are at the end, display last N lines
+ if (m_auto_scroll || start_line + visible_height > total_lines) {
+ start_line =
+ total_lines > static_cast<size_t>(visible_height) ?
+ total_lines - visible_height : 0;
+ }
+
+ for (int row = 1; row <= visible_height &&
+ (start_line + row - 1) < total_lines; ++row) {
+ window.MoveCursor(2, row);
+ const std::string &line = m_output_lines[start_line + row - 1];
+
+ // Highlight stderr lines?
+ bool is_stderr = (line.find("[stderr]") == 0);
+ if (is_stderr) {
+ window.AttributeOn(COLOR_PAIR(2));
+ }
+
+ // Truncate line to fit window width
+ int available_width = width - 3;
+ if (static_cast<int>(line.length()) > available_width) {
+ window.PutCString(line.substr(0, available_width).c_str());
+ } else {
+ window.PutCString(line.c_str());
+ }
+
+ if (is_stderr) {
+ window.AttributeOff(COLOR_PAIR(2));
+ }
+ }
+
+ return true;
+ }
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
+ std::lock_guard<std::mutex> lock(m_output_mutex);
+
+ size_t total_lines = m_output_lines.size();
+ int visible_height = window.GetHeight() - 1;
+
+ switch (key) {
+ case KEY_UP:
+ if (m_first_visible_line > 0) {
+ --m_first_visible_line;
+ m_auto_scroll = false;
+ }
+ return eKeyHandled;
+
+ case KEY_DOWN:
+ if (m_first_visible_line + visible_height < total_lines) {
+ ++m_first_visible_line;
+ }
+ // Re-enable Auto-scroll at bottom
+ if (m_first_visible_line + visible_height >= total_lines) {
+ m_auto_scroll = true;
+ }
+ return eKeyHandled;
+
+ case KEY_PPAGE:
+ if (m_first_visible_line > static_cast<size_t>(visible_height)) {
+ m_first_visible_line -= visible_height;
+ } else {
+ m_first_visible_line = 0;
+ }
+ m_auto_scroll = false;
+ return eKeyHandled;
+
+ case KEY_NPAGE:
+ m_first_visible_line += visible_height;
+ if (m_first_visible_line + visible_height >= total_lines) {
+ m_first_visible_line = total_lines > static_cast<size_t>(visible_height)
+ ? total_lines - visible_height : 0;
+ m_auto_scroll = true;
+ }
+ return eKeyHandled;
+
+ case 'a':
+ m_auto_scroll = !m_auto_scroll;
+ if (m_auto_scroll && total_lines > 0) {
+ m_first_visible_line = total_lines > static_cast<size_t>(visible_height)
+ ? total_lines - visible_height : 0;
+ }
+ return eKeyHandled;
+
+ case 'c':
+ m_output_lines.clear();
+ m_partial_line.clear();
+ m_first_visible_line = 0;
+ return eKeyHandled;
+
+ case KEY_HOME:
+ m_first_visible_line = 0;
+ m_auto_scroll = false;
+ return eKeyHandled;
+
+ case KEY_END:
+ m_first_visible_line = total_lines > static_cast<size_t>(visible_height)
+ ? total_lines - visible_height : 0;
+ m_auto_scroll = true;
+ return eKeyHandled;
+
+ default:
+ break;
+ }
+
+ return eKeyNotHandled;
+ }
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Console Output view shows stdout and stderr from the process.";
+ }
+
+ KeyHelp *WindowDelegateGetKeyHelp() override {
+ static curses::KeyHelp g_source_view_key_help[] = {
+ {KEY_UP, "Scroll up"},
+ {KEY_DOWN, "Scroll down"},
+ {KEY_PPAGE, "Page up"},
+ {KEY_NPAGE, "Page down"},
+ {KEY_HOME, "Go to top"},
+ {KEY_END, "Go to bottom"},
+ {'h', "Show help dialog"},
+ {'a', "Toggle auto-scroll"},
+ {'c', "Clear output"},
+ {'\0', nullptr}};
+ return g_source_view_key_help;
+ }
+
+protected:
+ Debugger &m_debugger;
+ std::deque<std::string> m_output_lines;
+ std::string m_partial_line;
+ size_t m_first_visible_line = 0;
+ bool m_auto_scroll = true;
+ size_t m_max_lines = 10000;
+ std::mutex m_output_mutex;
+};
+
+
class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
public:
enum {
@@ -6339,6 +6580,7 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
eMenuID_ViewSource,
eMenuID_ViewVariables,
eMenuID_ViewBreakpoints,
+ eMenuId_ViewConsole,
eMenuID_Help,
eMenuID_HelpGUIHelp
@@ -6579,6 +6821,7 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
WindowSP main_window_sp = m_app.GetMainWindow();
WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
+ WindowSP console_window_sp = main_window_sp->FindSubWindow("Console");
WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
const Rect source_bounds = source_window_sp->GetBounds();
@@ -6587,39 +6830,50 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
main_window_sp->RemoveSubWindow(variables_window_sp.get());
- if (registers_window_sp) {
+ if (console_window_sp) {
+ Rect console_bounds = console_window_sp->GetBounds();
+ console_bounds.origin.x = variables_bounds.origin.x;
+ console_bounds.size.width = variables_bounds.size.width + console_bounds.size.width;
+ console_window_sp->SetBounds(console_bounds);
+ } else if (registers_window_sp) {
// We have a registers window, so give all the area back to the
// registers window
Rect registers_bounds = variables_bounds;
registers_bounds.size.width = source_bounds.size.width;
registers_window_sp->SetBounds(registers_bounds);
} else {
- // We have no registers window showing so give the bottom area back
+ // We have no console or registers window showing so give the bottom area back
// to the source view
source_window_sp->Resize(source_bounds.size.width,
source_bounds.size.height +
variables_bounds.size.height);
}
} else {
- Rect new_variables_rect;
- if (registers_window_sp) {
+ Rect new_vars_rect;
+ if (console_window_sp) {
+ // Console exists, so split the area
+ const Rect console_bounds = console_window_sp->GetBounds();
+ Rect new_console_rect;
+ console_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
+ new_console_rect);
+ } else if (registers_window_sp) {
// We have a registers window so split the area of the registers
// window into two columns where the left hand side will be the
// variables and the right hand side will be the registers
- const Rect variables_bounds = registers_window_sp->GetBounds();
- Rect new_registers_rect;
- variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
- new_registers_rect);
- registers_window_sp->SetBounds(new_registers_rect);
+ const Rect registers_bounds = registers_window_sp->GetBounds();
+ Rect new_regs_rect;
+ registers_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
+ new_regs_rect);
+ registers_window_sp->SetBounds(new_regs_rect);
} else {
- // No registers window, grab the bottom part of the source window
+ // No registers or console window, grab the bottom part of the source window
Rect new_source_rect;
source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
- new_variables_rect);
+ new_vars_rect);
source_window_sp->SetBounds(new_source_rect);
}
WindowSP new_window_sp = main_window_sp->CreateSubWindow(
- "Variables", new_variables_rect, false);
+ "Variables", new_vars_rect, false);
new_window_sp->SetDelegate(
WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
}
@@ -6678,6 +6932,67 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
}
return MenuActionResult::Handled;
+ case eMenuId_ViewConsole: {
+ WindowSP main_window_sp = m_app.GetMainWindow();
+ WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
+ WindowSP console_window_sp = main_window_sp->FindSubWindow("Console");
+ WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
+ WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
+ const Rect source_bounds = source_window_sp->GetBounds();
+
+ if (console_window_sp) {
+ const Rect console_bounds = console_window_sp->GetBounds();
+ main_window_sp->RemoveSubWindow(console_window_sp.get());
+
+ if (variables_window_sp) {
+ // Variables window exists, so give Console space to Variables
+ Rect variables_bounds = variables_window_sp->GetBounds();
+ variables_bounds.size.width = variables_bounds.size.width +
+ console_bounds.size.width;
+ variables_window_sp->SetBounds(variables_bounds);
+ } else if (registers_window_sp) {
+ // Registers window exists, so give Console space to Registers
+ Rect registers_bounds = registers_window_sp->GetBounds();
+ registers_bounds.size.width = source_bounds.size.width;
+ registers_window_sp->SetBounds(registers_bounds);
+ } else {
+ // No Variables or Registers window exists
+ source_window_sp->Resize(source_bounds.size.width,
+ source_bounds.size.height +
+ console_bounds.size.height);
+ }
+ } else {
+ Rect new_console_rect;
+ if (variables_window_sp) {
+ // Variable window exists, split area
+ const Rect variables_bounds = variables_window_sp->GetBounds();
+ Rect new_vars_rect;
+ variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
+ new_console_rect);
+ variables_window_sp->SetBounds(new_vars_rect);
+ } else if (registers_window_sp) {
+ // Registers window exists, split area
+ const Rect registers_bounds = registers_window_sp->GetBounds();
+ Rect new_regs_rect;
+ registers_bounds.VerticalSplitPercentage(0.50, new_console_rect,
+ new_regs_rect);
+ registers_window_sp->SetBounds(new_regs_rect);
+ } else {
+ // No Registers or Variables window exists, split source area
+ Rect new_source_rect;
+ source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
+ new_console_rect);
+ source_window_sp->SetBounds(new_source_rect);
+ }
+ WindowSP new_window_sp =
+ main_window_sp->CreateSubWindow("Console", new_console_rect, false);
+ new_window_sp->SetDelegate(
+ WindowDelegateSP(new ConsoleOutputWindowDelegate(m_debugger)));
+ }
+ touchwin(stdscr);
+ }
+ return MenuActionResult::Handled;
+
case eMenuID_ViewBreakpoints: {
WindowSP main_window_sp = m_app.GetMainWindow();
WindowSP threads_window_sp = main_window_sp->FindSubWindow("Threads");
@@ -7629,9 +7944,10 @@ void IOHandlerCursesGUI::Activate() {
"Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource));
view_menu_sp->AddSubmenu(std::make_shared<Menu>(
"Variables", nullptr, 'v', ApplicationDelegate::eMenuID_ViewVariables));
- view_menu_sp->AddSubmenu(
- std::make_shared<Menu>("Breakpoints", nullptr, 'b',
- ApplicationDelegate::eMenuID_ViewBreakpoints));
+ view_menu_sp->AddSubmenu(std::make_shared<Menu>(
+ "Breakpoints", nullptr, 'b', ApplicationDelegate::eMenuID_ViewBreakpoints));
+ view_menu_sp->AddSubmenu(std::make_shared<Menu>(
+ "Console", nullptr, 'o', ApplicationDelegate::eMenuId_ViewConsole));
MenuSP help_menu_sp(
new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
@@ -7655,12 +7971,16 @@ void IOHandlerCursesGUI::Activate() {
Rect status_bounds = content_bounds.MakeStatusBar();
Rect source_bounds;
Rect variables_bounds;
+ Rect console_bounds;
Rect threads_bounds;
Rect source_variables_bounds;
+ Rect variables_console_bounds;
content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
threads_bounds);
source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
- variables_bounds);
+ variables_console_bounds);
+ variables_console_bounds.VerticalSplitPercentage(0.50, variables_bounds,
+ console_bounds);
WindowSP menubar_window_sp =
main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
@@ -7672,10 +7992,12 @@ void IOHandlerCursesGUI::Activate() {
WindowSP source_window_sp(
main_window_sp->CreateSubWindow("Source", source_bounds, true));
- WindowSP variables_window_sp(
- main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
WindowSP threads_window_sp(
main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
+ WindowSP variables_window_sp(
+ main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
+ WindowSP console_window_sp(
+ main_window_sp->CreateSubWindow("Console", console_bounds, false));
WindowSP status_window_sp(
main_window_sp->CreateSubWindow("Status", status_bounds, false));
status_window_sp->SetCanBeActive(
@@ -7686,6 +8008,8 @@ void IOHandlerCursesGUI::Activate() {
WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
variables_window_sp->SetDelegate(
WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
+ console_window_sp->SetDelegate(
+ WindowDelegateSP(new ConsoleOutputWindowDelegate(m_debugger)));
TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
threads_window_sp->SetDelegate(WindowDelegateSP(
new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
>From 3db53482df85ad8478dccd7802ec3696eadd8cec Mon Sep 17 00:00:00 2001
From: Nagesh Nazare <nageshnnazare at gmail.com>
Date: Wed, 21 Jan 2026 21:50:21 +0530
Subject: [PATCH 2/6] added a testcase
---
.../API/commands/gui/console-output/Makefile | 2 +
.../console-output/TestGuiConsoleOutput.py | 212 ++++++++++++++++++
.../API/commands/gui/console-output/main.cpp | 25 +++
3 files changed, 239 insertions(+)
create mode 100644 lldb/test/API/commands/gui/console-output/Makefile
create mode 100644 lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
create mode 100644 lldb/test/API/commands/gui/console-output/main.cpp
diff --git a/lldb/test/API/commands/gui/console-output/Makefile b/lldb/test/API/commands/gui/console-output/Makefile
new file mode 100644
index 0000000000000..3d0b98f13f3d7
--- /dev/null
+++ b/lldb/test/API/commands/gui/console-output/Makefile
@@ -0,0 +1,2 @@
+CXX_SOURCES := main.cpp
+include Makefile.rules
diff --git a/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py b/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
new file mode 100644
index 0000000000000..d4445d825cca5
--- /dev/null
+++ b/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
@@ -0,0 +1,212 @@
+"""
+Test that the 'gui' console output pane displays stdout / stderr from the debugged process
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.lldbpexpect import PExpectTest
+
+
+class TestGuiConsoleOutputTest(PExpectTest):
+ # PExpect uses many timeouts internally and doesn't play well
+ # under ASAN on a loaded machine..
+ @skipIfAsan
+ @skipIfCursesSupportMissing
+ @skipIf(oslist=["linux"], archs=["arm$", "aarch64"])
+ def test_gui_console_output(self):
+ """Test that console pane prints messages"""
+ self.build()
+
+ self.launch(executable=self.getBuildArtifact("a.out"), dimensions=(100, 500))
+ self.expect(
+ 'br set -o true -f main.cpp -p "// break here begin"',
+ substrs=["Breakpoint 1", "address ="],
+ )
+ self.expect(
+ 'br set -o true -f main.cpp -p "// break here end"',
+ 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("Sources") # wait for gui
+
+ # Check for other gui elements in Menu bar
+ self.child.expect_exact("Target")
+ self.child.expect_exact("Process")
+ self.child.expect_exact("View")
+
+ # Check Console window exists
+ self.child.expect_exact("Console")
+
+ # The Console window show this message before continuing
+ self.child.expect_exact("(no output yet)")
+
+ # Continue program execution
+ self.child.send('c')
+
+ # Wait for Breakpoint 2
+ self.child.expect_exact("stop reason")
+
+ # Check console output for messages
+ self.child.expect_exact("Hello from stdout line 1")
+ self.child.expect_exact("Hello from stderr line 1")
+ self.child.expect_exact("Hello from stdout line 2")
+ self.child.expect_exact("Hello from stderr line 2")
+ self.child.expect_exact("Hello from stdout line 3")
+ self.child.expect_exact("Hello from stderr line 3")
+
+ # Press escape to quit the gui
+ self.child.send(escape_key)
+
+ self.expect_prompt()
+ self.quit()
+
+ @skipIfAsan
+ @skipIfCursesSupportMissing
+ @skipIf(oslist=["linux"], archs=["arm$", "aarch64"])
+ def test_gui_console_menu_toggle(self):
+ """Test that console pane can be toggled via View Menu"""
+ self.build()
+
+ self.launch(executable=self.getBuildArtifact("a.out"), dimensions=(100, 500))
+ self.expect(
+ 'br set -o true -f main.cpp -p "// break here begin"',
+ substrs=["Breakpoint 1", "address ="],
+ )
+ self.expect("run", substrs=["stop reason ="])
+
+ escape_key = chr(27).encode()
+
+ # Start the GUI.
+ self.child.sendline("gui")
+ self.child.expect_exact("Sources") # wait for gui
+
+ # Check Console window exists by default
+ self.child.expect_exact("Console")
+
+ # Open View Menu and toggle Console window off
+ self.child.send('v')
+ self.child.expect_exact("Console") # menu item should exist
+ self.child.send('o')
+
+ # Wait for gui update
+ import time
+ time.sleep(0.5)
+
+ # Open View Menu and toggle Console window on
+ self.child.send('v')
+ self.child.expect_exact("Console") # menu item should exist
+ self.child.send('o')
+
+ # Console window show re-appear
+ self.child.expect_exact("Console")
+
+ # Press escape to quit the gui
+ self.child.send(escape_key)
+
+ self.expect_prompt()
+ self.quit()
+
+ @skipIfAsan
+ @skipIfCursesSupportMissing
+ @skipIf(oslist=["linux"], archs=["arm$", "aarch64"])
+ def test_gui_console_navigate(self):
+ """Test that console pane navigation works"""
+ self.build()
+
+ self.launch(executable=self.getBuildArtifact("a.out"), dimensions=(100, 500))
+ self.expect(
+ 'br set -o true -f main.cpp -p "// break here first"',
+ substrs=["Breakpoint 1", "address ="],
+ )
+ self.expect(
+ 'br set -o true -f main.cpp -p "// break here end"',
+ substrs=["Breakpoint 2", "address ="],
+ )
+ self.expect("run", substrs=["stop reason ="])
+
+ escape_key = chr(27).encode()
+ tab_key = chr(9).encode()
+
+ # Start the GUI.
+ self.child.sendline("gui")
+ self.child.expect_exact("Sources") # wait for gui
+
+ # Check Console window exists by default
+ self.child.expect_exact("Console")
+
+ # The Console window show this message before continuing
+ self.child.expect_exact("(no output yet)")
+
+ # Continue program execution
+ self.child.send('c')
+
+ # Wait for Breakpoint 2
+ self.child.expect_exact("stop reason")
+
+ # Check console output for messages
+ self.child.expect_exact("Hello from stdout line 1")
+
+ # Tab to console
+ self.child.send(tab_key) # Sources -> Threads
+ self.child.send(tab_key) # Threads -> Variables
+ self.child.send(tab_key) # Variables -> Console
+
+ # Clear Console output
+ self.child.send('c')
+
+ # The Console window show this message after clear
+ self.child.expect_exact("(no output yet)")
+
+ # Press escape to quit the gui
+ self.child.send(escape_key)
+
+ self.expect_prompt()
+ self.quit()
+
+ @skipIfAsan
+ @skipIfCursesSupportMissing
+ @skipIf(oslist=["linux"], archs=["arm$", "aarch64"])
+ def test_gui_console_interaction(self):
+ """Test that console pane doesn't interfere with other window layouts"""
+ self.build()
+
+ self.launch(executable=self.getBuildArtifact("a.out"), dimensions=(100, 500))
+ self.expect(
+ 'br set -o true -f main.cpp -p "// break here begin"',
+ substrs=["Breakpoint 1", "address ="],
+ )
+ self.expect("run", substrs=["stop reason ="])
+
+ escape_key = chr(27).encode()
+
+ # Start the GUI.
+ self.child.sendline("gui")
+ self.child.expect_exact("Sources") # wait for gui
+
+ # Check Console window exists by default
+ self.child.expect_exact("Console")
+
+ # Check other windows exists
+ self.child.expect_exact("Threads")
+ self.child.expect_exact("Variables")
+
+ # Check test_var variable is listed in Variables window
+ self.child.expect_exact("test_var")
+
+ # Check source code in shown Sources window
+ self.child.expect_exact("main.cpp")
+
+ # Check main thread is shown in Threads window
+ self.child.expect_exact("thread #1")
+
+ # Press escape to quit the gui
+ self.child.send(escape_key)
+
+ self.expect_prompt()
+ self.quit()
diff --git a/lldb/test/API/commands/gui/console-output/main.cpp b/lldb/test/API/commands/gui/console-output/main.cpp
new file mode 100644
index 0000000000000..70aff9753ef43
--- /dev/null
+++ b/lldb/test/API/commands/gui/console-output/main.cpp
@@ -0,0 +1,25 @@
+#include <iostream>
+#include <thread>
+#include <chrono>
+
+void generate_output() {
+ for (unsigned i = 1; i < 4; ++i) {
+ std::cout << "Hello from stdout line " << i << std::endl;
+ std::cerr << "Hello from stderr line " << i << std::endl;
+ }
+}
+
+int main (int argc, char *argv[]) {
+ int test_var = 42;
+
+ // Break before output
+ int break_here = 0; // break here begin
+
+ // Generate stdout/stderr output
+ generate_output();
+
+ // Wait to capture output
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ return 0; // break here end
+}
>From 322b54f4842e776dfec21dade4ac90a8fadd22ce Mon Sep 17 00:00:00 2001
From: Nagesh Nazare <nageshnnazare at gmail.com>
Date: Thu, 22 Jan 2026 18:53:09 +0530
Subject: [PATCH 3/6] Syntax + test
Modified syntax as per llvm guidelines. Tested the added testcase and
fixed accordingly
---
lldb/source/Core/IOHandlerCursesGUI.cpp | 37 ++---
.../console-output/TestGuiConsoleOutput.py | 136 +++++-------------
2 files changed, 47 insertions(+), 126 deletions(-)
diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp
index 437584f2361c2..7b46178d6eaed 100644
--- a/lldb/source/Core/IOHandlerCursesGUI.cpp
+++ b/lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -6315,7 +6315,8 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
m_debugger.GetCommandInterpreter().GetExecutionContext();
Process *process = exe_ctx.GetProcessPtr();
- if (!process || !process->IsAlive()) return;
+ if (!process || !process->IsAlive())
+ return;
// Buffer for reading output
char buffer[1024];
@@ -6337,7 +6338,8 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
}
void AppendOutput(const char *text, bool is_stderr) {
- if (!text || text[0] == '\0') return;
+ if (!text || text[0] == '\0')
+ return;
std::lock_guard<std::mutex> lock(m_output_mutex);
@@ -6348,17 +6350,15 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
size_t start = 0, pos = 0;
while ((pos = remaining.find('\n', start)) != std::string::npos) {
std::string line = remaining.substr(start, pos - start);
- if (is_stderr) {
+ if (is_stderr)
line = "[stderr] " + line;
- }
m_output_lines.push_back(line);
// Keep buffer size under limit
while (m_output_lines.size() > m_max_lines) {
m_output_lines.pop_front();
- if (m_first_visible_line > 0) {
+ if (m_first_visible_line > 0)
--m_first_visible_line;
- }
}
start = pos + 1;
@@ -6425,21 +6425,18 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
// Highlight stderr lines?
bool is_stderr = (line.find("[stderr]") == 0);
- if (is_stderr) {
+ if (is_stderr)
window.AttributeOn(COLOR_PAIR(2));
- }
// Truncate line to fit window width
int available_width = width - 3;
- if (static_cast<int>(line.length()) > available_width) {
+ if (static_cast<int>(line.length()) > available_width)
window.PutCString(line.substr(0, available_width).c_str());
- } else {
+ else
window.PutCString(line.c_str());
- }
- if (is_stderr) {
+ if (is_stderr)
window.AttributeOff(COLOR_PAIR(2));
- }
}
return true;
@@ -6460,21 +6457,18 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
return eKeyHandled;
case KEY_DOWN:
- if (m_first_visible_line + visible_height < total_lines) {
+ if (m_first_visible_line + visible_height < total_lines)
++m_first_visible_line;
- }
// Re-enable Auto-scroll at bottom
- if (m_first_visible_line + visible_height >= total_lines) {
+ if (m_first_visible_line + visible_height >= total_lines)
m_auto_scroll = true;
- }
return eKeyHandled;
case KEY_PPAGE:
- if (m_first_visible_line > static_cast<size_t>(visible_height)) {
+ if (m_first_visible_line > static_cast<size_t>(visible_height))
m_first_visible_line -= visible_height;
- } else {
+ else
m_first_visible_line = 0;
- }
m_auto_scroll = false;
return eKeyHandled;
@@ -6489,10 +6483,9 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
case 'a':
m_auto_scroll = !m_auto_scroll;
- if (m_auto_scroll && total_lines > 0) {
+ if (m_auto_scroll && total_lines > 0)
m_first_visible_line = total_lines > static_cast<size_t>(visible_height)
? total_lines - visible_height : 0;
- }
return eKeyHandled;
case 'c':
diff --git a/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py b/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
index d4445d825cca5..1e4fedf313351 100644
--- a/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
+++ b/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
@@ -18,29 +18,37 @@ def test_gui_console_output(self):
"""Test that console pane prints messages"""
self.build()
- self.launch(executable=self.getBuildArtifact("a.out"), dimensions=(100, 500))
+ self.launch(
+ executable=self.getBuildArtifact("a.out"),
+ dimensions=(100, 500),
+ run_under=["env", "TERM=xterm"]
+ )
+
self.expect(
'br set -o true -f main.cpp -p "// break here begin"',
substrs=["Breakpoint 1", "address ="],
)
+
self.expect(
'br set -o true -f main.cpp -p "// break here end"',
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("Sources") # wait for gui
-
- # Check for other gui elements in Menu bar
+
+ # Check for gui elements in Menu bar (top of screen)
+ # We expect these in the order they appear to avoid consumption issues
self.child.expect_exact("Target")
self.child.expect_exact("Process")
self.child.expect_exact("View")
- # Check Console window exists
+ # Check for window titles (middle of screen)
+ self.child.expect_exact("Sources")
self.child.expect_exact("Console")
# The Console window show this message before continuing
@@ -49,9 +57,6 @@ def test_gui_console_output(self):
# Continue program execution
self.child.send('c')
- # Wait for Breakpoint 2
- self.child.expect_exact("stop reason")
-
# Check console output for messages
self.child.expect_exact("Hello from stdout line 1")
self.child.expect_exact("Hello from stderr line 1")
@@ -60,51 +65,8 @@ def test_gui_console_output(self):
self.child.expect_exact("Hello from stdout line 3")
self.child.expect_exact("Hello from stderr line 3")
- # Press escape to quit the gui
- self.child.send(escape_key)
-
- self.expect_prompt()
- self.quit()
-
- @skipIfAsan
- @skipIfCursesSupportMissing
- @skipIf(oslist=["linux"], archs=["arm$", "aarch64"])
- def test_gui_console_menu_toggle(self):
- """Test that console pane can be toggled via View Menu"""
- self.build()
-
- self.launch(executable=self.getBuildArtifact("a.out"), dimensions=(100, 500))
- self.expect(
- 'br set -o true -f main.cpp -p "// break here begin"',
- substrs=["Breakpoint 1", "address ="],
- )
- self.expect("run", substrs=["stop reason ="])
-
- escape_key = chr(27).encode()
-
- # Start the GUI.
- self.child.sendline("gui")
- self.child.expect_exact("Sources") # wait for gui
-
- # Check Console window exists by default
- self.child.expect_exact("Console")
-
- # Open View Menu and toggle Console window off
- self.child.send('v')
- self.child.expect_exact("Console") # menu item should exist
- self.child.send('o')
-
- # Wait for gui update
- import time
- time.sleep(0.5)
-
- # Open View Menu and toggle Console window on
- self.child.send('v')
- self.child.expect_exact("Console") # menu item should exist
- self.child.send('o')
-
- # Console window show re-appear
- self.child.expect_exact("Console")
+ # Wait for Breakpoint 2
+ self.child.expect_exact("stop reason")
# Press escape to quit the gui
self.child.send(escape_key)
@@ -119,15 +81,22 @@ def test_gui_console_navigate(self):
"""Test that console pane navigation works"""
self.build()
- self.launch(executable=self.getBuildArtifact("a.out"), dimensions=(100, 500))
+ self.launch(
+ executable=self.getBuildArtifact("a.out"),
+ dimensions=(100, 500),
+ run_under=["env", "TERM=xterm"]
+ )
+
self.expect(
- 'br set -o true -f main.cpp -p "// break here first"',
+ 'br set -o true -f main.cpp -p "// break here begin"',
substrs=["Breakpoint 1", "address ="],
)
+
self.expect(
'br set -o true -f main.cpp -p "// break here end"',
substrs=["Breakpoint 2", "address ="],
)
+
self.expect("run", substrs=["stop reason ="])
escape_key = chr(27).encode()
@@ -135,9 +104,10 @@ def test_gui_console_navigate(self):
# Start the GUI.
self.child.sendline("gui")
- self.child.expect_exact("Sources") # wait for gui
-
- # Check Console window exists by default
+
+ # Match elements in top-to-bottom order
+ self.child.expect_exact("Target")
+ self.child.expect_exact("Sources")
self.child.expect_exact("Console")
# The Console window show this message before continuing
@@ -146,12 +116,12 @@ def test_gui_console_navigate(self):
# Continue program execution
self.child.send('c')
- # Wait for Breakpoint 2
- self.child.expect_exact("stop reason")
-
# Check console output for messages
self.child.expect_exact("Hello from stdout line 1")
+ # Wait for Breakpoint 2
+ self.child.expect_exact("stop reason")
+
# Tab to console
self.child.send(tab_key) # Sources -> Threads
self.child.send(tab_key) # Threads -> Variables
@@ -167,46 +137,4 @@ def test_gui_console_navigate(self):
self.child.send(escape_key)
self.expect_prompt()
- self.quit()
-
- @skipIfAsan
- @skipIfCursesSupportMissing
- @skipIf(oslist=["linux"], archs=["arm$", "aarch64"])
- def test_gui_console_interaction(self):
- """Test that console pane doesn't interfere with other window layouts"""
- self.build()
-
- self.launch(executable=self.getBuildArtifact("a.out"), dimensions=(100, 500))
- self.expect(
- 'br set -o true -f main.cpp -p "// break here begin"',
- substrs=["Breakpoint 1", "address ="],
- )
- self.expect("run", substrs=["stop reason ="])
-
- escape_key = chr(27).encode()
-
- # Start the GUI.
- self.child.sendline("gui")
- self.child.expect_exact("Sources") # wait for gui
-
- # Check Console window exists by default
- self.child.expect_exact("Console")
-
- # Check other windows exists
- self.child.expect_exact("Threads")
- self.child.expect_exact("Variables")
-
- # Check test_var variable is listed in Variables window
- self.child.expect_exact("test_var")
-
- # Check source code in shown Sources window
- self.child.expect_exact("main.cpp")
-
- # Check main thread is shown in Threads window
- self.child.expect_exact("thread #1")
-
- # Press escape to quit the gui
- self.child.send(escape_key)
-
- self.expect_prompt()
- self.quit()
+ self.quit()
\ No newline at end of file
>From 408f453ef91941a6ec480ce531eedd74e125719d Mon Sep 17 00:00:00 2001
From: Nagesh Nazare <nageshnnazare at gmail.com>
Date: Mon, 26 Jan 2026 18:29:50 +0530
Subject: [PATCH 4/6] fixed formatting issues
---
lldb/source/Core/IOHandlerCursesGUI.cpp | 78 +++++++++----------
.../console-output/TestGuiConsoleOutput.py | 41 +++++-----
.../API/commands/gui/console-output/main.cpp | 4 +-
3 files changed, 61 insertions(+), 62 deletions(-)
diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp
index 7b46178d6eaed..6c77bb53d43bd 100644
--- a/lldb/source/Core/IOHandlerCursesGUI.cpp
+++ b/lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -6312,7 +6312,7 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
private:
void PollProcessOutput() {
ExecutionContext exe_ctx =
- m_debugger.GetCommandInterpreter().GetExecutionContext();
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
Process *process = exe_ctx.GetProcessPtr();
if (!process || !process->IsAlive())
@@ -6370,14 +6370,14 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
// Auto-scroll to bottom if enabled
if (m_auto_scroll && !m_output_lines.empty()) {
m_first_visible_line =
- m_output_lines.size() > 0 ? m_output_lines.size() - 1 : 0;
+ m_output_lines.size() > 0 ? m_output_lines.size() - 1 : 0;
}
}
public:
ConsoleOutputWindowDelegate(Debugger &debugger)
- : m_debugger(debugger), m_first_visible_line(0),
- m_auto_scroll(true), m_max_lines(10000) {}
+ : m_debugger(debugger), m_first_visible_line(0), m_auto_scroll(true),
+ m_max_lines(10000) {}
~ConsoleOutputWindowDelegate() override = default;
@@ -6390,7 +6390,7 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
window.Erase();
window.DrawTitleBox(window.GetName());
- const int width = window.GetWidth();
+ const int width = window.GetWidth();
const int height = window.GetHeight();
// Calculate the visible range
@@ -6403,8 +6403,7 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
// Adjust scroll pos if needed
if (m_first_visible_line >= total_lines) {
- m_first_visible_line =
- total_lines > 0 ? total_lines - 1 : 0;
+ m_first_visible_line = total_lines > 0 ? total_lines - 1 : 0;
}
// Draw visible line
@@ -6413,13 +6412,13 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
// If we are at the end, display last N lines
if (m_auto_scroll || start_line + visible_height > total_lines) {
- start_line =
- total_lines > static_cast<size_t>(visible_height) ?
- total_lines - visible_height : 0;
+ start_line = total_lines > static_cast<size_t>(visible_height)
+ ? total_lines - visible_height
+ : 0;
}
- for (int row = 1; row <= visible_height &&
- (start_line + row - 1) < total_lines; ++row) {
+ for (int row = 1;
+ row <= visible_height && (start_line + row - 1) < total_lines; ++row) {
window.MoveCursor(2, row);
const std::string &line = m_output_lines[start_line + row - 1];
@@ -6476,7 +6475,8 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
m_first_visible_line += visible_height;
if (m_first_visible_line + visible_height >= total_lines) {
m_first_visible_line = total_lines > static_cast<size_t>(visible_height)
- ? total_lines - visible_height : 0;
+ ? total_lines - visible_height
+ : 0;
m_auto_scroll = true;
}
return eKeyHandled;
@@ -6485,7 +6485,8 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
m_auto_scroll = !m_auto_scroll;
if (m_auto_scroll && total_lines > 0)
m_first_visible_line = total_lines > static_cast<size_t>(visible_height)
- ? total_lines - visible_height : 0;
+ ? total_lines - visible_height
+ : 0;
return eKeyHandled;
case 'c':
@@ -6501,7 +6502,8 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
case KEY_END:
m_first_visible_line = total_lines > static_cast<size_t>(visible_height)
- ? total_lines - visible_height : 0;
+ ? total_lines - visible_height
+ : 0;
m_auto_scroll = true;
return eKeyHandled;
@@ -6518,16 +6520,11 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
KeyHelp *WindowDelegateGetKeyHelp() override {
static curses::KeyHelp g_source_view_key_help[] = {
- {KEY_UP, "Scroll up"},
- {KEY_DOWN, "Scroll down"},
- {KEY_PPAGE, "Page up"},
- {KEY_NPAGE, "Page down"},
- {KEY_HOME, "Go to top"},
- {KEY_END, "Go to bottom"},
- {'h', "Show help dialog"},
- {'a', "Toggle auto-scroll"},
- {'c', "Clear output"},
- {'\0', nullptr}};
+ {KEY_UP, "Scroll up"}, {KEY_DOWN, "Scroll down"},
+ {KEY_PPAGE, "Page up"}, {KEY_NPAGE, "Page down"},
+ {KEY_HOME, "Go to top"}, {KEY_END, "Go to bottom"},
+ {'h', "Show help dialog"}, {'a', "Toggle auto-scroll"},
+ {'c', "Clear output"}, {'\0', nullptr}};
return g_source_view_key_help;
}
@@ -6826,7 +6823,8 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
if (console_window_sp) {
Rect console_bounds = console_window_sp->GetBounds();
console_bounds.origin.x = variables_bounds.origin.x;
- console_bounds.size.width = variables_bounds.size.width + console_bounds.size.width;
+ console_bounds.size.width =
+ variables_bounds.size.width + console_bounds.size.width;
console_window_sp->SetBounds(console_bounds);
} else if (registers_window_sp) {
// We have a registers window, so give all the area back to the
@@ -6835,8 +6833,8 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
registers_bounds.size.width = source_bounds.size.width;
registers_window_sp->SetBounds(registers_bounds);
} else {
- // We have no console or registers window showing so give the bottom area back
- // to the source view
+ // We have no console or registers window showing so give the bottom
+ // area back to the source view
source_window_sp->Resize(source_bounds.size.width,
source_bounds.size.height +
variables_bounds.size.height);
@@ -6859,14 +6857,15 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
new_regs_rect);
registers_window_sp->SetBounds(new_regs_rect);
} else {
- // No registers or console window, grab the bottom part of the source window
+ // No registers or console window, grab the bottom part of the source
+ // window
Rect new_source_rect;
source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
new_vars_rect);
source_window_sp->SetBounds(new_source_rect);
}
- WindowSP new_window_sp = main_window_sp->CreateSubWindow(
- "Variables", new_vars_rect, false);
+ WindowSP new_window_sp =
+ main_window_sp->CreateSubWindow("Variables", new_vars_rect, false);
new_window_sp->SetDelegate(
WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
}
@@ -6940,8 +6939,8 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
if (variables_window_sp) {
// Variables window exists, so give Console space to Variables
Rect variables_bounds = variables_window_sp->GetBounds();
- variables_bounds.size.width = variables_bounds.size.width +
- console_bounds.size.width;
+ variables_bounds.size.width =
+ variables_bounds.size.width + console_bounds.size.width;
variables_window_sp->SetBounds(variables_bounds);
} else if (registers_window_sp) {
// Registers window exists, so give Console space to Registers
@@ -6952,7 +6951,7 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
// No Variables or Registers window exists
source_window_sp->Resize(source_bounds.size.width,
source_bounds.size.height +
- console_bounds.size.height);
+ console_bounds.size.height);
}
} else {
Rect new_console_rect;
@@ -6978,9 +6977,9 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
source_window_sp->SetBounds(new_source_rect);
}
WindowSP new_window_sp =
- main_window_sp->CreateSubWindow("Console", new_console_rect, false);
+ main_window_sp->CreateSubWindow("Console", new_console_rect, false);
new_window_sp->SetDelegate(
- WindowDelegateSP(new ConsoleOutputWindowDelegate(m_debugger)));
+ WindowDelegateSP(new ConsoleOutputWindowDelegate(m_debugger)));
}
touchwin(stdscr);
}
@@ -7937,8 +7936,9 @@ void IOHandlerCursesGUI::Activate() {
"Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource));
view_menu_sp->AddSubmenu(std::make_shared<Menu>(
"Variables", nullptr, 'v', ApplicationDelegate::eMenuID_ViewVariables));
- view_menu_sp->AddSubmenu(std::make_shared<Menu>(
- "Breakpoints", nullptr, 'b', ApplicationDelegate::eMenuID_ViewBreakpoints));
+ view_menu_sp->AddSubmenu(
+ std::make_shared<Menu>("Breakpoints", nullptr, 'b',
+ ApplicationDelegate::eMenuID_ViewBreakpoints));
view_menu_sp->AddSubmenu(std::make_shared<Menu>(
"Console", nullptr, 'o', ApplicationDelegate::eMenuId_ViewConsole));
@@ -7973,7 +7973,7 @@ void IOHandlerCursesGUI::Activate() {
source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
variables_console_bounds);
variables_console_bounds.VerticalSplitPercentage(0.50, variables_bounds,
- console_bounds);
+ console_bounds);
WindowSP menubar_window_sp =
main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
diff --git a/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py b/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
index 1e4fedf313351..307d266012f35 100644
--- a/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
+++ b/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
@@ -13,34 +13,33 @@ class TestGuiConsoleOutputTest(PExpectTest):
# under ASAN on a loaded machine..
@skipIfAsan
@skipIfCursesSupportMissing
- @skipIf(oslist=["linux"], archs=["arm$", "aarch64"])
def test_gui_console_output(self):
"""Test that console pane prints messages"""
self.build()
self.launch(
- executable=self.getBuildArtifact("a.out"),
+ executable=self.getBuildArtifact("a.out"),
dimensions=(100, 500),
- run_under=["env", "TERM=xterm"]
+ run_under=["env", "TERM=xterm"],
)
-
+
self.expect(
'br set -o true -f main.cpp -p "// break here begin"',
substrs=["Breakpoint 1", "address ="],
)
-
+
self.expect(
'br set -o true -f main.cpp -p "// break here end"',
substrs=["Breakpoint 2", "address ="],
)
-
+
self.expect("run", substrs=["stop reason ="])
escape_key = chr(27).encode()
# Start the GUI.
self.child.sendline("gui")
-
+
# Check for gui elements in Menu bar (top of screen)
# We expect these in the order they appear to avoid consumption issues
self.child.expect_exact("Target")
@@ -76,35 +75,34 @@ def test_gui_console_output(self):
@skipIfAsan
@skipIfCursesSupportMissing
- @skipIf(oslist=["linux"], archs=["arm$", "aarch64"])
def test_gui_console_navigate(self):
"""Test that console pane navigation works"""
self.build()
self.launch(
- executable=self.getBuildArtifact("a.out"),
+ executable=self.getBuildArtifact("a.out"),
dimensions=(100, 500),
- run_under=["env", "TERM=xterm"]
+ run_under=["env", "TERM=xterm"],
)
-
+
self.expect(
'br set -o true -f main.cpp -p "// break here begin"',
substrs=["Breakpoint 1", "address ="],
)
-
+
self.expect(
'br set -o true -f main.cpp -p "// break here end"',
substrs=["Breakpoint 2", "address ="],
)
-
+
self.expect("run", substrs=["stop reason ="])
escape_key = chr(27).encode()
- tab_key = chr(9).encode()
+ tab_key = chr(9).encode()
# Start the GUI.
self.child.sendline("gui")
-
+
# Match elements in top-to-bottom order
self.child.expect_exact("Target")
self.child.expect_exact("Sources")
@@ -114,7 +112,7 @@ def test_gui_console_navigate(self):
self.child.expect_exact("(no output yet)")
# Continue program execution
- self.child.send('c')
+ self.child.send("c")
# Check console output for messages
self.child.expect_exact("Hello from stdout line 1")
@@ -123,12 +121,12 @@ def test_gui_console_navigate(self):
self.child.expect_exact("stop reason")
# Tab to console
- self.child.send(tab_key) # Sources -> Threads
- self.child.send(tab_key) # Threads -> Variables
- self.child.send(tab_key) # Variables -> Console
+ self.child.send(tab_key) # Sources -> Threads
+ self.child.send(tab_key) # Threads -> Variables
+ self.child.send(tab_key) # Variables -> Console
# Clear Console output
- self.child.send('c')
+ self.child.send("c")
# The Console window show this message after clear
self.child.expect_exact("(no output yet)")
@@ -137,4 +135,5 @@ def test_gui_console_navigate(self):
self.child.send(escape_key)
self.expect_prompt()
- self.quit()
\ No newline at end of file
+ self.quit()
+
diff --git a/lldb/test/API/commands/gui/console-output/main.cpp b/lldb/test/API/commands/gui/console-output/main.cpp
index 70aff9753ef43..0baa6058b438e 100644
--- a/lldb/test/API/commands/gui/console-output/main.cpp
+++ b/lldb/test/API/commands/gui/console-output/main.cpp
@@ -1,6 +1,6 @@
+#include <chrono>
#include <iostream>
#include <thread>
-#include <chrono>
void generate_output() {
for (unsigned i = 1; i < 4; ++i) {
@@ -9,7 +9,7 @@ void generate_output() {
}
}
-int main (int argc, char *argv[]) {
+int main(int argc, char *argv[]) {
int test_var = 42;
// Break before output
>From 31cf83c7a160f9ef5e2eaf8ef96e184ff5825f24 Mon Sep 17 00:00:00 2001
From: Nagesh Nazare <nageshnnazare at gmail.com>
Date: Mon, 26 Jan 2026 18:38:18 +0530
Subject: [PATCH 5/6] Added fixes for comments
---
lldb/source/Core/IOHandlerCursesGUI.cpp | 60 +++++++++----------
.../API/commands/gui/console-output/main.cpp | 6 +-
2 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp
index 6c77bb53d43bd..6118a1dc388ac 100644
--- a/lldb/source/Core/IOHandlerCursesGUI.cpp
+++ b/lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -6318,18 +6318,18 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
if (!process || !process->IsAlive())
return;
- // Buffer for reading output
+ // Buffer for reading output.
char buffer[1024];
Status error;
- // Read stdout
+ // Read process's stdout.
size_t stdout_bytes = process->GetSTDOUT(buffer, sizeof(buffer) - 1, error);
if (stdout_bytes > 0) {
buffer[stdout_bytes] = '\0';
AppendOutput(buffer, false);
}
- // Read stderr
+ // Read process's stderr.
size_t stderr_bytes = process->GetSTDERR(buffer, sizeof(buffer) - 1, error);
if (stderr_bytes > 0) {
buffer[stderr_bytes] = '\0';
@@ -6343,7 +6343,7 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
std::lock_guard<std::mutex> lock(m_output_mutex);
- // Split text into lines and add to buffer
+ // Split text into lines and add to buffer.
std::string remaining = m_partial_line;
remaining += text;
@@ -6354,7 +6354,7 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
line = "[stderr] " + line;
m_output_lines.push_back(line);
- // Keep buffer size under limit
+ // Keep buffer size under limit.
while (m_output_lines.size() > m_max_lines) {
m_output_lines.pop_front();
if (m_first_visible_line > 0)
@@ -6364,10 +6364,10 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
start = pos + 1;
}
- // Save any remaining partial line
+ // Save any remaining partial line.
m_partial_line = remaining.substr(start);
- // Auto-scroll to bottom if enabled
+ // Auto-scroll to bottom if enabled.
if (m_auto_scroll && !m_output_lines.empty()) {
m_first_visible_line =
m_output_lines.size() > 0 ? m_output_lines.size() - 1 : 0;
@@ -6382,7 +6382,7 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
~ConsoleOutputWindowDelegate() override = default;
bool WindowDelegateDraw(Window &window, bool force) override {
- // Poll for new output
+ // Poll for new output.
PollProcessOutput();
std::lock_guard<std::mutex> lock(m_output_mutex);
@@ -6393,7 +6393,7 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
const int width = window.GetWidth();
const int height = window.GetHeight();
- // Calculate the visible range
+ // Calculate the visible range.
size_t total_lines = m_output_lines.size();
if (total_lines == 0) {
window.MoveCursor(2, 1);
@@ -6401,16 +6401,16 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
return true;
}
- // Adjust scroll pos if needed
+ // Adjust scroll pos if needed.
if (m_first_visible_line >= total_lines) {
m_first_visible_line = total_lines > 0 ? total_lines - 1 : 0;
}
- // Draw visible line
+ // Draw visible line.
int visible_height = height - 2;
size_t start_line = m_first_visible_line;
- // If we are at the end, display last N lines
+ // If we are at the end, display last N lines.
if (m_auto_scroll || start_line + visible_height > total_lines) {
start_line = total_lines > static_cast<size_t>(visible_height)
? total_lines - visible_height
@@ -6422,12 +6422,12 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
window.MoveCursor(2, row);
const std::string &line = m_output_lines[start_line + row - 1];
- // Highlight stderr lines?
+ // Highlight stderr lines?.
bool is_stderr = (line.find("[stderr]") == 0);
if (is_stderr)
window.AttributeOn(COLOR_PAIR(2));
- // Truncate line to fit window width
+ // Truncate line to fit window width.
int available_width = width - 3;
if (static_cast<int>(line.length()) > available_width)
window.PutCString(line.substr(0, available_width).c_str());
@@ -6458,7 +6458,7 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
case KEY_DOWN:
if (m_first_visible_line + visible_height < total_lines)
++m_first_visible_line;
- // Re-enable Auto-scroll at bottom
+ // Re-enable Auto-scroll at bottom.
if (m_first_visible_line + visible_height >= total_lines)
m_auto_scroll = true;
return eKeyHandled;
@@ -6828,13 +6828,13 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
console_window_sp->SetBounds(console_bounds);
} else if (registers_window_sp) {
// We have a registers window, so give all the area back to the
- // registers window
+ // registers window.
Rect registers_bounds = variables_bounds;
registers_bounds.size.width = source_bounds.size.width;
registers_window_sp->SetBounds(registers_bounds);
} else {
// We have no console or registers window showing so give the bottom
- // area back to the source view
+ // area back to the source view.
source_window_sp->Resize(source_bounds.size.width,
source_bounds.size.height +
variables_bounds.size.height);
@@ -6842,7 +6842,7 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
} else {
Rect new_vars_rect;
if (console_window_sp) {
- // Console exists, so split the area
+ // Console exists, so split the area.
const Rect console_bounds = console_window_sp->GetBounds();
Rect new_console_rect;
console_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
@@ -6850,7 +6850,7 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
} else if (registers_window_sp) {
// We have a registers window so split the area of the registers
// window into two columns where the left hand side will be the
- // variables and the right hand side will be the registers
+ // variables and the right hand side will be the registers.
const Rect registers_bounds = registers_window_sp->GetBounds();
Rect new_regs_rect;
registers_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
@@ -6858,7 +6858,7 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
registers_window_sp->SetBounds(new_regs_rect);
} else {
// No registers or console window, grab the bottom part of the source
- // window
+ // window.
Rect new_source_rect;
source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
new_vars_rect);
@@ -6885,13 +6885,13 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
const Rect variables_bounds = variables_window_sp->GetBounds();
// We have a variables window, so give all the area back to the
- // variables window
+ // variables window.
variables_window_sp->Resize(variables_bounds.size.width +
registers_window_sp->GetWidth(),
variables_bounds.size.height);
} else {
// We have no variables window showing so give the bottom area back
- // to the source view
+ // to the source view.
source_window_sp->Resize(source_bounds.size.width,
source_bounds.size.height +
registers_window_sp->GetHeight());
@@ -6902,14 +6902,14 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
if (variables_window_sp) {
// We have a variables window, split it into two columns where the
// left hand side will be the variables and the right hand side will
- // be the registers
+ // be the registers.
const Rect variables_bounds = variables_window_sp->GetBounds();
Rect new_vars_rect;
variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
new_regs_rect);
variables_window_sp->SetBounds(new_vars_rect);
} else {
- // No variables window, grab the bottom part of the source window
+ // No variables window, grab the bottom part of the source window.
Rect new_source_rect;
source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
new_regs_rect);
@@ -6937,18 +6937,18 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
main_window_sp->RemoveSubWindow(console_window_sp.get());
if (variables_window_sp) {
- // Variables window exists, so give Console space to Variables
+ // Variables window exists, so give Console space to Variables.
Rect variables_bounds = variables_window_sp->GetBounds();
variables_bounds.size.width =
variables_bounds.size.width + console_bounds.size.width;
variables_window_sp->SetBounds(variables_bounds);
} else if (registers_window_sp) {
- // Registers window exists, so give Console space to Registers
+ // Registers window exists, so give Console space to Registers.
Rect registers_bounds = registers_window_sp->GetBounds();
registers_bounds.size.width = source_bounds.size.width;
registers_window_sp->SetBounds(registers_bounds);
} else {
- // No Variables or Registers window exists
+ // No Variables or Registers window exists.
source_window_sp->Resize(source_bounds.size.width,
source_bounds.size.height +
console_bounds.size.height);
@@ -6956,21 +6956,21 @@ class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
} else {
Rect new_console_rect;
if (variables_window_sp) {
- // Variable window exists, split area
+ // Variable window exists, split area.
const Rect variables_bounds = variables_window_sp->GetBounds();
Rect new_vars_rect;
variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
new_console_rect);
variables_window_sp->SetBounds(new_vars_rect);
} else if (registers_window_sp) {
- // Registers window exists, split area
+ // Registers window exists, split area.
const Rect registers_bounds = registers_window_sp->GetBounds();
Rect new_regs_rect;
registers_bounds.VerticalSplitPercentage(0.50, new_console_rect,
new_regs_rect);
registers_window_sp->SetBounds(new_regs_rect);
} else {
- // No Registers or Variables window exists, split source area
+ // No Registers or Variables window exists, split source area.
Rect new_source_rect;
source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
new_console_rect);
diff --git a/lldb/test/API/commands/gui/console-output/main.cpp b/lldb/test/API/commands/gui/console-output/main.cpp
index 0baa6058b438e..ca6cf95cd0dfd 100644
--- a/lldb/test/API/commands/gui/console-output/main.cpp
+++ b/lldb/test/API/commands/gui/console-output/main.cpp
@@ -12,13 +12,13 @@ void generate_output() {
int main(int argc, char *argv[]) {
int test_var = 42;
- // Break before output
+ // Break before output.
int break_here = 0; // break here begin
- // Generate stdout/stderr output
+ // Generate stdout/stderr output.
generate_output();
- // Wait to capture output
+ // Wait to capture output.
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return 0; // break here end
>From 52dba28b1c33ed0d660640a9a77e70ad7864c123 Mon Sep 17 00:00:00 2001
From: Nagesh Nazare <nageshnnazare at gmail.com>
Date: Thu, 29 Jan 2026 20:00:39 +0530
Subject: [PATCH 6/6] More formatting fixes..
---
lldb/source/Core/IOHandlerCursesGUI.cpp | 1 -
.../API/commands/gui/console-output/TestGuiConsoleOutput.py | 3 +--
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp
index 6118a1dc388ac..e9ec16aae853b 100644
--- a/lldb/source/Core/IOHandlerCursesGUI.cpp
+++ b/lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -6538,7 +6538,6 @@ class ConsoleOutputWindowDelegate : public WindowDelegate {
std::mutex m_output_mutex;
};
-
class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
public:
enum {
diff --git a/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py b/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
index 307d266012f35..4c2e8075aabd6 100644
--- a/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
+++ b/lldb/test/API/commands/gui/console-output/TestGuiConsoleOutput.py
@@ -54,7 +54,7 @@ def test_gui_console_output(self):
self.child.expect_exact("(no output yet)")
# Continue program execution
- self.child.send('c')
+ self.child.send("c")
# Check console output for messages
self.child.expect_exact("Hello from stdout line 1")
@@ -136,4 +136,3 @@ def test_gui_console_navigate(self):
self.expect_prompt()
self.quit()
-
More information about the lldb-commits
mailing list