Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vucMX-00G4a7-1y for pgsql-hackers@arkaria.postgresql.org; Mon, 23 Feb 2026 20:17:01 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vucMW-00F0aG-1o for pgsql-hackers@arkaria.postgresql.org; Mon, 23 Feb 2026 20:17:00 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vucMV-00F0a7-37 for pgsql-hackers@lists.postgresql.org; Mon, 23 Feb 2026 20:17:00 +0000 Received: from fhigh-b2-smtp.messagingengine.com ([202.12.124.153]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1vucMS-00000000v8w-1uMn for pgsql-hackers@postgresql.org; Mon, 23 Feb 2026 20:16:59 +0000 Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailfhigh.stl.internal (Postfix) with ESMTP id 91D327A0163; Mon, 23 Feb 2026 15:16:55 -0500 (EST) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-04.internal (MEProxy); Mon, 23 Feb 2026 15:16:56 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kurilemu.de; h= cc:cc:content-transfer-encoding:content-type:content-type:date :date:from:from:in-reply-to:in-reply-to:message-id:mime-version :reply-to:subject:subject:to:to; s=fm2; t=1771877815; x= 1771964215; bh=7tMdAPwwheSlTL4DF2kbKlDGWl/sl1eF9ZGxnF7lTwc=; b=I 60Ck5lgOAr+83E9DNW7wd6//VBcTKOJ7M4TTf+2OBTg7QAbneqQaYJ4yDKW3U41D uoiEFuT5BVPju/4MqEuNziYcTnSVniIxyLaPlQ7x2vb2upkp/VRk8/u6PIlQWQeJ c55RbbeKYn9oAzUXB30xYj7M9zhlH3v4nA/VscyJHoAh2CE5e3B0FupjJTdGxchY CKKw+6mUcnTjjFpZO1aeVc6dr9t1IV1/9W7KWbt8hvdehIEQEmyanGr6PofN/iLh Fe+azeSCsSW00QEWXthTiVgseLBLXbYAcErvs6pt2sVbZZ0lZMw2TlqBhd0D1M/m E9PYBsxVl6w7rHA7KrJuA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:content-type:date:date:feedback-id:feedback-id :from:from:in-reply-to:in-reply-to:message-id:mime-version :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1771877815; x=1771964215; bh=7 tMdAPwwheSlTL4DF2kbKlDGWl/sl1eF9ZGxnF7lTwc=; b=L6FDEza2o0Ytva1Zj 9barq/ebkeyVWN+m5AmIw12Z9KoP2LoEKzsAoy1LPpn1yEZ+0P8/aXa5AYpdkeAR riTehsyVrLzAFyEHHMrXm62sK1428LoyYl6ExEp1VAF/QmAH+WeLs13nqfgDU/mw 6M2VFCRJCzOqRiOCqOJAbDT0E0siwdWfTnEjUFIHjuV+ohTbZ7ivGxM83HAsxctg kamiJs98ddz7F77ku36+WLiZgo4pLShenV6Q+4ZM6wCUzGa3s5CcoG2W+1hoaGmp K0P/vyEr6nBpjYDa+T5Ke37Y7Ps3oHPVq0hZSPh1esxJ7jl7YqTm6ywQWH5Hb3pe SlYYw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefgedrtddtgddvfeekudekucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnegouf hushhpvggtthffohhmrghinhculdegledmnecujfgurhepfffhvfevuffkgggtugfgjges mhekreertddtjeenucfhrhhomheplmhlvhgrrhhoucfjvghrrhgvrhgruceorghlvhhhvg hrrhgvsehkuhhrihhlvghmuhdruggvqeenucggtffrrghtthgvrhhnpeejveeggfegffeu heeuvdfhlefgteeuueelhfefleehjeegheelkeetfeeileeiudenucffohhmrghinhepmh hitghrohhsohhfthdrtghomhdpvghnthgvrhhprhhishgvuggsrdgtohhmpdhgohhoghhl vgdrtghomhenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhroh hmpegrlhhvhhgvrhhrvgeskhhurhhilhgvmhhurdguvgdpnhgspghrtghpthhtohephedp mhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepjhgrkhhusgdrfigrrhhtrghksegvnh htvghrphhrihhsvggusgdrtghomhdprhgtphhtthhopegvuhhlvghrsegvuhhlvghrthho rdgtohhmpdhrtghpthhtohepuggsrhihrghnrdhgrhgvvghnsehgmhgrihhlrdgtohhmpd hrtghpthhtohepmhhitghhrggvlhesphgrqhhuihgvrhdrgiihiidprhgtphhtthhopehp ghhsqhhlqdhhrggtkhgvrhhssehpohhsthhgrhgvshhqlhdrohhrgh X-ME-Proxy: Feedback-ID: ie3de48e3:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Mon, 23 Feb 2026 15:16:54 -0500 (EST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kurilemu.de; s=schmee; t=1771877810; bh=gkfL68/8rYrjU/jz1xbT/2abUdaBIV5VY3wxflC0rQ4=; h=Date:From:To:Cc:Subject:In-Reply-To:From; b=fbQ/UJGopRCEq6CJj4jqHuagPhOpUoNaCkHzPY7jDfUahvnvxq2wkQZp5o0gxW+5I iBrNJsT8pAP8osAkmqCLwsqdqv4zsMAcgFnOzTe8PgjiG3SPwdJwrjpyam03za71B0 3h0OgmREFY1UgBf4TKy8fb1SfnqjpBdU2gGsElsnH2AtNDHp+Q6dlrJGuSp2eilYKK kqb1jv3ADHNKRZAlpkXvd9Q2PwW4OL/QC1QVTRYnkG/PrQG4au17yfNRKcYlAYcHbf QPL+9RSAv5x1IdNRRNfrW8tseo0N8CDpEGd7wrp6jv3sOnE6+KP6eZj1pV8UPIeYnj pZjr0RL0hnyFA== Received: by schmee.kurilemu.internal (Postfix, from userid 1000) id 5556278; Mon, 23 Feb 2026 21:16:50 +0100 (CET) Date: Mon, 23 Feb 2026 21:16:50 +0100 From: =?utf-8?Q?=C3=81lvaro?= Herrera To: Bryan Green Cc: Euler Taveira , Jakub Wartak , Michael Paquier , pgsql-hackers Subject: Re: [PATCH] Add Windows support for backtrace_functions (MSVC only) Message-ID: <202602232005.2cmbj5ougsfv@alvherre.pgsql> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="h24v6pggzlszygs4" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --h24v6pggzlszygs4 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit Hello, thanks for following up. However, I was going over the commit message one final time after your reply, and I noticed that SymInitialize()'s documentation says quite explicitly: "Do not use the handle returned by GetCurrentProcess" https://learn.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-syminitialize which is exactly what we do with this patch. I think we should DuplicateHandle() on the GetCurrentProcess() handle (which then obviously needs CloseHandle afterwards). Would you agree? Here's the whole thing again with edited commit message and some trivial stylistic changes. I was amused to discover that Solaris, macOS and FreeBSD all copied the backtrace() interface from glibc, and the only platform in the buildfarm other than Windows that seems to lack support for backtraces is Alpine Linux. Regards -- Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/ "Ed is the standard text editor." http://groups.google.com/group/alt.religion.emacs/msg/8d94ddab6a9b0ad3 --h24v6pggzlszygs4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v8-0001-Add-backtrace-support-for-Windows-using-DbgHelp-A.patch" From a1666e89d771c036df71fc70155ce706114657d8 Mon Sep 17 00:00:00 2001 From: Bryan Green Date: Sat, 1 Nov 2025 23:04:24 -0600 Subject: [PATCH v8] Add backtrace support for Windows using DbgHelp API Previously, backtrace generation on Windows would return an "unsupported" message. With this commit, we rely on CaptureStackBackTrace() to capture the call stack and the DbgHelp API (SymFromAddrW, SymGetLineFromAddrW64) for symbol resolution. Symbol handler initialization (SymInitialize) is performed once per process and cached. If initialization fails, the report for it is returned as the backtrace output. The symbol handler is cleaned up via on_proc_exit() to release DbgHelp resources. The implementation provides symbol names, offsets, and addresses. When PDB files are available, it also includes source file names and line numbers. Symbol names and file paths are converted from UTF-16 to the database encoding using wchar2char(), which properly handles both UTF-8 and non-UTF-8 databases on Windows. When symbol information is unavailable or encoding conversion fails, it falls back to displaying raw addresses. The implementation uses the explicit Unicode versions of the DbgHelp functions (SYMBOL_INFOW, SymFromAddrW, IMAGEHLP_LINEW64, SymGetLineFromAddrW64) rather than the generic versions. This allows us to rely on predictable encoding conversion from Unicode, rather than using the haphazard ANSI codepage that we'd get otherwise. DbgHelp is apparently available on all Windows platforms we support, so there are no version number checks. Author: Bryan Green Reviewed-by: Euler Taveira Reviewed-by: Jakub Wartak Reviewed-by: Greg Burd Discussion: https://postgr.es/m/a692c0fe-caca-4c08-9c5d-debfd0ef2504@gmail.com --- src/backend/meson.build | 6 ++ src/backend/utils/error/elog.c | 153 ++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 3 deletions(-) diff --git a/src/backend/meson.build b/src/backend/meson.build index 712a857cdb4..9041cde59fd 100644 --- a/src/backend/meson.build +++ b/src/backend/meson.build @@ -41,6 +41,12 @@ backend_link_args = [] backend_link_depends = [] +# On Windows also make the backend depend on dbghelp, for backtrace support +if host_system == 'windows' and cc.get_id() == 'msvc' + backend_build_deps += cc.find_library('dbghelp') +endif + + # On windows when compiling with msvc we need to make postgres export all its # symbols so that extension libraries can use them. For that we need to scan # the constituting objects and generate a file specifying all the functions as diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index cb1c9d85ffe..d406f18f9d7 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -66,6 +66,10 @@ #include #endif +#ifdef _MSC_VER +#include +#endif + #include "access/xact.h" #include "common/ip.h" #include "libpq/libpq.h" @@ -140,6 +144,11 @@ static void write_syslog(int level, const char *line); static void write_eventlog(int level, const char *line, int len); #endif +#ifdef _MSC_VER +static bool backtrace_symbols_initialized = false; +static HANDLE backtrace_process = NULL; +#endif + /* We provide a small stack of ErrorData records for re-entrant cases */ #define ERRORDATA_STACK_SIZE 5 @@ -180,6 +189,7 @@ static void set_stack_entry_location(ErrorData *edata, const char *funcname); static bool matches_backtrace_functions(const char *funcname); static pg_noinline void set_backtrace(ErrorData *edata, int num_skip); +static void backtrace_cleanup(int code, Datum arg); static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str); static void FreeErrorDataContents(ErrorData *edata); static int log_min_messages_cmp(const ListCell *a, const ListCell *b); @@ -1122,6 +1132,13 @@ errbacktrace(void) * specifies how many inner frames to skip. Use this to avoid showing the * internal backtrace support functions in the backtrace. This requires that * this and related functions are not inlined. + * + * The implementation is, unsurprisingly, platform-specific: + * - GNU libc and copycats: Uses backtrace() and backtrace_symbols() + * - Windows: Uses CaptureStackBackTrace() with DbgHelp for symbol resolution + * (requires PDB files; falls back to exported functions/raw addresses if + * unavailable) + * - Others (musl libc): unsupported */ static void set_backtrace(ErrorData *edata, int num_skip) @@ -1132,12 +1149,12 @@ set_backtrace(ErrorData *edata, int num_skip) #ifdef HAVE_BACKTRACE_SYMBOLS { - void *buf[100]; + void *frames[100]; int nframes; char **strfrms; - nframes = backtrace(buf, lengthof(buf)); - strfrms = backtrace_symbols(buf, nframes); + nframes = backtrace(frames, lengthof(frames)); + strfrms = backtrace_symbols(frames, nframes); if (strfrms != NULL) { for (int i = num_skip; i < nframes; i++) @@ -1148,6 +1165,123 @@ set_backtrace(ErrorData *edata, int num_skip) appendStringInfoString(&errtrace, "insufficient memory for backtrace generation"); } +#elif defined(_MSC_VER) + { + void *frames[100]; + int nframes; + char buffer[sizeof(SYMBOL_INFOW) + MAX_SYM_NAME * sizeof(wchar_t)]; + PSYMBOL_INFOW psymbol; + + if (!backtrace_symbols_initialized) + { + backtrace_process = GetCurrentProcess(); + + SymSetOptions(SYMOPT_UNDNAME | + SYMOPT_DEFERRED_LOADS | + SYMOPT_LOAD_LINES | + SYMOPT_FAIL_CRITICAL_ERRORS); + + if (SymInitialize(backtrace_process, NULL, TRUE)) + { + backtrace_symbols_initialized = true; + on_proc_exit(backtrace_cleanup, 0); + } + else + { + appendStringInfo(&errtrace, + "could not initialize symbol handler: error code %lu", + GetLastError()); + edata->backtrace = errtrace.data; + return; + } + } + + nframes = CaptureStackBackTrace(num_skip, lengthof(frames), frames, NULL); + + if (nframes == 0) + { + appendStringInfoString(&errtrace, "zero stack frames captured"); + edata->backtrace = errtrace.data; + return; + } + + psymbol = (PSYMBOL_INFOW) buffer; + psymbol->MaxNameLen = MAX_SYM_NAME; + psymbol->SizeOfStruct = sizeof(SYMBOL_INFOW); + + for (int i = 0; i < nframes; i++) + { + DWORD64 address = (DWORD64) frames[i]; + DWORD64 displacement = 0; + BOOL sym_result; + + sym_result = SymFromAddrW(backtrace_process, + address, + &displacement, + psymbol); + if (sym_result) + { + char symbol_name[MAX_SYM_NAME]; + size_t result; + + /* + * Convert symbol name from UTF-16 to database encoding using + * wchar2char(), which handles both UTF-8 and non-UTF-8 + * databases correctly on Windows. + */ + result = wchar2char(symbol_name, (const wchar_t *) psymbol->Name, + sizeof(symbol_name), NULL); + + if (result == (size_t) -1) + { + /* Conversion failed, use address only */ + appendStringInfo(&errtrace, + "\n[0x%llx]", + (unsigned long long) address); + } + else + { + IMAGEHLP_LINEW64 line; + DWORD line_displacement = 0; + char filename[MAX_PATH]; + + line.SizeOfStruct = sizeof(IMAGEHLP_LINEW64); + + /* Start with the common part: symbol+offset [address] */ + appendStringInfo(&errtrace, + "\n%s+0x%llx [0x%llx]", + symbol_name, + (unsigned long long) displacement, + (unsigned long long) address); + + /* Try to append line info if available */ + if (SymGetLineFromAddrW64(backtrace_process, + address, + &line_displacement, + &line)) + { + result = wchar2char(filename, (const wchar_t *) line.FileName, + sizeof(filename), NULL); + + if (result != (size_t) -1) + { + appendStringInfo(&errtrace, + " [%s:%lu]", + filename, + (unsigned long) line.LineNumber); + } + } + } + } + else + { + appendStringInfo(&errtrace, + "\n[0x%llx] (symbol lookup failed: error code %lu)", + (unsigned long long) address, + GetLastError()); + } + } + } #else appendStringInfoString(&errtrace, "backtrace generation is not supported by this installation"); @@ -1156,6 +1290,19 @@ set_backtrace(ErrorData *edata, int num_skip) edata->backtrace = errtrace.data; } +/* + * Cleanup function for DbgHelp resources. + * Called via on_proc_exit() to release resources allocated by SymInitialize(). + */ +pg_attribute_unused() +static void +backtrace_cleanup(int code, Datum arg) +{ +#ifdef _MSC_VER + SymCleanup(backtrace_process); +#endif +} + /* * errmsg_internal --- add a primary error message text to the current error * -- 2.47.3 --h24v6pggzlszygs4--