Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

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

    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

    • Federated identity across the hybrid cloud using zero trust workload identity manager

    • Confidential virtual machine storage attack scenarios

    • Introducing virtualization platform autopilot

    • Integrate zero trust workload identity manager with Red Hat OpenShift GitOps

    • Best Practice Configuration and Tuning for Linux and Windows VMs

    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

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    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
    © 2026 Red Hat

    Red Hat legal and privacy links

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

    Chat Support

    Please log in with your Red Hat account to access chat support.