https://github.com/nageshnnazare updated 
https://github.com/llvm/llvm-project/pull/197760

>From 5b756054e45a6efc6792e031cd3d1ab33e416ac2 Mon Sep 17 00:00:00 2001
From: Nagesh Nazare <[email protected]>
Date: Wed, 21 Jan 2026 01:30:23 +0530
Subject: [PATCH 1/2] 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 310922f1513d6a03d332a532eadb19b21a9fb2ac Mon Sep 17 00:00:00 2001
From: Nagesh Nazare <[email protected]>
Date: Thu, 14 May 2026 22:37:17 +0530
Subject: [PATCH 2/2] [lldb] Improved highlighting logic for lldb gui

Modified OutputColoredStringTruncated to use BlackOnCyan when rendering
the PC line and disabled syntax highlighting for that line. This ensures
the text remains black and legible against the highlight background.

Updated both the source code view and the disassembly view to use the
BlackOnCyan styling for the PC line and thread stop descriptions.
---
 lldb/source/Core/IOHandlerCursesGUI.cpp | 29 +++++++++++++++----------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp 
b/lldb/source/Core/IOHandlerCursesGUI.cpp
index 437584f2361c2..17305d3859b32 100644
--- a/lldb/source/Core/IOHandlerCursesGUI.cpp
+++ b/lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -309,7 +309,9 @@ enum {
   // Other colors, as needed.
   BlackOnWhite,
   MagentaOnWhite,
-  LastColorPairIndex = MagentaOnWhite
+  BlackOnCyan,
+  WhiteOnCyan,
+  LastColorPairIndex = WhiteOnCyan
 };
 
 class WindowDelegate {
@@ -491,13 +493,13 @@ class Surface {
   // visible characters were skipped due to first_skip_count.
   bool OutputColoredStringTruncated(int right_pad, StringRef string,
                                     size_t skip_first_count,
-                                    bool use_blue_background) {
+                                    bool use_highlight_background) {
     attr_t saved_attr;
     short saved_pair;
     bool result = false;
     wattr_get(m_window, &saved_attr, &saved_pair, nullptr);
-    if (use_blue_background)
-      ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
+    if (use_highlight_background)
+      ::wattron(m_window, COLOR_PAIR(BlackOnCyan));
     while (!string.empty()) {
       size_t esc_pos = string.find(ANSI_ESC_START);
       if (esc_pos == StringRef::npos) {
@@ -543,14 +545,15 @@ class Surface {
       }
       if (value == 0) { // Reset.
         wattr_set(m_window, saved_attr, saved_pair, nullptr);
-        if (use_blue_background)
-          ::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
+        if (use_highlight_background)
+          ::wattron(m_window, COLOR_PAIR(BlackOnCyan));
       } else if (value == ANSI_CTRL_UNDERLINE) {
         ::wattron(m_window, A_UNDERLINE);
       } else {
-        // Mapped directly to first 16 color pairs (black/blue background).
-        ::wattron(m_window, COLOR_PAIR(value - ANSI_FG_COLOR_BLACK + 1 +
-                                       (use_blue_background ? 8 : 0)));
+        if (!use_highlight_background) {
+          // Mapped directly to first 16 color pairs (black/blue background).
+          ::wattron(m_window, COLOR_PAIR(value - ANSI_FG_COLOR_BLACK + 1));
+        }
       }
     }
     wattr_set(m_window, saved_attr, saved_pair, nullptr);
@@ -7389,7 +7392,7 @@ class SourceFileWindowDelegate : public WindowDelegate {
                   window.Printf("%*s", desc_x - window.GetCursorX(), "");
                 window.MoveCursor(window_width - stop_description_len - 16,
                                   line_y);
-                const attr_t stop_reason_attr = COLOR_PAIR(WhiteOnBlue);
+                const attr_t stop_reason_attr = COLOR_PAIR(BlackOnCyan);
                 window.AttributeOn(stop_reason_attr);
                 window.PrintfTruncated(1, " <<< Thread %u: %s ",
                                        thread->GetIndexID(), stop_description);
@@ -7427,7 +7430,7 @@ class SourceFileWindowDelegate : public WindowDelegate {
         }
 
         const attr_t selected_highlight_attr = A_REVERSE;
-        const attr_t pc_highlight_attr = COLOR_PAIR(WhiteOnBlue);
+        const attr_t pc_highlight_attr = COLOR_PAIR(BlackOnCyan);
 
         StreamString strm;
 
@@ -8037,7 +8040,9 @@ void IOHandlerCursesGUI::Activate() {
     // These must match the order in the color indexes enum.
     init_pair(17, COLOR_BLACK, COLOR_WHITE);
     init_pair(18, COLOR_MAGENTA, COLOR_WHITE);
-    static_assert(LastColorPairIndex == 18, "Color indexes do not match.");
+    init_pair(19, COLOR_BLACK, COLOR_CYAN);
+    init_pair(20, COLOR_WHITE, COLOR_CYAN);
+    static_assert(LastColorPairIndex == 20, "Color indexes do not match.");
 
     define_key("\033[Z", KEY_SHIFT_TAB);
     define_key("\033\015", KEY_ALT_ENTER);

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to