Introduction

PostgreSQL has a process-based architecture. A single running PostgreSQL instance contains several types of processes: a single postgres server process, a backend process per each client connection and various maintenance background processes.

During the development of an extension it is often necessary to debug a backend process using gdb (or a different debugger). Along with debugging logs it helps to understand how to work with the extension API since it is often not clear and the PostgreSQL documentation might not be enough. Debugging also helps to find bugs in your extension or in PostgreSQL itself.

Using gdb

The common way of how you can debug a backend process is to attach to it using a debugger using its pid. You can get the pid by the following ways:

Since I use psql a lot I also use its prompt variables. Here is my PROMPT1 variable:

\set PROMPT1 'pid:%[%033[1;44;1m%]%p%[%033[0m%] %M:%> %n@%/%R%x%# '

Which gives the following output:

Running psql
Running psql

To attach to this backend process with gdb you just need to execute the following command in a separate terminal window:

gdb -p 10972

Using WezTerm

While the above method isn’t complicated it requires additional steps like copying or typing the pid, which is error-prone.

WezTerm terminal emulator can help here. It has powerful functions and it can be configured using the Lua language.

The configuration file can be named .wezterm.lua (or wezterm.lua depending on a place where it is stored). You can check where it can be placed in the documentation.

To achieve our goal first of all you need to use psql and the prompt variable to show the pid.

Secondly it is necessary to define a new rule to show the pid as a hyperlink using hyperlink_rules option. Here is an example:

config.hyperlink_rules = wezterm.default_hyperlink_rules()

table.insert(config.hyperlink_rules, {
  regex = 'pid:\\d+',
  format = '$0',
})

Now we can handle that hyperlink. In Wezterm we can handle various pre-defined events using wezterm.on function. For our purpose we need to handle open-uri event. Within that handler we need to check the uri variable and if it looks like a pid call gdb and pass the pid as an argument. Here is an example of open-uri handler:

-- Use the defaults as a base
wezterm.on('open-uri', function(window, pane, uri)
  local start, match_end = uri:find 'pid:'
  if start == 1 then
    local pid = uri:sub(match_end + 1)
    window:perform_action(
      wezterm.action.SpawnCommandInNewTab {
        args = { 'gdb', '-p', pid },
      },
      pane
    )
    -- prevent the default action from opening in a browser
    return false
  end
  -- otherwise, by not specifying a return value, we allow later
  -- handlers and ultimately the default action to caused the
  -- URI to be opened in the browser
end)

Lastly if you save the changes in .wezterm.lua file you can just click on the pid and gdb will start and attach to the backend process. Here how it might look like:

Starting gdb and attach to the backend
Starting gdb and attach to the backend

Complete example of the configuration file

Here is full .wezterm.lua configuration file:

local wezterm = require 'wezterm'
local config = {}

-- Use the defaults as a base
wezterm.on('open-uri', function(window, pane, uri)
  local start, match_end = uri:find 'pid:'
  if start == 1 then
    local pid = uri:sub(match_end + 1)
    window:perform_action(
      wezterm.action.SpawnCommandInNewTab {
        args = { 'gdb', '-p', pid },
      },
      pane
    )
    -- prevent the default action from opening in a browser
    return false
  end
  -- otherwise, by not specifying a return value, we allow later
  -- handlers and ultimately the default action to caused the
  -- URI to be opened in the browser
end)

config.hyperlink_rules = wezterm.default_hyperlink_rules()

table.insert(config.hyperlink_rules, {
  regex = 'pid:\\d+',
  format = '$0',
})

return config
  1. PostgreSQl Process and Memory Architecture
  2. WezTerm website