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:
pid
column of thepg_stat_activity
view.pg_backend_pid
function.- If you use psql it is easy to get the backend process pid using prompt variables.
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:
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:
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