On 10/30/2025 3:52 AM, Jakub Wartak wrote:
On Thu, Oct 30, 2025 at 10:40 AM Bryan Green <[email protected]> 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 <[email protected]>
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.build
index 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.c
index 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