Skip to main content
Redhat Developers  Logo
  • Products

    Platforms

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat AI
      Red Hat AI
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • View All Red Hat Products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat Developer Hub
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat OpenShift Local
    • Red Hat Developer Sandbox

      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Secure Development & Architectures

      • Security
      • Secure coding
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • Product Documentation
    • API Catalog
    • Legacy Documentation
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Add custom windows to GDB: Programming the TUI in Python

August 3, 2022
Andrew Burgess
Related topics:
C, C#, C++CompilersLinux
Related products:
Red Hat Enterprise Linux

Share:

    The GNU Debugger (GDB), a popular free and open source tool for C and C++ programmers, offers a Text User Interface (TUI) to split the console into multiple windows and display different content in each window. One window will always be a command window, in which you enter the usual GDB commands, but you might also have a source code window, a register contents window, or a disassembly window. Since GDB 11, you can use a Python API to add new window types. This API can be incredibly useful, allowing you to customize GDB to visualize your application's data in new ways.

    Note: The Python API for adding TUI windows was actually added to GDB 10. Unfortunately, prior to GDB 11, the gdb.TuiWindow.write call had some bugs that were not resolved until GDB 11.

    In this article, the first in a two-part series, you'll learn how to create a window and load it with dynamic content. The real power of the TUI will be shown in the second article, which shows how to display useful information from GDB.

    Why use the GDB Text User Interface?

    Imagine you are debugging a system containing a cache. You could create a custom window that shows you, at a glance, what is mapped into each cache entry. Or if your system generates an event or activity log, you could set up a window that shows the most recent entries from that log. All of these things could be done in a pure command-line environment, but by creating a custom TUI window, you can view the information on the screen all of the time, making it much easier to spot problems, and so fix bugs more quickly.

    For this two-part tutorial, you'll create a new window that just displays GDB's value history. However, once you've finished, you should have the skills needed to adapt this example to display any data you want, including unique views into your application.

    Your first window

    Start by creating the simplest working window possible. Create an empty file named history.py and then add this content:

    class history_window:
        def __init__(self, tui_window):
            self._tui_window = tui_window
            self._tui_window.title = 'Value History'
    
    gdb.register_window_type('history', history_window)

    The function gdb.register_window_type is how you alert GDB to your new window type. The first argument is a string giving the name of the new window (history, in the example). The second argument is a factory method that GDB will call when it needs to create an instance of the history window.

    The factory method is passed an object of type gdb.TuiWindow, and should return a brand new object of any type that represents your custom window.

    The example uses the history_window class constructor as the factory method. The tui_window argument is of type gdb.TuiWindow and is passed from GDB. The class stores tui_window into the new instance to be used later on for writing into the window. But for now, all the class does is use the object to set the title of the window to Value History.

    Now get GDB to display the new window. Start GDB, and enter this command to load and run your Python script, registering the new window type with GDB:

    (gdb) source history.py

    Use the tui new-layout command to create a new layout:

    (gdb) tui new-layout example_1 history 1 cmd 1 status 1

    A layout is just a collection of windows that are displayed together. GDB has several built-in layouts, but you just created a new one called example_1.

    The remaining arguments to tui new-layout define the windows you will add to this layout. The number after each window is the relative weight of that window in the layout. The weight is a guide to GDB for how much terminal space to allocate to each window. The weight is only a guide, though. Some windows, such as status, have a maximum size. Other windows, such as cmd and history, can be any size. GDB considers all of these constraints and sizes each window appropriately.

    Finally, tell GDB to activate TUI mode and use your new layout:

    (gdb) layout example_1

    If everything has worked, your GDB terminal should now look like Figure 1.

    GDB shows a new blank screen with the title Value History.
    Figure 1. GDB shows a new blank screen with the title Value History.
    Figure 1: GDB shows a new blank screen with the title Value History.

    That's not very exciting. Your history_window window, displayed at the top part of the terminal, shows the Value History title, but otherwise, the new window remains blank.

    Adding content to the window

    The next task is to generate some content in your window. Every time GDB needs to redraw the window contents, it calls the render method on the window object. We didn't implement this method initially. If the method doesn't exist, GDB doesn't try to call it, and just leaves the window blank.

    So now, add the following render method to your history_window class:

        def render(self):
            self._tui_window.erase()
            self._tui_window.write('Hello World\n')
            self._tui_window.write('Two\nLines\n')
            self._tui_window.write('abc'*2000000)

    Restart GDB and reload your history.py script. Rather than retyping the commands into your GDB session, you can pass the commands to GDB from the command line like this:

    gdb -ex 'source history.py' \
        -ex 'tui new-layout example_1 history 1 cmd 1 status 1' \
        -ex 'layout example_1'

    GDB should start and immediately switch to TUI mode. The terminal should look like Figure 2.

    The Value History screen contains predefined text inserted by your "render" method.
    Figure 2. The Value History screen contains predefined text inserted by your "render" method.
    Figure 2: The Value History screen contains predefined text inserted by your render method.

    Note how the very long line of repeated abc characters wraps at the right side of the window, but is cut off at the bottom of the window. The way GDB fits content into windows is important to keep in mind when you start laying out real content.

    Spotting new history items

    To display every value from GDB's history list, you need some way to spot when new values are added to the history list. GDB doesn't have a Python event that notifies you when values are added to the history list, but there is an event that notifies you when GDB is about to display a new prompt. So you need to catch this event, called before_prompt, then fetch any new values from the history list and add them to your window content.

    Start by adding code to catch the before_prompt event to your history_window class. Add the following two lines to the history_window class, in the __init__ method:

            self._before_prompt_listener = lambda : self._before_prompt()
            gdb.events.before_prompt.connect(self._before_prompt_listener)

    This code creates a new lambda function stored in _before_prompt_listener. This is the callback function for the event. The next line calls gdb.events.before_prompt.connect to register this callback with GDB. Now, every time GDB displays a prompt, it will first call this function.

    The callback function forwards the call to the _before_prompt method of your history_window class. You haven't written that yet, but you will shortly. Before you do, though, let's go back and rework our render method to make it a little more useful. First, add the following line to the __init__ method:

            self._lines = []

    This list will contain all of the lines for our window.

    Now replace the existing render method with a new one, which displays the content out of your newly created _lines variable:

        def render(self):
            height = self._tui_window.height
            width = self._tui_window.width
            lines = self._lines[-height:]
            self._tui_window.erase()
            for l in lines:
                if len(l) < width:
                    l += "\n"
                else:
                    l = l[0:width]
                self._tui_window.write(l)

    The first two lines of this method read the height and width from the gdb.TuiWindow object. These values can change every time the render method is called, because GDB might have resized the window.

    The third line of the function uses the height to select the last few lines from the list of all content lines.

    Next, erase clears the window contents. The function then loops through all the lines to display. If the line is shorter than the screen width, the if statement adds a newline. Otherwise, the else statement trims the line to exactly the screen width. Finally, the function calls write to add the line to the screen.

    The last thing you need to do is write the _before_prompt method:

        def _before_prompt(self):
            self._lines.append('The GDB prompt has been displayed. Good Job!')
            self.render()

    This method adds content to the _lines variable, which your render method can then display.

    Restart GDB using this command line:

    gdb -ex 'source history.py' \
        -ex 'tui new-layout example_1 history 1 cmd 1 status 1' \
        -ex 'layout example_1'

    With luck, your debugger should look something like Figure 3.

    The Value History screen displays text you requested before displaying the GDB prompt.
    Figure 3. The Value History screen displays text you requested before displaying the GDB prompt.
    Figure 3: The Value History screen displays text you requested before displaying the GDB prompt.

    If you press the Return key a few times, you should start to see the window fill with text. Next, if you reduce the width of your terminal, you should see the text truncated to the new width (Figure 4).

    GDB truncates each line if the window is too narrow to display it.
    Figure 4. GDB truncates each line if the window is too narrow to display it.
    Figure 4: GDB truncates each line if the window is too narrow to display it.

    Excellent. You are almost ready to replace the placeholder strings you've been using with the actual history values into the window, which the second article in this series accomplishes.

    Oh, no—a bug

    Start GDB and load your window, just as before. But this time, switch away from your new layout to a layout that doesn't use your custom window. The full set of GDB commands for this task are:

    $ gdb
    (gdb) source history.py
    (gdb) tui new-layout example_1 history 1 cmd 1 status 1
    (gdb) layout example_1
    (gdb) layout src

    You should see this error message from GDB in the terminal:

    Python Exception <class 'RuntimeError'>: TUI window is invalid.

    The problem is that, once GDB is no longer displaying your history window, the gdb.TuiWindow that represents it is invalidated and can no longer be used to write to the screen. However, your before_prompt event handler is still registered and continues to call render, which will try to write to the screen using the gdb.TuiWindow object.

    To remove this message, you need to disconnect the event listener when your history_window is being removed from the screen. This task is easily done by adding the close method to your history_window class, like this:

        def close(self):
            gdb.events.before_prompt.disconnect(self._before_prompt_listener)

    Now, when switching from the example_1 layout to the src layout, you no longer get an error.

    Conclusion

    This article got you started with displaying content dynamically in a GDB window. The next article in this series will show you how to display useful information by retrieving the values from GDB's history list.

    Last updated: November 6, 2023

    Related Posts

    • The GDB developer's GNU Debugger tutorial, Part 1: Getting started with the debugger

    • The GDB developer’s GNU Debugger tutorial, Part 2: All about debuginfo

    • How to debug stack frames and recursion in GDB

    Recent Posts

    • How to enable Ansible Lightspeed intelligent assistant

    • Why some agentic AI developers are moving code from Python to Rust

    • Confidential VMs: The core of confidential containers

    • Benchmarking with GuideLLM in air-gapped OpenShift clusters

    • Run Qwen3-Next on vLLM with Red Hat AI: A step-by-step guide

    What’s up next?

    Linux server image

    The Intermediate Linux Cheat Sheet introduces developers and system administrators to more Linux commands they should know.

    Get the free cheat sheet
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue