mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Dump full memory maps around failing Windows reattach code.
This morning's results from buildfarm member dory make it pretty clear that something is getting mapped into the just-freed space, but not what that something is. Replace my minimalistic probes with a full dump of the process address space and module space, based on Noah's work at <20170403065106.GA2624300%40tornado.leadboat.com> This is all (probably) to get reverted once we have fixed the problem, but for now we need information. Discussion: https://postgr.es/m/25495.1524517820@sss.pgh.pa.us
This commit is contained in:
		@@ -79,7 +79,7 @@ libpostgres.a: postgres
 | 
				
			|||||||
endif # cygwin
 | 
					endif # cygwin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ifeq ($(PORTNAME), win32)
 | 
					ifeq ($(PORTNAME), win32)
 | 
				
			||||||
LIBS += -lsecur32
 | 
					LIBS += -lsecur32 -lpsapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
postgres: $(OBJS) $(WIN32RES)
 | 
					postgres: $(OBJS) $(WIN32RES)
 | 
				
			||||||
	$(CC) $(CFLAGS) $(LDFLAGS) $(LDFLAGS_EX) -Wl,--stack=$(WIN32_STACK_RLIMIT) -Wl,--export-all-symbols -Wl,--out-implib=libpostgres.a $(call expand_subsys,$(OBJS)) $(WIN32RES) $(LIBS) -o $@$(X)
 | 
						$(CC) $(CFLAGS) $(LDFLAGS) $(LDFLAGS_EX) -Wl,--stack=$(WIN32_STACK_RLIMIT) -Wl,--export-all-symbols -Wl,--out-implib=libpostgres.a $(call expand_subsys,$(OBJS)) $(WIN32RES) $(LIBS) -o $@$(X)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,8 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
#include "postgres.h"
 | 
					#include "postgres.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <psapi.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "miscadmin.h"
 | 
					#include "miscadmin.h"
 | 
				
			||||||
#include "storage/dsm.h"
 | 
					#include "storage/dsm.h"
 | 
				
			||||||
#include "storage/ipc.h"
 | 
					#include "storage/ipc.h"
 | 
				
			||||||
@@ -24,6 +26,88 @@ static Size UsedShmemSegSize = 0;
 | 
				
			|||||||
static bool EnableLockPagesPrivilege(int elevel);
 | 
					static bool EnableLockPagesPrivilege(int elevel);
 | 
				
			||||||
