summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2022-02-03 10:06:35 -0500
committerMarge Bot <ben+marge-bot@smart-cactus.org>2022-02-06 01:43:56 -0500
commitfc083b480adedf26d47f880402f111680ec34183 (patch)
tree223118d16bf7cf820bd379b1ef03abdf5c7d59b0
parente96f50beec172f5ff95769842cb9be724363311c (diff)
downloadhaskell-fc083b480adedf26d47f880402f111680ec34183.tar.gz
rts: Dump memory map on memory mapping failures
Fixes #20992.
-rw-r--r--rts/Linker.c3
-rw-r--r--rts/MemoryMap.c138
-rw-r--r--rts/MemoryMap.h13
-rw-r--r--rts/linker/M32Alloc.c3
-rw-r--r--rts/rts.cabal.in1
5 files changed, 158 insertions, 0 deletions
diff --git a/rts/Linker.c b/rts/Linker.c
index 6468e7818d..0b24a49979 100644
--- a/rts/Linker.c
+++ b/rts/Linker.c
@@ -33,6 +33,7 @@
#include "linker/SymbolExtras.h"
#include "PathUtils.h"
#include "CheckUnload.h" // createOCSectionIndices
+#include "MemoryMap.h"
#if !defined(mingw32_HOST_OS)
#include "posix/Signals.h"
@@ -1106,6 +1107,7 @@ mmap_again:
MAP_PRIVATE|tryMap32Bit|fixed|flags, fd, offset);
if (result == MAP_FAILED) {
+ reportMemoryMap();
sysErrorBelch("mmap %" FMT_Word " bytes at %p",(W_)size,map_addr);
errorBelch("Try specifying an address with +RTS -xm<addr> -RTS");
return NULL;
@@ -1128,6 +1130,7 @@ mmap_again:
fixed = MAP_FIXED;
goto mmap_again;
#else
+ reportMemoryMap();
errorBelch("mmapForLinker: failed to mmap() memory below 2Gb; "
"asked for %lu bytes at %p. "
"Try specifying an address with +RTS -xm<addr> -RTS",
diff --git a/rts/MemoryMap.c b/rts/MemoryMap.c
new file mode 100644
index 0000000000..67db8b4830
--- /dev/null
+++ b/rts/MemoryMap.c
@@ -0,0 +1,138 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team, 1998-2004
+ *
+ * Memory-map dumping.
+ *
+ * This is intended to be used for reporting the process memory-map
+ * in diagnostics when the RTS fails to map a block of memory.
+ *
+ * ---------------------------------------------------------------------------*/
+
+#include "rts/PosixSource.h"
+#include "Rts.h"
+
+#include <string.h>
+
+#if defined(darwin_HOST_OS)
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <mach/vm_region.h>
+#include <mach/vm_statistics.h>
+#endif
+
+#include "MemoryMap.h"
+
+#if defined(mingw32_HOST_OS)
+
+void reportMemoryMap() {
+ debugBelch("\nMemory map:\n");
+ uint8_t *addr = NULL;
+ while (true) {
+ MEMORY_BASIC_INFORMATION info;
+ int res = VirtualQuery(addr, &info, sizeof(info));
+ if (!res && GetLastError() == ERROR_INVALID_PARAMETER) {
+ return;
+ } else if (!res) {
+ sysErrorBelch("VirtualQuery failed");
+ return;
+ }
+
+ if (info.State & MEM_FREE) {
+ // free range
+ } else {
+ const char *protection;
+ switch (info.Protect) {
+ case PAGE_EXECUTE: protection = "--x"; break;
+ case PAGE_EXECUTE_READ: protection = "r-x"; break;
+ case PAGE_EXECUTE_READWRITE: protection = "rwx"; break;
+ case PAGE_EXECUTE_WRITECOPY: protection = "rcx"; break;
+ case PAGE_NOACCESS: protection = "---"; break;
+ case PAGE_READONLY: protection = "r--"; break;
+ case PAGE_READWRITE: protection = "rw-"; break;
+ case PAGE_WRITECOPY: protection = "rc-"; break;
+ default: protection = "???"; break;
+ }
+
+ const char *type;
+ switch (info.Type) {
+ case MEM_IMAGE: type = "image"; break;
+ case MEM_MAPPED: type = "mapped"; break;
+ case MEM_PRIVATE: type = "private"; break;
+ default: type = "unknown"; break;
+ }
+
+ debugBelch("%08llx-%08llx %8zuK %3s (%s)\n",
+ (uintptr_t) info.BaseAddress,
+ (uintptr_t) info.BaseAddress + info.RegionSize,
+ (size_t) info.RegionSize,
+ protection, type);
+ }
+ addr = (uint8_t *) info.BaseAddress + info.RegionSize;
+ }
+}
+
+#elif defined(darwin_HOST_OS)
+
+void reportMemoryMap() {
+ // Inspired by MacFUSE /proc implementation
+ debugBelch("\nMemory map:\n");
+ while (true) {
+ vm_size_t vmsize;
+ vm_address_t address;
+ vm_region_basic_info_data_t info;
+ vm_region_flavor_t flavor = VM_REGION_BASIC_INFO;
+ memory_object_name_t object;
+ mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
+ kern_return_t kr =
+ mach_vm_region(mach_task_self(), &address, &vmsize, flavor,
+ (vm_region_info_t)&info, &info_count, &object);
+ if (kr == KERN_SUCCESS) {
+ debugBelch("%08lx-%08lx %8zuK %c%c%c/%c%c%c\n",
+ address, (address + vmsize), (vmsize >> 10),
+ (info.protection & VM_PROT_READ) ? 'r' : '-',
+ (info.protection & VM_PROT_WRITE) ? 'w' : '-',
+ (info.protection & VM_PROT_EXECUTE) ? 'x' : '-',
+ (info.max_protection & VM_PROT_READ) ? 'r' : '-',
+ (info.max_protection & VM_PROT_WRITE) ? 'w' : '-',
+ (info.max_protection & VM_PROT_EXECUTE) ? 'x' : '-');
+ address += vmsize;
+ } else if (kr == KERN_INVALID_ADDRESS) {
+ // We presumably reached the end of address space
+ break;
+ } else {
+ debugBelch(" Error: %s\n", mach_error_string(kr));
+ break;
+ }
+ }
+}
+
+#else
+
+// Linux et al.
+void reportMemoryMap() {
+ debugBelch("\nMemory map:\n");
+ FILE *f = fopen("/proc/self/maps", "r");
+ if (f == NULL) {
+ debugBelch(" Could not open /proc/self/maps\n");
+ return;
+ }
+
+ while (true) {
+ char buf[256];
+ size_t n = fread(buf, 1, sizeof(buf)-1, f);
+ if (n <= 0) {
+ debugBelch(" Error: %s\n", strerror(errno));
+ break;
+ }
+ buf[n] = '\0';
+ debugBelch("%s", buf);
+ if (n < sizeof(buf)-1) {
+ break;
+ }
+ }
+ debugBelch("\n");
+ fclose(f);
+}
+
+#endif
diff --git a/rts/MemoryMap.h b/rts/MemoryMap.h
new file mode 100644
index 0000000000..7d2c4a58b1
--- /dev/null
+++ b/rts/MemoryMap.h
@@ -0,0 +1,13 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team, 1998-2004
+ *
+ * Memory-map dumping.
+ *
+ * This is intended to be used for reporting the process memory-map
+ * in diagnostics when the RTS fails to map a block of memory.
+ *
+ * ---------------------------------------------------------------------------*/
+
+void reportMemoryMap(void);
+
diff --git a/rts/linker/M32Alloc.c b/rts/linker/M32Alloc.c
index c81cac919c..c09662ed24 100644
--- a/rts/linker/M32Alloc.c
+++ b/rts/linker/M32Alloc.c
@@ -11,6 +11,7 @@
#include "RtsUtils.h"
#include "linker/M32Alloc.h"
#include "LinkerInternals.h"
+#include "MemoryMap.h"
#include <inttypes.h>
#include <stdlib.h>
@@ -325,6 +326,7 @@ m32_alloc_page(void)
const size_t map_sz = pgsz * M32_MAP_PAGES;
uint8_t *chunk = mmapAnonForLinker(map_sz);
if (! is_okay_address(chunk + map_sz)) {
+ reportMemoryMap();
barf("m32_alloc_page: failed to allocate pages within 4GB of program text (got %p)", chunk);
}
IF_DEBUG(sanity, memset(chunk, 0xaa, map_sz));
@@ -477,6 +479,7 @@ m32_alloc(struct m32_allocator_t *alloc, size_t size, size_t alignment)
sysErrorBelch("m32_alloc: Failed to map pages for %zd bytes", size);
return NULL;
} else if (! is_okay_address(page)) {
+ reportMemoryMap();
barf("m32_alloc: warning: Allocation of %zd bytes resulted in pages above 4GB (%p)",
size, page);
}
diff --git a/rts/rts.cabal.in b/rts/rts.cabal.in
index a2449a5ce5..986d6bf6e0 100644
--- a/rts/rts.cabal.in
+++ b/rts/rts.cabal.in
@@ -493,6 +493,7 @@ library
Libdw.c
LibdwPool.c
Linker.c
+ MemoryMap.c
Messages.c
OldARMAtomic.c
PathUtils.c