From: Bryan Green <dbryan.green@gmail.com>
To: Jakub Wartak <jakub.wartak@enterprisedb.com>
Cc: Álvaro Herrera <alvherre@kurilemu.de>
Cc: Euler Taveira <euler@eulerto.com>
Cc: Michael Paquier <michael@paquier.xyz>
Cc: pgsql-hackers <pgsql-hackers@postgresql.org>
Subject: Re: [PATCH] Add Windows support for backtrace_functions (MSVC only)
Date: Thu, 30 Oct 2025 08:51:43 -0600
Message-ID: <30aea073-109c-43cc-979c-081b922d055d@gmail.com> (raw)
In-Reply-To: <CAKZiRmzuqPmgd_A+M+RiuT-4UtDt_TASqQJoqCdvCUrVY3w7cg@mail.gmail.com>
References: <202510300932.isdvgkzqzshk@alvherre.pgsql>
<5b4c6ce0-ed17-4abb-9127-859cc95cbdf3@gmail.com>
<CAKZiRmzuqPmgd_A+M+RiuT-4UtDt_TASqQJoqCdvCUrVY3w7cg@mail.gmail.com>
On 10/30/2025 3:52 AM, Jakub Wartak wrote:
> On Thu, Oct 30, 2025 at 10:40 AM Bryan Green <dbryan.green@gmail.com> wrote:
>>
>> On 10/30/2025 3:37 AM, Álvaro Herrera wrote:
>>> On 2025-Oct-30, Jakub Wartak wrote:
>>>
>>>> Hi Bryan, cfbot is red. I'm was fan of having those tests for this
>>>> (bring complexity and we didn't have tests for Linux backtrace
>>>> anyway), but now MINGW win32 is failing on those tests where the
>>>> feature is not present:
>>>
>>> I hate to say this after the code is written, but I think we should not
>>> put any tests in the first step. I predict that these are going to be
>>> enormously brittle and that we'll waste a lot of time making them
>>> stable. I think we should commit the Windows support for backtraces
>>> first, then consider whether we actually want TAP tests for the overall
>>> feature. We've gone several years with glibc backtrace support without
>>> any tests -- why do we think the Windows implementation thereof _must_
>>> necessarily have them?
>>>
>> It will not bother me to remove them. It was my first effort at writing
>> TAP tests, so it was a nice learning experience.
>
> Well, that was a typo on my part (stupid me), I wanted to write: I was
> NOT a fan of having those tests for this (in first place) - sorry for
> confusion!
>
> Anyway we have test because I think Michael and Euler triggered this
> but earlier i've tried to persuade NOT to do this (see: `Also is it
> worth it to test that setting backtrace_funciton=FOO really emits
> .*FOO.* in log message cross-platform way?`), anyway Bryan implemented
> this and it looks like v3 has just turned [gG]reen ;)
> (https://cirrus-ci.com/build/6001832838823936)
>
> -J.
I had reservations about the value the tests were adding, and
considering I am getting more concern around having the tests than not
having them for this initial release I have decided to remove them. v4
patch is attached. It is the same as the initial 0001-* patch.
Thanks,
Bryan Green
From 81ee688f490fa37b5eb30d1d88123e3d0a8423f2 Mon Sep 17 00:00:00 2001
From: Bryan Green <dbryan.green@gmail.com>
Date: Tue, 21 Oct 2025 19:31:29 -0500
Subject: [PATCH] Add Windows support for backtrace_functions
Implement backtrace generation on Windows using the DbgHelp API, providing
similar functionality to the existing Unix/Linux support. When PDB files
are available, backtraces include function names and source locations.
Without PDB files, raw addresses are shown.
DbgHelp is initialized once per backend and cleaned up via on_proc_exit().
This adds a dependency on dbghelp.lib for MSVC builds.
---
src/backend/meson.build | 5 +
src/backend/utils/error/elog.c | 174 +++++++++++++++++++++++++++++++++
2 files changed, 179 insertions(+)
diff --git a/src/backend/meson.build b/src/backend/meson.buildindex b831a54165..eeb69c4079 100644--- a/src/backend/meson.build+++ b/src/backend/meson.build@@ -1,6 +1,11 @@
# Copyright (c) 2022-2025, PostgreSQL Global Development Group
backend_build_deps = [backend_code]
++if host_system == 'windows' and cc.get_id() == 'msvc'+ backend_build_deps += cc.find_library('dbghelp')+endif+
backend_sources = []
backend_link_with = [pgport_srv, common_srv]
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.cindex 29643c5143..fc421ce444 100644--- a/src/backend/utils/error/elog.c+++ b/src/backend/utils/error/elog.c@@ -140,6 +140,13 @@ static void write_syslog(int level, const char *line);
static void write_eventlog(int level, const char *line, int len);
#endif
+#ifdef _MSC_VER+#include <windows.h>+#include <dbghelp.h>+static bool win32_backtrace_symbols_initialized = false;+static HANDLE win32_backtrace_process = NULL;+#endif+
/* We provide a small stack of ErrorData records for re-entrant cases */
#define ERRORDATA_STACK_SIZE 5
@@ -1116,6 +1123,18 @@ errbacktrace(void)
return 0;
}
+#ifdef _MSC_VER+/*+ * Cleanup function for DbgHelp resources.+ * Called via on_proc_exit() to release resources allocated by SymInitialize().+ */+static void+win32_backtrace_cleanup(int code, Datum arg)+{+ SymCleanup(win32_backtrace_process);+}+#endif+
/*
* Compute backtrace data and add it to the supplied ErrorData. num_skip
* specifies how many inner frames to skip. Use this to avoid showing the
@@ -1147,6 +1166,161 @@ set_backtrace(ErrorData *edata, int num_skip)
appendStringInfoString(&errtrace,
"insufficient memory for backtrace generation");
}
+#elif defined(_MSC_VER)+ {+ void *stack[100];+ DWORD frames;+ DWORD i;+ wchar_t buffer[sizeof(SYMBOL_INFOW) + MAX_SYM_NAME * sizeof(wchar_t)];+ PSYMBOL_INFOW symbol;+ char *utf8_buffer;+ int utf8_len;++ if (!win32_backtrace_symbols_initialized)+ {+ win32_backtrace_process = GetCurrentProcess();++ SymSetOptions(SYMOPT_UNDNAME |+ SYMOPT_DEFERRED_LOADS |+ SYMOPT_LOAD_LINES |+ SYMOPT_FAIL_CRITICAL_ERRORS);++ if (SymInitialize(win32_backtrace_process, NULL, TRUE))+ {+ win32_backtrace_symbols_initialized = true;+ on_proc_exit(win32_backtrace_cleanup, 0);+ }+ else+ {+ DWORD error = GetLastError();++ elog(WARNING, "SymInitialize failed with error %lu", error);+ }+ }++ frames = CaptureStackBackTrace(num_skip, lengthof(stack), stack, NULL);++ if (frames == 0)+ {+ appendStringInfoString(&errtrace, "\nNo stack frames captured");+ edata->backtrace = errtrace.data;+ return;+ }++ symbol = (PSYMBOL_INFOW) buffer;+ symbol ->MaxNameLen = MAX_SYM_NAME;+ symbol ->SizeOfStruct = sizeof(SYMBOL_INFOW);++ for (i = 0; i < frames; i++)+ {+ DWORD64 address = (DWORD64) (stack[i]);+ DWORD64 displacement = 0;+ BOOL sym_result;++ sym_result = SymFromAddrW(win32_backtrace_process,+ address,+ &displacement,+ symbol);++ if (sym_result)+ {+ IMAGEHLP_LINEW64 line;+ DWORD line_displacement = 0;++ line.SizeOfStruct = sizeof(IMAGEHLP_LINEW64);++ if (SymGetLineFromAddrW64(win32_backtrace_process,+ address,+ &line_displacement,+ &line))+ {+ /* Convert symbol name to UTF-8 */+ utf8_len = WideCharToMultiByte(CP_UTF8, 0, symbol->Name, -1,+ NULL, 0, NULL, NULL);+ if (utf8_len > 0)+ {+ char *filename_utf8;+ int filename_len;++ utf8_buffer = palloc(utf8_len);+ WideCharToMultiByte(CP_UTF8, 0, symbol->Name, -1,+ utf8_buffer, utf8_len, NULL, NULL);++ /* Convert file name to UTF-8 */+ filename_len = WideCharToMultiByte(CP_UTF8, 0,+ line.FileName, -1,+ NULL, 0, NULL, NULL);+ if (filename_len > 0)+ {+ filename_utf8 = palloc(filename_len);+ WideCharToMultiByte(CP_UTF8, 0, line.FileName, -1,+ filename_utf8, filename_len,+ NULL, NULL);++ appendStringInfo(&errtrace,+ "\n%s+0x%llx [%s:%lu]",+ utf8_buffer,+ (unsigned long long) displacement,+ filename_utf8,+ (unsigned long) line.LineNumber);++ pfree(filename_utf8);+ }+ else+ {+ appendStringInfo(&errtrace,+ "\n%s+0x%llx [0x%llx]",+ utf8_buffer,+ (unsigned long long) displacement,+ (unsigned long long) address);+ }++ pfree(utf8_buffer);+ }+ else+ {+ /* Conversion failed, use address only */+ appendStringInfo(&errtrace,+ "\n[0x%llx]",+ (unsigned long long) address);+ }+ }+ else+ {+ /* No line info, convert symbol name only */+ utf8_len = WideCharToMultiByte(CP_UTF8, 0, symbol->Name, -1,+ NULL, 0, NULL, NULL);+ if (utf8_len > 0)+ {+ utf8_buffer = palloc(utf8_len);+ WideCharToMultiByte(CP_UTF8, 0, symbol->Name, -1,+ utf8_buffer, utf8_len, NULL, NULL);++ appendStringInfo(&errtrace,+ "\n%s+0x%llx [0x%llx]",+ utf8_buffer,+ (unsigned long long) displacement,+ (unsigned long long) address);++ pfree(utf8_buffer);+ }+ else+ {+ /* Conversion failed, use address only */+ appendStringInfo(&errtrace,+ "\n[0x%llx]",+ (unsigned long long) address);+ }+ }+ }+ else+ {+ appendStringInfo(&errtrace,+ "\n[0x%llx]",+ (unsigned long long) address);+ }+ }+ }
#else
appendStringInfoString(&errtrace,
"backtrace generation is not supported by this installation");
--
2.46.0.windows.1
Attachments:
[text/plain] v4-0001-Add-Windows-support-for-backtrace_functions.patch (6.5K, ../30aea073-109c-43cc-979c-081b922d055d@gmail.com/2-v4-0001-Add-Windows-support-for-backtrace_functions.patch)
download | inline diff:
From 81ee688f490fa37b5eb30d1d88123e3d0a8423f2 Mon Sep 17 00:00:00 2001
From: Bryan Green <dbryan.green@gmail.com>
Date: Tue, 21 Oct 2025 19:31:29 -0500
Subject: [PATCH] Add Windows support for backtrace_functions
Implement backtrace generation on Windows using the DbgHelp API, providing
similar functionality to the existing Unix/Linux support. When PDB files
are available, backtraces include function names and source locations.
Without PDB files, raw addresses are shown.
DbgHelp is initialized once per backend and cleaned up via on_proc_exit().
This adds a dependency on dbghelp.lib for MSVC builds.
---
src/backend/meson.build | 5 +
src/backend/utils/error/elog.c | 174 +++++++++++++++++++++++++++++++++
2 files changed, 179 insertions(+)
diff --git a/src/backend/meson.build b/src/backend/meson.buildindex b831a54165..eeb69c4079 100644--- a/src/backend/meson.build+++ b/src/backend/meson.build@@ -1,6 +1,11 @@
# Copyright (c) 2022-2025, PostgreSQL Global Development Group
backend_build_deps = [backend_code]
++if host_system == 'windows' and cc.get_id() == 'msvc'+ backend_build_deps += cc.find_library('dbghelp')+endif+
backend_sources = []
backend_link_with = [pgport_srv, common_srv]
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.cindex 29643c5143..fc421ce444 100644--- a/src/backend/utils/error/elog.c+++ b/src/backend/utils/error/elog.c@@ -140,6 +140,13 @@ static void write_syslog(int level, const char *line);
static void write_eventlog(int level, const char *line, int len);
#endif
+#ifdef _MSC_VER+#include <windows.h>+#include <dbghelp.h>+static bool win32_backtrace_symbols_initialized = false;+static HANDLE win32_backtrace_process = NULL;+#endif+
/* We provide a small stack of ErrorData records for re-entrant cases */
#define ERRORDATA_STACK_SIZE 5
@@ -1116,6 +1123,18 @@ errbacktrace(void)
return 0;
}
+#ifdef _MSC_VER+/*+ * Cleanup function for DbgHelp resources.+ * Called via on_proc_exit() to release resources allocated by SymInitialize().+ */+static void+win32_backtrace_cleanup(int code, Datum arg)+{+ SymCleanup(win32_backtrace_process);+}+#endif+
/*
* Compute backtrace data and add it to the supplied ErrorData. num_skip
* specifies how many inner frames to skip. Use this to avoid showing the
@@ -1147,6 +1166,161 @@ set_backtrace(ErrorData *edata, int num_skip)
appendStringInfoString(&errtrace,
"insufficient memory for backtrace generation");
}
+#elif defined(_MSC_VER)+ {+ void *stack[100];+ DWORD frames;+ DWORD i;+ wchar_t buffer[sizeof(SYMBOL_INFOW) + MAX_SYM_NAME * sizeof(wchar_t)];+ PSYMBOL_INFOW symbol;+ char *utf8_buffer;+ int utf8_len;++ if (!win32_backtrace_symbols_initialized)+ {+ win32_backtrace_process = GetCurrentProcess();++ SymSetOptions(SYMOPT_UNDNAME |+ SYMOPT_DEFERRED_LOADS |+ SYMOPT_LOAD_LINES |+ SYMOPT_FAIL_CRITICAL_ERRORS);++ if (SymInitialize(win32_backtrace_process, NULL, TRUE))+ {+ win32_backtrace_symbols_initialized = true;+ on_proc_exit(win32_backtrace_cleanup, 0);+ }+ else+ {+ DWORD error = GetLastError();++ elog(WARNING, "SymInitialize failed with error %lu", error);+ }+ }++ frames = CaptureStackBackTrace(num_skip, lengthof(stack), stack, NULL);++ if (frames == 0)+ {+ appendStringInfoString(&errtrace, "\nNo stack frames captured");+ edata->backtrace = errtrace.data;+ return;+ }++ symbol = (PSYMBOL_INFOW) buffer;+ symbol ->MaxNameLen = MAX_SYM_NAME;+ symbol ->SizeOfStruct = sizeof(SYMBOL_INFOW);++ for (i = 0; i < frames; i++)+ {+ DWORD64 address = (DWORD64) (stack[i]);+ DWORD64 displacement = 0;+ BOOL sym_result;++ sym_result = SymFromAddrW(win32_backtrace_process,+ address,+ &displacement,+ symbol);++ if (sym_result)+ {+ IMAGEHLP_LINEW64 line;+ DWORD line_displacement = 0;++ line.SizeOfStruct = sizeof(IMAGEHLP_LINEW64);++ if (SymGetLineFromAddrW64(win32_backtrace_process,+ address,+ &line_displacement,+ &line))+ {+ /* Convert symbol name to UTF-8 */+ utf8_len = WideCharToMultiByte(CP_UTF8, 0, symbol->Name, -1,+ NULL, 0, NULL, NULL);+ if (utf8_len > 0)+ {+ char *filename_utf8;+ int filename_len;++ utf8_buffer = palloc(utf8_len);+ WideCharToMultiByte(CP_UTF8, 0, symbol->Name, -1,+ utf8_buffer, utf8_len, NULL, NULL);++ /* Convert file name to UTF-8 */+ filename_len = WideCharToMultiByte(CP_UTF8, 0,+ line.FileName, -1,+ NULL, 0, NULL, NULL);+ if (filename_len > 0)+ {+ filename_utf8 = palloc(filename_len);+ WideCharToMultiByte(CP_UTF8, 0, line.FileName, -1,+ filename_utf8, filename_len,+ NULL, NULL);++ appendStringInfo(&errtrace,+ "\n%s+0x%llx [%s:%lu]",+ utf8_buffer,+ (unsigned long long) displacement,+ filename_utf8,+ (unsigned long) line.LineNumber);++ pfree(filename_utf8);+ }+ else+ {+ appendStringInfo(&errtrace,+ "\n%s+0x%llx [0x%llx]",+ utf8_buffer,+ (unsigned long long) displacement,+ (unsigned long long) address);+ }++ pfree(utf8_buffer);+ }+ else+ {+ /* Conversion failed, use address only */+ appendStringInfo(&errtrace,+ "\n[0x%llx]",+ (unsigned long long) address);+ }+ }+ else+ {+ /* No line info, convert symbol name only */+ utf8_len = WideCharToMultiByte(CP_UTF8, 0, symbol->Name, -1,+ NULL, 0, NULL, NULL);+ if (utf8_len > 0)+ {+ utf8_buffer = palloc(utf8_len);+ WideCharToMultiByte(CP_UTF8, 0, symbol->Name, -1,+ utf8_buffer, utf8_len, NULL, NULL);++ appendStringInfo(&errtrace,+ "\n%s+0x%llx [0x%llx]",+ utf8_buffer,+ (unsigned long long) displacement,+ (unsigned long long) address);++ pfree(utf8_buffer);+ }+ else+ {+ /* Conversion failed, use address only */+ appendStringInfo(&errtrace,+ "\n[0x%llx]",+ (unsigned long long) address);+ }+ }+ }+ else+ {+ appendStringInfo(&errtrace,+ "\n[0x%llx]",+ (unsigned long long) address);+ }+ }+ }
#else
appendStringInfoString(&errtrace,
"backtrace generation is not supported by this installation");
--
2.46.0.windows.1
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: dbryan.green@gmail.com, jakub.wartak@enterprisedb.com, alvherre@kurilemu.de, euler@eulerto.com, michael@paquier.xyz
Subject: Re: [PATCH] Add Windows support for backtrace_functions (MSVC only)
In-Reply-To: <30aea073-109c-43cc-979c-081b922d055d@gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox