Debugging PostgreSQL Source Code on Mac + VSCode

Aug 31, 2025

3 min
💬

A step-by-step guide to debugging PostgreSQL source code on Mac with VSCode, covering the full flow from building from source to attach debugging with lldb.

1. Overview

Debugging PostgreSQL source code means attaching a debugger to a running database server process and tracing, at the code level, how a single SQL statement flows through the parser, planner, and executor. On Mac the default debugger is lldb, so the key setup is building PostgreSQL with debug symbols included and attaching to the backend process from VSCode.

TIP

As of the time of writing, version 17 is the latest release (excluding betas), so this article uses version 17 as the baseline.

The overall flow isn't much different from other operating systems (Linux, Windows), but on Mac you have to use lldb (Low Level Debugger, LLVM-based) instead of gdb, so a few settings and commands differ. By following the steps in this article, you'll be able to do the following yourself.

  • Build the PostgreSQL source code in debug mode and run the server
  • Attach to a PostgreSQL backend process with lldb from VSCode
  • Set breakpoints and trace query execution flow at the code level

2. Setting Up the PostgreSQL Debugging Environment

2.1. Getting the PostgreSQL Source Code

PostgreSQL provides an official mirror on GitHub, but the actual upstream source is managed in a separate repository. So instead of GitHub, you should use the PostgreSQL's own Git repository.

Bash
git clone https://git.postgresql.org/git/postgresql.git
cd postgresql

After cloning, you're on the default master branch. To debug a specific version, checkout the corresponding branch/commit.

Bash
## 17 버전 STABLE checkout
git checkout REL_17_STABLE && git pull
 
## 특정 마이너 버전 checkout
git checkout REL_17_6

2.2. Building PostgreSQL

Install the packages required for the build.

Bash
brew install icu4c
brew install pkg-config

Following the official PostgreSQL build procedure, run configure before building.

Bash
PG_VERSION=17
 
./configure \
	--prefix=$HOME/postgres/pg${PG_VERSION} \
	--enable-cassert \
	--enable-debug CFLAGS="-ggdb -O0 -fno-omit-frame-pointer" CPPFLAGS="-g -O0" \
	--with-includes=$(brew --prefix icu4c)/include \
	--with-libraries=$(brew --prefix icu4c)/lib \
	PKG_CONFIG_PATH=$(brew --prefix icu4c)/lib/pkgconfig

Option breakdown:

  • --prefix: PostgreSQL installation path
  • --enable-cassert: enables internal C Assert statements
  • --enable-debug: enables the debug build
  • CFLAGS: -ggdb (debug symbols), -O0 (disables optimization), -fno-omit-frame-pointer (easier stack tracing)
  • CPPFLAGS: -g (debug symbols)
  • --with-includes/--with-libraries: icu4c header/library paths
  • PKG_CONFIG_PATH: pkg-config library search path

Once configure finishes, src/Makefile.global is generated. Check that the intended options (especially -g and -O0) actually made it in. If they're missing, you won't be able to see variable values while debugging — I learned this the hard way.

Bash
grep -E '^(CFLAGS|CPPFLAGS) = ' src/Makefile.global
Bash
CPPFLAGS = -isysroot $(PG_SYSROOT) -g -O0  -I/opt/homebrew/opt/icu4c@77/include
CFLAGS = -Wall ... -g -ggdb -O0 -fno-omit-frame-pointer

Now build. The output lands in $HOME/postgres/pg17, the path specified with --prefix.

Bash
make && make install

WARNING

The crucial part of the build is making sure -g -O0 is included in CFLAGS and CPPFLAGS. Without it, you cannot inspect variable values while debugging.

2.3. Running the PostgreSQL Server

Create PGDATA (the data directory) with initdb.

Bash
$HOME/postgres/pg17/bin/initdb -D $HOME/postgres/pgdata/pg17

Before starting the server, check key settings such as port and max_connections in $HOME/postgres/pgdata/pg17/postgresql.conf. Now start the server and connect.

Bash
$HOME/postgres/pg17/bin/pg_ctl -D $HOME/postgres/pgdata/pg17 -l $HOME/postgres/pgdata/pg17/logfile start
## waiting for server to start.... done / server started
 
$HOME/postgres/pg17/bin/createdb -p 5432 test
$HOME/postgres/pg17/bin/psql -p 5432 test

NOTE

Typing $HOME/postgres/pg17/bin every time is tedious. You could register it as an environment variable, but if you're debugging multiple PostgreSQL versions at once the paths can collide, so I don't recommend it.

3. Configuring the VSCode Debugging Environment

VS Code debugger settings can be applied per project or globally. This article uses the global settings approach.

NOTE

To configure it per project, create .vscode/launch.json in the source directory and add the contents below.

  1. Open the command palette with Command + Shift + P.
  2. Search for settings.json and select Preferences: Open User Settings (JSON).

Opening settings.json

  1. Add the following to settings.json.
View the VSCode global launch configuration
JSON
"launch": {
  "version": "0.2.0",
  "configurations": [
    {
      "name": "(lldb) Attach DB - PostgreSQL 17",
      "type": "cppdbg",
      "request": "attach",
      "program": "${env:HOME}/postgres/pg${input:pg_version}/bin/postgres",
      "MIMode": "lldb"
    }
  ],
  "inputs": [
    {
      "id": "pg_version",
      "type": "promptString",
      "description": "Enter the PostgreSQL Version",
      "default": "17"
    }
  ]
}
  1. After connecting to the server, check the process PID with SELECT pg_backend_pid();.
SQL
SELECT pg_backend_pid();
 pg_backend_pid
----------------
          85770
(1 row)
  1. Launch the debugger with fn + F5 or Run and Debug → (lldb) Attach DB - PostgreSQL 17, then enter the version and the pg_backend_pid value.

Running lldb attach

NOTE

The key part of the VSCode configuration is setting MIMode to lldb and request to attach so it connects to the running PostgreSQL backend process.

4. Debugging Test

  1. Find the query execution function exec_simple_query in src/backend/tcop/postgres.c and set a breakpoint.

exec_simple_query breakpoint

  1. Run a query from psql.
SQL
CREATE TABLE test(id bigint not null generated by default as identity, name text);
  1. You can confirm the breakpoint triggers as expected. From here you can debug your way through PostgreSQL and learn how it works at the code level.

Debugging screen paused at the triggered breakpoint

5. Wrapping Up

StepKey points
Preparing the source codeClone from the official PostgreSQL Git repository and checkout the desired version
Debug buildAlways include the -g -O0 options in configure (disables optimization)
Running the serverCreate PGDATA with initdb, then start with pg_ctl
VSCode setupConnect to the backend process PID in lldb · attach mode
DebuggingSet a breakpoint, run a query, and trace the code flow

TIP

With a debugger you can learn how PostgreSQL works by tracing its internals directly at the code level as it executes.

6. References