PostgreSQL Source Code  git master
crashdump.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * crashdump.c
4  * Automatic crash dump creation for PostgreSQL on Windows
5  *
6  * The crashdump feature traps unhandled win32 exceptions produced by the
7  * backend, and tries to produce a Windows MiniDump crash
8  * dump for later debugging and analysis. The machine performing the dump
9  * doesn't need any special debugging tools; the user only needs to send
10  * the dump to somebody who has the same version of PostgreSQL and has debugging
11  * tools.
12  *
13  * crashdump module originally by Craig Ringer <ringerc@ringerc.id.au>
14  *
15  * LIMITATIONS
16  * ===========
17  * This *won't* work in hard OOM situations or stack overflows.
18  *
19  * For those, it'd be necessary to take a much more complicated approach where
20  * the handler switches to a new stack (if it can) and forks a helper process
21  * to debug it self.
22  *
23  * POSSIBLE FUTURE WORK
24  * ====================
25  * For bonus points, the crash dump format permits embedding of user-supplied
26  * data. If there's anything else that should always be supplied with a crash
27  * dump (postgresql.conf? Last few lines of a log file?), it could potentially
28  * be added, though at the cost of a greater chance of the crash dump failing.
29  *
30  *
31  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
32  *
33  * IDENTIFICATION
34  * src/backend/port/win32/crashdump.c
35  *
36  *-------------------------------------------------------------------------
37  */
38 
39 #include "postgres.h"
40 
41 /*
42  * Some versions of the MS SDK contain "typedef enum { ... } ;" which the MS
43  * compiler quite sanely complains about. Well done, Microsoft.
44  * This pragma disables the warning just while we include the header.
45  * The pragma is known to work with all (as at the time of writing) supported
46  * versions of MSVC.
47  */
48 #ifdef _MSC_VER
49 #pragma warning(push)
50 #pragma warning(disable : 4091)
51 #endif
52 #include <dbghelp.h>
53 #ifdef _MSC_VER
54 #pragma warning(pop)
55 #endif
56 
57 /*
58  * Much of the following code is based on CodeProject and MSDN examples,
59  * particularly
60  * http://www.codeproject.com/KB/debug/postmortemdebug_standalone1.aspx
61  *
62  * Useful MSDN articles:
63  *
64  * http://msdn.microsoft.com/en-us/library/ff805116(v=VS.85).aspx
65  * http://msdn.microsoft.com/en-us/library/ms679294(VS.85).aspx
66  *
67  * Other useful articles on working with minidumps:
68  * http://www.debuginfo.com/articles/effminidumps.html
69  */
70 
71 typedef BOOL (WINAPI * MINIDUMPWRITEDUMP) (HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
72  CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
73  CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
74  CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
75 );
76 
77 
78 /*
79  * This function is the exception handler passed to SetUnhandledExceptionFilter.
80  * It's invoked only if there's an unhandled exception. The handler will use
81  * dbghelp.dll to generate a crash dump, then resume the normal unhandled
82  * exception process, which will generally exit with an error message from
83  * the runtime.
84  *
85  * This function is run under the unhandled exception handler, effectively
86  * in a crash context, so it should be careful with memory and avoid using
87  * any PostgreSQL functions.
88  */
89 static LONG WINAPI
90 crashDumpHandler(struct _EXCEPTION_POINTERS *pExceptionInfo)
91 {
92  /*
93  * We only write crash dumps if the "crashdumps" directory within the
94  * postgres data directory exists.
95  */
96  DWORD attribs = GetFileAttributesA("crashdumps");
97 
98  if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY))
99  {
100  /* 'crashdumps' exists and is a directory. Try to write a dump' */
101  HMODULE hDll = NULL;
102  MINIDUMPWRITEDUMP pDump = NULL;
103  MINIDUMP_TYPE dumpType;
104  char dumpPath[_MAX_PATH];
105  HANDLE selfProcHandle = GetCurrentProcess();
106  DWORD selfPid = GetProcessId(selfProcHandle);
107  HANDLE dumpFile;
108  DWORD systemTicks;
109  struct _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
110 
111  ExInfo.ThreadId = GetCurrentThreadId();
112  ExInfo.ExceptionPointers = pExceptionInfo;
113  ExInfo.ClientPointers = FALSE;
114 
115  /* Load the dbghelp.dll library and functions */
116  hDll = LoadLibrary("dbghelp.dll");
117  if (hDll == NULL)
118  {
119  write_stderr("could not load dbghelp.dll, cannot write crash dump\n");
120  return EXCEPTION_CONTINUE_SEARCH;
121  }
122 
123  pDump = (MINIDUMPWRITEDUMP) (pg_funcptr_t) GetProcAddress(hDll, "MiniDumpWriteDump");
124 
125  if (pDump == NULL)
126  {
127  write_stderr("could not load required functions in dbghelp.dll, cannot write crash dump\n");
128  return EXCEPTION_CONTINUE_SEARCH;
129  }
130 
131  /*
132  * Dump as much as we can, except shared memory, code segments, and
133  * memory mapped files. Exactly what we can dump depends on the
134  * version of dbghelp.dll, see:
135  * http://msdn.microsoft.com/en-us/library/ms680519(v=VS.85).aspx
136  */
137  dumpType = MiniDumpNormal | MiniDumpWithHandleData |
138  MiniDumpWithDataSegs;
139 
140  if (GetProcAddress(hDll, "EnumDirTree") != NULL)
141  {
142  /* If this function exists, we have version 5.2 or newer */
143  dumpType |= MiniDumpWithIndirectlyReferencedMemory |
144  MiniDumpWithPrivateReadWriteMemory;
145  }
146 
147  systemTicks = GetTickCount();
148  snprintf(dumpPath, _MAX_PATH,
149  "crashdumps\\postgres-pid%0i-%0i.mdmp",
150  (int) selfPid, (int) systemTicks);
151  dumpPath[_MAX_PATH - 1] = '\0';
152 
153  dumpFile = CreateFile(dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE,
154  NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
155  NULL);
156  if (dumpFile == INVALID_HANDLE_VALUE)
157  {
158  write_stderr("could not open crash dump file \"%s\" for writing: error code %lu\n",
159  dumpPath, GetLastError());
160  return EXCEPTION_CONTINUE_SEARCH;
161  }
162 
163  if ((*pDump) (selfProcHandle, selfPid, dumpFile, dumpType, &ExInfo,
164  NULL, NULL))
165  write_stderr("wrote crash dump to file \"%s\"\n", dumpPath);
166  else
167  write_stderr("could not write crash dump to file \"%s\": error code %lu\n",
168  dumpPath, GetLastError());
169 
170  CloseHandle(dumpFile);
171  }
172 
173  return EXCEPTION_CONTINUE_SEARCH;
174 }
175 
176 
177 void
179 {
180  SetUnhandledExceptionFilter(crashDumpHandler);
181 }
#define write_stderr(str)
Definition: parallel.c:184
void(* pg_funcptr_t)(void)
Definition: c.h:375
static LONG WINAPI crashDumpHandler(struct _EXCEPTION_POINTERS *pExceptionInfo)
Definition: crashdump.c:90
BOOL(WINAPI * MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
Definition: crashdump.c:71
void pgwin32_install_crashdump_handler(void)
Definition: crashdump.c:178
static void dumpType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:10909
#define snprintf
Definition: port.h:238