static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
 | 
					static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Dump all modules loaded into proc */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					dumpdlls(HANDLE proc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						HMODULE		dll[1024];
 | 
				
			||||||
 | 
						DWORD		size_used = 1;
 | 
				
			||||||
 | 
						int			i,
 | 
				
			||||||
 | 
									n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!EnumProcessModules(proc, dll, sizeof(dll), &size_used))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							elog(LOG, "EnumProcessModules failed: %lu", GetLastError());
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						n = (int) (size_used / sizeof(*dll));
 | 
				
			||||||
 | 
						elog(LOG, "EnumProcessModules: %d modules in process 0x%p", n, proc);
 | 
				
			||||||
 | 
						for (i = 0; i < n; i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							char		name[MAXPGPATH];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!GetModuleFileNameEx(proc, dll[i], name, sizeof(name)))
 | 
				
			||||||
 | 
								sprintf(name, "GetModuleFileNameEx failed: %lu", GetLastError());
 | 
				
			||||||
 | 
							elog(LOG, "%d: 0x%p %s", i + 1, dll[i], name);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *
 | 
				
			||||||
 | 
					mi_type(DWORD code)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (code)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							case MEM_IMAGE:
 | 
				
			||||||
 | 
								return "img";
 | 
				
			||||||
 | 
							case MEM_MAPPED:
 | 
				
			||||||
 | 
								return "map";
 | 
				
			||||||
 | 
							case MEM_PRIVATE:
 | 
				
			||||||
 | 
								return "prv";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "???";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *
 | 
				
			||||||
 | 
					mi_state(DWORD code)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (code)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							case MEM_COMMIT:
 | 
				
			||||||
 | 
								return "commit";
 | 
				
			||||||
 | 
							case MEM_FREE:
 | 
				
			||||||
 | 
								return "free  ";
 | 
				
			||||||
 | 
							case MEM_RESERVE:
 | 
				
			||||||
 | 
								return "reserv";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "???";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					dumpmem(const char *reason, HANDLE proc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char	   *addr = 0;
 | 
				
			||||||
 | 
						MEMORY_BASIC_INFORMATION mi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						elog(LOG, "%s memory map", reason);
 | 
				
			||||||
 | 
						do
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							memset(&mi, 0, sizeof(mi));
 | 
				
			||||||
 | 
							if (!VirtualQueryEx(proc, addr, &mi, sizeof(mi)))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (GetLastError() == ERROR_INVALID_PARAMETER)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								elog(LOG, "VirtualQueryEx failed: %lu", GetLastError());
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							elog(LOG, "0x%p+0x%p %s (alloc 0x%p) %s",
 | 
				
			||||||
 | 
								 mi.BaseAddress, (void *) mi.RegionSize,
 | 
				
			||||||
 | 
								 mi_type(mi.Type), mi.AllocationBase, mi_state(mi.State));
 | 
				
			||||||
 | 
							addr += mi.RegionSize;
 | 
				
			||||||
 | 
						} while (addr > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dumpdlls(proc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Generate shared memory segment name. Expand the data directory, to generate
 | 
					 * Generate shared memory segment name. Expand the data directory, to generate
 | 
				
			||||||
 * an identifier unique for this data directory. Then replace all backslashes
 | 
					 * an identifier unique for this data directory. Then replace all backslashes
 | 
				
			||||||
@@ -388,19 +472,11 @@ PGSharedMemoryReAttach(void)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	PGShmemHeader *hdr;
 | 
						PGShmemHeader *hdr;
 | 
				
			||||||
	void	   *origUsedShmemSegAddr = UsedShmemSegAddr;
 | 
						void	   *origUsedShmemSegAddr = UsedShmemSegAddr;
 | 
				
			||||||
	MEMORY_BASIC_INFORMATION previnfo;
 | 
					 | 
				
			||||||
	MEMORY_BASIC_INFORMATION afterinfo;
 | 
					 | 
				
			||||||
	DWORD		preverr;
 | 
					 | 
				
			||||||
	DWORD		aftererr;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Assert(UsedShmemSegAddr != NULL);
 | 
						Assert(UsedShmemSegAddr != NULL);
 | 
				
			||||||
	Assert(IsUnderPostmaster);
 | 
						Assert(IsUnderPostmaster);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Preliminary probe of region we intend to release */
 | 
						dumpmem("before VirtualFree", GetCurrentProcess());
 | 
				
			||||||
	if (VirtualQuery(UsedShmemSegAddr, &previnfo, sizeof(previnfo)) != 0)
 | 
					 | 
				
			||||||
		preverr = 0;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		preverr = GetLastError();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Release memory region reservation that was made by the postmaster
 | 
						 * Release memory region reservation that was made by the postmaster
 | 
				
			||||||
@@ -409,48 +485,14 @@ PGSharedMemoryReAttach(void)
 | 
				
			|||||||
		elog(FATAL, "failed to release reserved memory region (addr=%p): error code %lu",
 | 
							elog(FATAL, "failed to release reserved memory region (addr=%p): error code %lu",
 | 
				
			||||||
			 UsedShmemSegAddr, GetLastError());
 | 
								 UsedShmemSegAddr, GetLastError());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Verify post-release state */
 | 
						dumpmem("after VirtualFree", GetCurrentProcess());
 | 
				
			||||||
	if (VirtualQuery(UsedShmemSegAddr, &afterinfo, sizeof(afterinfo)) != 0)
 | 
					 | 
				
			||||||
		aftererr = 0;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		aftererr = GetLastError();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hdr = (PGShmemHeader *) MapViewOfFileEx(UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
 | 
						hdr = (PGShmemHeader *) MapViewOfFileEx(UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
 | 
				
			||||||
	if (!hdr)
 | 
						if (!hdr)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		DWORD		maperr = GetLastError();
 | 
							DWORD		maperr = GetLastError();
 | 
				
			||||||
		MEMORY_BASIC_INFORMATION postinfo;
 | 
					 | 
				
			||||||
		DWORD		posterr;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Capture post-failure state */
 | 
							dumpmem("after MapViewOfFileEx", GetCurrentProcess());
 | 
				
			||||||
		if (VirtualQuery(UsedShmemSegAddr, &postinfo, sizeof(postinfo)) != 0)
 | 
					 | 
				
			||||||
			posterr = 0;
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			posterr = GetLastError();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (preverr == 0)
 | 
					 | 
				
			||||||
			elog(LOG, "VirtualQuery(%p) before free reports region of size %zu, base %p, has state 0x%lx",
 | 
					 | 
				
			||||||
				 UsedShmemSegAddr, previnfo.RegionSize,
 | 
					 | 
				
			||||||
				 previnfo.AllocationBase, previnfo.State);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			elog(LOG, "VirtualQuery(%p) before free failed: error code %lu",
 | 
					 | 
				
			||||||
				 UsedShmemSegAddr, preverr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (aftererr == 0)
 | 
					 | 
				
			||||||
			elog(LOG, "VirtualQuery(%p) after free reports region of size %zu, base %p, has state 0x%lx",
 | 
					 | 
				
			||||||
				 UsedShmemSegAddr, afterinfo.RegionSize,
 | 
					 | 
				
			||||||
				 afterinfo.AllocationBase, afterinfo.State);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			elog(LOG, "VirtualQuery(%p) after free failed: error code %lu",
 | 
					 | 
				
			||||||
				 UsedShmemSegAddr, aftererr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (posterr == 0)
 | 
					 | 
				
			||||||
			elog(LOG, "VirtualQuery(%p) after map reports region of size %zu, base %p, has state 0x%lx",
 | 
					 | 
				
			||||||
				 UsedShmemSegAddr, postinfo.RegionSize,
 | 
					 | 
				
			||||||
				 postinfo.AllocationBase, postinfo.State);
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			elog(LOG, "VirtualQuery(%p) after map failed: error code %lu",
 | 
					 | 
				
			||||||
				 UsedShmemSegAddr, posterr);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		elog(FATAL, "could not reattach to shared memory (key=%p, addr=%p): error code %lu",
 | 
							elog(FATAL, "could not reattach to shared memory (key=%p, addr=%p): error code %lu",
 | 
				
			||||||
			 UsedShmemSegID, UsedShmemSegAddr, maperr);
 | 
								 UsedShmemSegID, UsedShmemSegAddr, maperr);
 | 
				
			||||||
@@ -597,5 +639,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
 | 
				
			|||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dumpmem("after reserve", hChild);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
# src/makefiles/Makefile.win32
 | 
					# src/makefiles/Makefile.win32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					override CPPFLAGS+= -DPSAPI_VERSION=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ifdef PGXS
 | 
					ifdef PGXS
 | 
				
			||||||
BE_DLLLIBS= -L$(libdir) -lpostgres
 | 
					BE_DLLLIBS= -L$(libdir) -lpostgres
 | 
				
			||||||
override CPPFLAGS+= -I$(includedir_server)/port/win32
 | 
					override CPPFLAGS+= -I$(includedir_server)/port/win32
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -174,8 +174,10 @@ sub mkvcbuild
 | 
				
			|||||||
		'repl_gram.y',             'syncrep_scanner.l',
 | 
							'repl_gram.y',             'syncrep_scanner.l',
 | 
				
			||||||
		'syncrep_gram.y');
 | 
							'syncrep_gram.y');
 | 
				
			||||||
	$postgres->AddDefine('BUILDING_DLL');
 | 
						$postgres->AddDefine('BUILDING_DLL');
 | 
				
			||||||
 | 
						$postgres->AddDefine('PSAPI_VERSION=1');
 | 
				
			||||||
	$postgres->AddLibrary('secur32.lib');
 | 
						$postgres->AddLibrary('secur32.lib');
 | 
				
			||||||
	$postgres->AddLibrary('ws2_32.lib');
 | 
						$postgres->AddLibrary('ws2_32.lib');
 | 
				
			||||||
 | 
						$postgres->AddLibrary('psapi.lib');
 | 
				
			||||||
	$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 | 
						$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 | 
				
			||||||
	$postgres->FullExportDLL('postgres.lib');
 | 
						$postgres->FullExportDLL('postgres.lib');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user