GDB Plugin

Martin Gergov

T.C. Hollingsworth

Introduction

Kate's GDB plugin provides a simple frontend to any debugger that supports the Debugger Adapter Protocol. In particular, that includes the GNU Project Debugger, aka GDB, as outlined here.

Important

Previous experience with GDB is strongly recommended. For more information on using GDB, visit the GDB website.

You can enable the GDB plugin in the Plugins section of Kate's configuration.

Tip

If you compile using gcc/g++ you might want to use the -ggdb command line argument.

After these preparations are made, open the source file in Kate, select the "debugger profile", enter the path to the executable in the Settings tab of the Debug View tool view, and select DebugStart Debugging from the menu to get started.

The "debugger profile" selects the DAP server to use (e.g. GDB) and the way in which to launch this server. A typical case is to have the server launch a process as specified above, but it may also attach to a running process (in which case a PID will have to be specified rather than an executable). There may also be other modes which are specific to the language and DAP server. See also later for additional background and configuration details on this.

Menu and Toolbar Structure

All of these options are available in Kate's menus, and many are available on the Debug toolbar as well.

ViewTool ViewShow Debug View

Shows a tool view containing GDB output, the GDB command line used, and other settings.

ViewTool ViewShow Locals and Stack

Shows a list of all currently loaded variables and their values and a GDB backtrace.

DebugTargets

A submenu containing a list of targets (executables).

DebugStart Debugging

Starts GDB with a target.

DebugKill / Stop Debugging

Stops GDB.

DebugRestart Debugging

Restarts GDB.

DebugToggle Breakpoint / Break

Set a breakpoint at the current cursor position.

DebugStep In

Execute the present statement (function call will be debugged).

DebugStep Over

Execute the present statement (function call will not be debugged).

DebugStep Out

Resumes execution until the program that is executing terminates.

DebugMove PC

Move program counter (next execution).

DebugRun To Cursor

Runs the program until it reaches current cursor position.

DebugContinue

Ignores any breakpoints and executes program until it terminates (successfully or not).

DebugPrint Value

Prints the value of the variable that the cursor is currently pointing to.

SettingsToolbars ShownGDB Plugin

Display the debugging toolbar.

Debug View

The Debug View tool view consists of several tabs:

GDB Output

Contains output from GDB and a GDB command line.

The Output tab.

The Output tab displaying the output from a debugging session.

Settings
Executable

Path to the target (executable) for debugging.

Working Directory

The current working directory provided to the target.

Arguments

Arguments passed to the program.

Keep focus

Keeps focus on the GDB command line.

Redirect IO

Opens a new IO tab in the Debug View where you can view output and provide input to the running program.

The Settings dialog

The Settings dialog displaying the configuration of a debugging session.

IO

Contains an area that displays output from the running program and a command line where you may provide input to it.

The IO tab.

The IO tab displaying output from a simple test program.

Call Stack and Locals

The Call Stack tool view contains a list of the formatted backtrace returned from GDB.

The Call Stack tool view.

The GDB Plugin's Call Stack tool view.

The Locals tool view contains a list of all currently loaded variables from the program and their corresponding values.

The Locals tool view.

The GDB Plugin's Locals tool view.

Thanks and Acknowledgments

Special thanks to Google Code-In 2011 participant Martin Gergov for writing much of this section.

Configuration

The plugin's configuration page defines the "debugger profiles" which can be selected. The default configuration (JSON) is shown there, and it can be "overlayed" by a user provided of similar form. An example excerpt is as follows:

{
    "dap": {
        "debugpy": {
            "url": "https://github.com/microsoft/debugpy",
            "run": {
                "command": ["python", "-m", "debugpy", "--listen", "${#run.port}", "--wait-for-client"],
                "port": 0,
                "supportsSourceRequest": false
            },
            "configurations": {
                "launch": {
                    "commandArgs": ["${file}", "${args|list}"],
                    "request": {
                        "command": "attach",
                        "stopOnEntry": true,
                        "redirectOutput": true
                    }
                },
                "attach": {
                    "commandArgs": ["--pid", "${pid}"],
                    "request": {
                        "command": "attach",
                        "stopOnEntry": true,
                        "redirectOutput": true
                    }
                },
        },
        "gdb": {
            "url": "gdb",
            "run": {
                "command": [
                    "gdb",
                    "-i",
                    "dap"
                ],
                "redirectStderr": true,
                "redirectStdout": true,
                "supportsSourceRequest": true
            },
            "configurations": {
                "launch (debug)": {
                    "request": {
                        "command": "launch",
                        "mode": "debug",
                        "program": "${file}",
                        "args": "${args|list}",
                        "cwd": "${workdir}"
                    }
                }
            }
        }
    }
}

Each of the entries in configurations is combined with the run data and forms a "profile". This specifies the DAP server to launch along with its arguments, where the latter are specific to the profile (commandArgs). The other parts specify the DAP protocol request (launch or attach), along with DAP specific extensions.

Of course, the specified server should be installed (and typically also in PATH for proper execution).

Various stages of override/merge are applied; user configuration (loaded from file) overrides (internal) default configuration, and the "dap" entry in .kateproject project configuration in turn overrides.

Execution environment setup

More background and specifics can be found in the LSP Client Execution environment section below. But suffice it to say here it may be needed to run the debuggee process (and the DAP server) in a "special environment", whether merely defined by environment variables or some container (providing the required dependencies and circumstances for proper execution).

Similar to the example in the referenced section, the following configuration may be provided in a .kateproject.

{
    // this may also be an array of objects
    "exec": {
        "hostname": "foobar"
        // the command could also be an array of string
        "prefix": "podman exec -i foobarcontainer",
        "mapRemoteRoot": true,
        "pathMappings": [
            // either of the following forms are possible
            // a more automagic alternative exists as well, see referenced section
            [ "/dir/on/host", "/mounted/in/container" ]
            { "localRoot": "/local/dir", "remoteRoot": "/remote/dir" }
        ]
    },
    "dap": {
        "debugpy": {
            "run": {
                // in this section, it applies to all configurations
                // this will match/join with the above object
                "exec": { "hostname": "foobar" },
                // if server is connected to,
                // optionally specify explicit port (which is suitable published/forwarded)
                "port": 5678,
                // the server may then also have to accept more than localhost
                "host": "0.0.0.0"
            }
        }
    }
}

The referenced section should be consulted for details, but in essence the prefix will be prepended before the DAP server command line specified elsewhere. The effect is that the server is run within the specified container and then in turn also the launched process. The pathMapping arranges for transformation of filepaths between editor's view and DAP server (container) view, e.g. when dealing with setting of breakpoints or handling of reported backtraces. Note that such mapping is optional and may or may not be useful. When dealing with C/C++ code that is compiled on "host", the symbol info references source files on host which do not exist at all in the other environment. However, in other scripted (e.g. python) circumstances, actual runtime files are referenced (on the other environment).

The following must evidently be kept in mind.

  • The DAP server must be present in the environment/container, which must be configured to support proper debugger operation (so, if needed, privileged, capabilities).

  • Communication between editor and DAP must be possible. In case of a container, the latter should either use host networking, or provide a suitable mapped/published port along with corresponding config snippet as in above example (as an auto-selected one in case of port 0 would not make it through).

  • A specified executable/PID should be in "container" perspective, as well as any (debuggee executable) arguments.

Also, as in the LSP case, some environment variables are set; KATE_EXEC_PLUGIN is set to dap, KATE_EXEC_SERVER is set to the debugger/language type (e.g. python) and KATE_EXEC_PROFILE is set to the configuration entry (e.g. launch).