for review: split mercury_memory.{c,h}
Tyson Dowd
trd at cs.mu.OZ.AU
Thu May 7 17:04:46 AEST 1998
Estimated hours taken: 5
Split mercury_memory.{c,h} into several files.
runtime/Mmakefile:
Add new files.
runtime/mercury_context.c:
Add a #include "mercury_memory_handlers.h" as this module
creates new zones using default_handler.
runtime/mercury_memory.c:
runtime/mercury_memory.h:
Other code (mostly code to set up the actual memory
zones we use in the Mercury runtime).
runtime/mercury_memory_handlers.c:
runtime/mercury_memory_handlers.h:
Signal handlers for memory access violations.
runtime/mercury_memory_zones.c:
runtime/mercury_memory_zones.h:
The implementation MemoryZone data type.
Index: runtime/Mmakefile
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/Mmakefile,v
retrieving revision 1.25
diff -u -r1.25 Mmakefile
--- Mmakefile 1998/03/30 13:03:13 1.25
+++ Mmakefile 1998/04/28 06:35:45
@@ -42,8 +42,9 @@
mercury_imp.h \
mercury_init.h \
mercury_label.h \
- mercury_stack_layout.h \
mercury_memory.h \
+ mercury_memory_zones.h \
+ mercury_memory_handlers.h \
mercury_misc.h \
mercury_overflow.h \
mercury_prof.h \
@@ -53,6 +54,7 @@
mercury_spinlock.h \
mercury_std.h \
mercury_stacks.h \
+ mercury_stack_layout.h \
mercury_stack_trace.h \
mercury_string.h \
mercury_table.h \
@@ -91,6 +93,8 @@
mercury_ho_call.c \
mercury_label.c \
mercury_memory.c \
+ mercury_memory_zones.c \
+ mercury_memory_handlers.c \
mercury_misc.c \
mercury_prof.c \
mercury_prof_mem.c \
Index: runtime/mercury_context.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/mercury_context.c,v
retrieving revision 1.1
diff -u -r1.1 mercury_context.c
--- mercury_context.c 1997/11/20 01:59:46 1.1
+++ mercury_context.c 1998/04/28 07:23:15
@@ -18,6 +18,7 @@
#include <signal.h>
#endif
+#include "mercury_memory_handlers.h"
#include "mercury_context.h"
#include "mercury_engine.h" /* for `memdebug' */
Index: runtime/mercury_memory.c
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/mercury_memory.c,v
retrieving revision 1.6
diff -u -r1.6 mercury_memory.c
--- mercury_memory.c 1998/04/08 11:33:56 1.6
+++ mercury_memory.c 1998/04/29 06:02:43
@@ -25,6 +25,11 @@
** If the operating system of the machine supports the mprotect syscall,
** we also protect a chunk at the end of each area against access,
** thus detecting area overflow.
+**
+** The code for handling the allocation and management of different
+** memory zones is in mercury_memory_zones.{c,h}.
+** The code for handling overflows and memory access errors in general
+** is in mercury_memory_handlers.{c,h}.
*/
/*---------------------------------------------------------------------------*/
@@ -81,6 +86,7 @@
#include "mercury_imp.h"
#include "mercury_trace.h"
+#include "mercury_memory_handlers.h"
/*---------------------------------------------------------------------------*/
@@ -90,71 +96,8 @@
#define getpagesize() 8192
#endif
-#ifdef CONSERVATIVE_GC
- #define memalign(a,s) GC_MALLOC_UNCOLLECTABLE(s)
-#elif defined(HAVE_MEMALIGN)
- extern void *memalign(size_t, size_t);
-#else
- #define memalign(a,s) malloc(s)
-#endif
-
-/*
-** DESCRIPTION
-** The function mprotect() changes the access protections on
-** the mappings specified by the range [addr, addr + len) to be
-** that specified by prot. Legitimate values for prot are the
-** same as those permitted for mmap and are defined in
-** <sys/mman.h> as:
-**
-** PROT_READ page can be read
-** PROT_WRITE page can be written
-** PROT_EXEC page can be executed
-** PROT_NONE page can not be accessed
-*/
-#ifdef HAVE_MPROTECT
-
- #ifdef CONSERVATIVE_GC
- /*
- ** The conservative garbage collectors scans through
- ** all these areas, so we need to allow reads.
- ** XXX This probably causes efficiency problems:
- ** too much memory for the GC to scan, and it probably
- ** all gets paged in.
- */
- #define MY_PROT PROT_READ
- #else
- #define MY_PROT PROT_NONE
- #endif
-
- /* The BSDI BSD/386 1.1 headers don't define PROT_NONE */
- #ifndef PROT_NONE
- #define PROT_NONE 0
- #endif
-
-#endif /* HAVE_MPROTECT */
-
/*---------------------------------------------------------------------------*/
-#ifdef HAVE_SIGINFO
- #if defined(HAVE_SIGCONTEXT_STRUCT)
- static void complex_sighandler(int, struct sigcontext_struct);
- #elif defined(HAVE_SIGINFO_T)
- static void complex_bushandler(int, siginfo_t *, void *);
- static void complex_segvhandler(int, siginfo_t *, void *);
- #else
- #error "HAVE_SIGINFO defined but don't know how to get it"
- #endif
-#else
- static void simple_sighandler(int);
-#endif
-
-/*
-** round_up(amount, align) returns `amount' rounded up to the nearest
-** alignment boundary. `align' must be a power of 2.
-*/
-
-#define round_up(amount, align) ((((amount) - 1) | ((align) - 1)) + 1)
-
static void setup_mprotect(void);
#ifdef HAVE_SIGINFO
@@ -162,19 +105,6 @@
static char *explain_context(void *context);
#endif /* HAVE_SIGINFO */
-static void setup_signal(void);
-
-Word fake_reg[MAX_FAKE_REG];
-
-Word virtual_reg_map[MAX_REAL_REG] = VIRTUAL_REG_MAP_BODY;
-
-unsigned long num_uses[MAX_RN];
-
-MemoryZone *zone_table;
-
-MemoryZone *used_memory_zones;
-MemoryZone *free_memory_zones;
-
MemoryZone *detstack_zone;
MemoryZone *nondetstack_zone;
#ifndef CONSERVATIVE_GC
@@ -186,29 +116,8 @@
int dumpindex;
#endif
-static size_t unit;
-static size_t page_size;
-
-static MemoryZone *get_zone(void);
-static void unget_zone(MemoryZone *zone);
-
- /*
- ** We manage the handing out of offets through the cache by
- ** computing the offsets once and storing them in an array
- ** (in shared memory if necessary). We then maintain a global
- ** counter used to index the array which we increment (modulo
- ** the size of the array) after handing out each offset.
- */
-
-#define CACHE_SLICES 8
-
-static size_t *offset_vector;
-static int *offset_counter;
-static SpinLock *offset_lock;
-size_t next_offset(void);
-
-static void init_memory_arena(void);
-static void init_zones(void);
+size_t unit;
+size_t page_size;
void
init_memory(void)
@@ -281,128 +190,6 @@
if (memdebug) debug_memory();
} /* end init_memory() */
-void
-debug_memory(void)
-{
- MemoryZone *zone;
-
- fprintf(stderr, "\n");
- fprintf(stderr, "pcache_size = %lu (0x%lx)\n",
- (unsigned long) pcache_size, (unsigned long) pcache_size);
- fprintf(stderr, "page_size = %lu (0x%lx)\n",
- (unsigned long) page_size, (unsigned long) page_size);
- fprintf(stderr, "unit = %lu (0x%lx)\n",
- (unsigned long) unit, (unsigned long) unit);
-
- fprintf(stderr, "\n");
- fprintf(stderr, "fake_reg = %p (offset %ld)\n",
- (void *) fake_reg, (long) fake_reg & (unit-1));
- fprintf(stderr, "\n");
-
- for (zone = used_memory_zones; zone; zone = zone->next)
- {
- fprintf(stderr, "%-16s#%d-base = %p\n",
- zone->name, zone->id, (void *) zone->bottom);
- fprintf(stderr, "%-16s#%d-min = %p\n",
- zone->name, zone->id, (void *) zone->min);
- fprintf(stderr, "%-16s#%d-top = %p\n",
- zone->name, zone->id, (void *) zone->top);
-#ifdef HAVE_MPROTECT
- #ifdef HAVE_SIGINFO
- fprintf(stderr, "%-16s#%d-redzone = %p\n",
- zone->name, zone->id, (void *) zone->redzone);
- #endif /* HAVE_SIGINFO */
- fprintf(stderr, "%-16s#%d-hardmax = %p\n",
- zone->name, zone->id, (void *) zone->hardmax);
- fprintf(stderr, "%-16s#%d-size = %lu\n",
- zone->name, zone->id, (unsigned long)
- ((char *)zone->hardmax - (char *)zone->min));
-#else
- fprintf(stderr, "%-16s#%d-size = %lu\n",
- zone->name, zone->id, (unsigned long)
- ((char *)zone->top - (char *)zone->min));
-#endif /* HAVE_MPROTECT */
- fprintf(stderr, "\n");
- }
-}
-
-/*
-** init_memory_arena() allocates (if necessary) the top-level memory pool
-** from which all allocations should come. If PARALLEL is defined, then
-** this pool should be shared memory. In the absence of PARALLEL, it
-** doesn't need to do anything, since with CONSERVATIVE_GC, the collector
-** manages the heap, and without GC, we can allocate memory using memalign
-** or malloc.
-*/
-static void
-init_memory_arena()
-{
-#ifdef PARALLEL
- #ifndef CONSERVATIVE_GC
- if (numprocs > 1) {
- fatal_error("shared memory not implemented");
- }
- #else
- if (numprocs > 1) {
- fatal_error("shared memory not implemented with conservative gc");
- }
- #endif
-#endif
-}
-
-static void
-init_zones()
-{
- int i;
- size_t fake_reg_offset;
-
- /*
- ** Allocate the MemoryZone table.
- */
- zone_table = allocate_array(MemoryZone, MAX_ZONES);
-
- /*
- ** Initialize the MemoryZone table.
- */
- used_memory_zones = NULL;
- free_memory_zones = zone_table;
- for(i = 0; i < MAX_ZONES; i++) {
- zone_table[i].name = "unused";
- zone_table[i].id = i;
- zone_table[i].bottom = NULL;
- zone_table[i].top = NULL;
- zone_table[i].min = NULL;
-#ifdef MR_LOWLEVEL_DEBUG
- zone_table[i].max = NULL;
-#endif
-#ifdef HAVE_MPROTECT
- #ifdef HAVE_SIGINFO
- zone_table[i].redzone = NULL;
- #endif
- zone_table[i].hardmax = NULL;
-#endif
- if (i+1 < MAX_ZONES) {
- zone_table[i].next = &(zone_table[i+1]);
- } else {
- zone_table[i].next = NULL;
- }
- }
-
- offset_counter = allocate_object(int);
- *offset_counter = 0;
-
- offset_vector = allocate_array(size_t, CACHE_SLICES - 1);
-
- fake_reg_offset = (Unsigned) fake_reg % pcache_size;
-
- for (i = 0; i < CACHE_SLICES - 1; i++) {
- offset_vector[i] =
- (fake_reg_offset + pcache_size / CACHE_SLICES)
- % pcache_size;
- }
-} /* end init_zones() */
-
-
void
init_heap(void)
{
@@ -436,718 +223,6 @@
#endif
} /* end init_heap() */
-MemoryZone *
-get_zone(void)
-{
- MemoryZone *zone;
-
- /*
- ** unlink the first zone on the free-list,
- ** link it onto the used-list and return it.
- */
- zone = free_memory_zones;
- if (zone == NULL) {
- fatal_error("no more memory zones");
- }
- free_memory_zones = free_memory_zones->next;
-
- zone->next = used_memory_zones;
- used_memory_zones = zone;
-
- return zone;
-}
-
-void
-unget_zone(MemoryZone *zone)
-{
- MemoryZone *prev, *tmp;
-
- /*
- ** Find the zone on the used list, and unlink it from
- ** the list, then link it onto the start of the free-list.
- */
- for(prev = NULL, tmp = used_memory_zones;
- tmp && tmp != zone; prev = tmp, tmp = tmp->next)
- {
- /* VOID */
- }
- if (tmp == NULL) {
- fatal_error("memory zone not found!");
- }
- if (prev == NULL) {
- used_memory_zones = used_memory_zones->next;
- } else {
- prev->next = tmp->next;
- }
-
- zone->next = free_memory_zones;
- free_memory_zones = zone;
-}
-
-/*
-** successive calls to next_offset return offsets modulo the primary
-** cache size (carefully avoiding ever giving an offset that clashes
-** with fake_reg_array). This is used to give different memory zones
-** different starting points across the caches so that it is better
-** utilized.
-** An alternative implementation would be to increment the offset by
-** a fixed amount (eg 2Kb) so that as primary caches get bigger, we
-** allocate more offsets across them.
-*/
-size_t
-next_offset(void)
-{
- size_t offset;
-
- get_lock(offset_lock);
-
- offset = offset_vector[*offset_counter];
-
- *offset_counter = (*offset_counter + 1) % (CACHE_SLICES - 1);
-
- release_lock(offset_lock);
-
- return offset;
-}
-
-MemoryZone *
-create_zone(const char *name, int id, size_t size,
- size_t offset, size_t redsize,
- bool ((*handler)(Word *addr, MemoryZone *zone, void *context)))
-{
- Word *base;
- size_t total_size;
-
- /*
- ** total allocation is:
- ** unit (roundup to page boundary)
- ** size (including redzone)
- ** unit (an extra page for protection if
- ** mprotect is being used)
- */
-#ifdef HAVE_MPROTECT
- total_size = size + 2 * unit;
-#else
- total_size = size + unit;
-#endif
-
-#ifdef PARALLEL
- if (numprocs > 1) {
- fatal_error("shared memory not supported yet");
- }
-#endif
- base = memalign(unit, total_size);
- if (base == NULL) {
- char buf[2560];
- sprintf(buf, "unable allocate memory zone: %s#%d", name, id);
- fatal_error(buf);
- }
-
- return construct_zone(name, id, base, size, offset, redsize, handler);
-} /* end create_zone() */
-
-MemoryZone *
-construct_zone(const char *name, int id, Word *base,
- size_t size, size_t offset, size_t redsize,
- ZoneHandler handler)
-{
- MemoryZone *zone;
- size_t total_size;
-
- if (base == NULL) {
- fatal_error("construct_zone called with NULL pointer");
- }
-
- zone = get_zone();
-
- zone->name = name;
- zone->id = id;
-
-#if defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
- zone->handler = handler;
-#endif
-
- zone->bottom = base;
-
-#ifdef HAVE_MPROTECT
- total_size = size + unit;
-#else
- total_size = size;
-#endif
-
- zone->top = (Word *) ((char *)base+total_size);
- zone->min = (Word *) ((char *)base+offset);
-#ifdef MR_LOWLEVEL_DEBUG
- zone->max = zone->min;
-#endif
-
- /*
- ** setup the redzone+hardzone
- */
-#ifdef HAVE_MPROTECT
- #ifdef HAVE_SIGINFO
- zone->redzone_base = zone->redzone = (Word *)
- round_up((Unsigned)base + size - redsize, unit);
- if (mprotect((char *)zone->redzone, redsize + unit, MY_PROT) < 0) {
- char buf[2560];
- sprintf(buf, "unable to set %s#%d redzone\n"
- "base=%p, redzone=%p",
- zone->name, zone->id, zone->bottom, zone->redzone);
- fatal_error(buf);
- }
- #else /* not HAVE_SIGINFO */
- zone->hardmax = (Word *) ((char *)zone->top-unit);
- if (mprotect((char *)zone->hardmax, unit, MY_PROT) < 0) {
- char buf[2560];
- sprintf(buf, "unable to set %s#%d hardmax\n"
- "base=%p, hardmax=%p",
- zone->name, zone->id, zone->bottom, zone->hardmax);
- fatal_error(buf);
- }
- #endif /* not HAVE_SIGINFO */
-#endif /* not HAVE_MPROTECT */
-
- return zone;
-} /* end construct_zone() */
-
-void
-reset_zone(MemoryZone *zone)
-{
-#if defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
- zone->redzone = zone->redzone_base;
-
- if (mprotect((char *)zone->redzone,
- ((char *)zone->top) - ((char *) zone->redzone), MY_PROT) < 0)
- {
- char buf[2560];
- sprintf(buf, "unable to reset %s#%d redzone\n"
- "base=%p, redzone=%p",
- zone->name, zone->id, zone->bottom, zone->redzone);
- fatal_error(buf);
- }
-#endif
-}
-
-#define STDERR 2
-
-#ifndef MR_LOWLEVEL_DEBUG
-
-static void
-print_dump_stack(void)
-{
- const char *msg =
- "You can get a stack dump by using `--low-level-debug'\n";
- write(STDERR, msg, strlen(msg));
-}
-
-#else /* MR_LOWLEVEL_DEBUG */
-
-static void
-print_dump_stack(void)
-{
- int i;
- int start;
- int count;
- char buf[2560];
-
- strcpy(buf, "A dump of the det stack follows\n\n");
- write(STDERR, buf, strlen(buf));
-
- i = 0;
- while (i < dumpindex) {
- start = i;
- count = 1;
- i++;
-
- while (i < dumpindex &&
- strcmp(((char **)(dumpstack_zone->min))[i],
- ((char **)(dumpstack_zone->min))[start]) == 0)
- {
- count++;
- i++;
- }
-
- if (count > 1) {
- sprintf(buf, "%s * %d\n",
- ((char **)(dumpstack_zone->min))[start], count);
- } else {
- sprintf(buf, "%s\n",
- ((char **)(dumpstack_zone->min))[start]);
- }
-
- write(STDERR, buf, strlen(buf));
- } /* end while */
-
- strcpy(buf, "\nend of stack dump\n");
- write(STDERR, buf, strlen(buf));
-
-} /* end print_dump_stack() */
-
-#endif /* MR_LOWLEVEL_DEBUG */
-
-#if defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
- /* try_munprotect is only useful if we have SIGINFO */
-
-/*
-** fatal_abort() prints an error message, possibly a stack dump, and then exits.
-** It is like fatal_error(), except that it is safe to call
-** from a signal handler.
-*/
-
-static void
-fatal_abort(void *context, const char *main_msg, int dump)
-{
- char *context_msg;
-
- context_msg = explain_context(context);
- write(STDERR, main_msg, strlen(main_msg));
- write(STDERR, context_msg, strlen(context_msg));
- MR_trace_report_raw(STDERR);
-
- if (dump) {
- print_dump_stack();
- }
-
- _exit(1);
-}
-
-static bool
-try_munprotect(void *addr, void *context)
-{
- Word * fault_addr;
- Word * new_zone;
- MemoryZone *zone;
-
- fault_addr = (Word *) addr;
-
- zone = used_memory_zones;
-
- if (memdebug) {
- fprintf(stderr, "caught fault at %p\n", (void *)addr);
- }
-
- while(zone != NULL) {
- if (memdebug) {
- fprintf(stderr, "checking %s#%d: %p - %p\n",
- zone->name, zone->id, (void *) zone->redzone,
- (void *) zone->top);
- }
-
- if (zone->redzone <= fault_addr && fault_addr <= zone->top) {
-
- if (memdebug) {
- fprintf(stderr, "address is in %s#%d redzone\n",
- zone->name, zone->id);
- }
-
- return zone->handler(fault_addr, zone, context);
- }
- zone = zone->next;
- }
-
- if (memdebug) {
- fprintf(stderr, "address not in any redzone.\n");
- }
-
- return FALSE;
-} /* end try_munprotect() */
-
-bool
-default_handler(Word *fault_addr, MemoryZone *zone, void *context)
-{
- Word *new_zone;
- size_t zone_size;
-
- new_zone = (Word *) round_up((Unsigned) fault_addr + sizeof(Word), unit);
-
- if (new_zone <= zone->hardmax) {
- zone_size = (char *)new_zone - (char *)zone->redzone;
-
- if (memdebug) {
- fprintf(stderr, "trying to unprotect %s#%d from %p to %p (%x)\n",
- zone->name, zone->id, (void *) zone->redzone, (void *) new_zone,
- (int)zone_size);
- }
- if (mprotect((char *)zone->redzone, zone_size,
- PROT_READ|PROT_WRITE) < 0)
- {
- char buf[2560];
- sprintf(buf, "Mercury runtime: cannot unprotect %s#%d zone",
- zone->name, zone->id);
- perror(buf);
- exit(1);
- }
-
- zone->redzone = new_zone;
-
- if (memdebug) {
- fprintf(stderr, "successful: %s#%d redzone now %p to %p\n",
- zone->name, zone->id, (void *) zone->redzone,
- (void *) zone->top);
- }
- return TRUE;
- } else {
- char buf[2560];
- if (memdebug) {
- fprintf(stderr, "can't unprotect last page of %s#%d\n",
- zone->name, zone->id);
- fflush(stdout);
- }
- sprintf(buf, "\nMercury runtime: memory zone %s#%d overflowed\n",
- zone->name, zone->id);
- fatal_abort(context, buf, TRUE);
- }
-
- return FALSE;
-} /* end default_handler() */
-
-bool
-null_handler(Word *fault_addr, MemoryZone *zone, void *context)
-{
- return FALSE;
-}
-
-#else
-/* not HAVE_MPROTECT || not HAVE_SIGINFO */
-
-static bool
-try_munprotect(void *addr, void *context)
-{
- return FALSE;
-}
-
-bool
-default_handler(Word *fault_addr, MemoryZone *zone, void *context)
-{
- return FALSE;
-}
-
-bool
-null_handler(Word *fault_addr, MemoryZone *zone, void *context)
-{
- return FALSE;
-}
-
-#endif /* not HAVE_MPROTECT || not HAVE_SIGINFO */
-
-#if defined(HAVE_SIGCONTEXT_STRUCT)
-
-static void
-setup_signal(void)
-{
- if (signal(SIGBUS, (void(*)(int)) complex_sighandler) == SIG_ERR)
- {
- perror("cannot set SIGBUS handler");
- exit(1);
- }
-
- if (signal(SIGSEGV, (void(*)(int)) complex_sighandler) == SIG_ERR)
- {
- perror("cannot set SIGSEGV handler");
- exit(1);
- }
-}
-
-static void
-complex_sighandler(int sig, struct sigcontext_struct sigcontext)
-{
- void *address = (void *) sigcontext.cr2;
- #ifdef PC_ACCESS
- void *pc_at_signal = (void *) sigcontext.PC_ACCESS;
- #endif
-
- switch(sig) {
- case SIGSEGV:
- /*
- ** If we're debugging, print the segv explanation
- ** messages before we call try_munprotect. But if
- ** we're not debugging, only print them if
- ** try_munprotect fails.
- */
- if (memdebug) {
- fflush(stdout);
- fprintf(stderr, "\n*** Mercury runtime: "
- "caught segmentation violation ***\n");
- }
- if (try_munprotect(address, &sigcontext)) {
- if (memdebug) {
- fprintf(stderr, "returning from "
- "signal handler\n\n");
- }
- return;
- }
- if (!memdebug) {
- fflush(stdout);
- fprintf(stderr, "\n*** Mercury runtime: "
- "caught segmentation violation ***\n");
- }
- break;
-
- case SIGBUS:
- fflush(stdout);
- fprintf(stderr, "\n*** Mercury runtime: "
- "caught bus error ***\n");
- break;
-
- default:
- fflush(stdout);
- fprintf(stderr, "\n*** Mercury runtime: "
- "caught unknown signal %d ***\n", sig);
- break;
- }
-
- #ifdef PC_ACCESS
- fprintf(stderr, "PC at signal: %ld (%lx)\n",
- (long) pc_at_signal, (long) pc_at_signal);
- #endif
- fprintf(stderr, "address involved: %p\n", address);
-
- MR_trace_report(stderr);
- print_dump_stack();
- dump_prev_locations();
- fprintf(stderr, "exiting from signal handler\n");
- exit(1);
-} /* end complex_sighandler() */
-
-static char *
-explain_context(void *the_context)
-{
- static char buf[100];
- #ifdef PC_ACCESS
- struct sigcontext_struct *context = the_context;
- void *pc_at_signal = (void *) context->PC_ACCESS;
-
- sprintf(buf, "PC at signal: %ld (%lx)\n",
- (long)pc_at_signal, (long)pc_at_signal);
- #else
- buf[0] = '\0';
- #endif
-
- return buf;
-}
-
-#elif defined(HAVE_SIGINFO_T)
-
-static void
-setup_signal(void)
-{
- struct sigaction act;
-
- act.sa_flags = SA_SIGINFO | SA_RESTART;
- if (sigemptyset(&act.sa_mask) != 0) {
- perror("Mercury runtime: cannot set clear signal mask");
- exit(1);
- }
-
- act.SIGACTION_FIELD = complex_bushandler;
- if (sigaction(SIGBUS, &act, NULL) != 0) {
- perror("Mercury runtime: cannot set SIGBUS handler");
- exit(1);
- }
-
- act.SIGACTION_FIELD = complex_segvhandler;
- if (sigaction(SIGSEGV, &act, NULL) != 0) {
- perror("Mercury runtime: cannot set SIGSEGV handler");
- exit(1);
- }
-}
-
-static void
-complex_bushandler(int sig, siginfo_t *info, void *context)
-{
- fflush(stdout);
-
- if (sig != SIGBUS || !info || info->si_signo != SIGBUS) {
- fprintf(stderr, "\n*** Mercury runtime: ");
- fprintf(stderr, "caught strange bus error ***\n");
- exit(1);
- }
-
- fprintf(stderr, "\n*** Mercury runtime: ");
- fprintf(stderr, "caught bus error ***\n");
-
- if (info->si_code > 0) {
- fprintf(stderr, "cause: ");
- switch (info->si_code)
- {
- case BUS_ADRALN:
- fprintf(stderr, "invalid address alignment\n");
- break;
-
- case BUS_ADRERR:
- fprintf(stderr, "non-existent physical address\n");
- break;
-
- case BUS_OBJERR:
- fprintf(stderr, "object specific hardware error\n");
- break;
-
- default:
- fprintf(stderr, "unknown\n");
- break;
-
- } /* end switch */
-
- fprintf(stderr, "%s", explain_context(context));
- fprintf(stderr, "address involved: %p\n",
- (void *) info->si_addr);
- } /* end if */
-
- MR_trace_report(stderr);
- print_dump_stack();
- dump_prev_locations();
- fprintf(stderr, "exiting from signal handler\n");
- exit(1);
-} /* end complex_bushandler() */
-
-static void
-explain_segv(siginfo_t *info, void *context)
-{
- fflush(stdout);
-
- fprintf(stderr, "\n*** Mercury runtime: ");
- fprintf(stderr, "caught segmentation violation ***\n");
-
- if (!info) {
- return;
- }
-
- if (info->si_code > 0) {
- fprintf(stderr, "cause: ");
- switch (info->si_code)
- {
- case SEGV_MAPERR:
- fprintf(stderr, "address not mapped to object\n");
- break;
-
- case SEGV_ACCERR:
- fprintf(stderr, "bad permissions for mapped object\n");
- break;
-
- default:
- fprintf(stderr, "unknown\n");
- break;
- }
-
- fprintf(stderr, "%s", explain_context(context));
- fprintf(stderr, "address involved: %p\n",
- (void *) info->si_addr);
-
- } /* end if */
-} /* end explain_segv() */
-
-static void
-complex_segvhandler(int sig, siginfo_t *info, void *context)
-{
- if (sig != SIGSEGV || !info || info->si_signo != SIGSEGV) {
- fprintf(stderr, "\n*** Mercury runtime: ");
- fprintf(stderr, "caught strange segmentation violation ***\n");
- exit(1);
- }
-
- /*
- ** If we're debugging, print the segv explanation messages
- ** before we call try_munprotect. But if we're not debugging,
- ** only print them if try_munprotect fails.
- */
-
- if (memdebug) {
- explain_segv(info, context);
- }
-
- if (try_munprotect(info->si_addr, context)) {
- if (memdebug) {
- fprintf(stderr, "returning from signal handler\n\n");
- }
-
- return;
- }
-
- if (!memdebug) {
- explain_segv(info, context);
- }
-
- MR_trace_report(stderr);
- print_dump_stack();
- dump_prev_locations();
- fprintf(stderr, "exiting from signal handler\n");
- exit(1);
-} /* end complex_segvhandler */
-
-static char *
-explain_context(void *the_context)
-{
- static char buf[100];
-
- #ifdef PC_ACCESS
-
- ucontext_t *context = the_context;
-
- #ifdef PC_ACCESS_GREG
- sprintf(buf, "PC at signal: %ld (%lx)\n",
- (long) context->uc_mcontext.gregs[PC_ACCESS],
- (long) context->uc_mcontext.gregs[PC_ACCESS]);
- #else
- sprintf(buf, "PC at signal: %ld (%lx)\n",
- (long) context->uc_mcontext.PC_ACCESS,
- (long) context->uc_mcontext.PC_ACCESS);
- #endif
-
- #else /* not PC_ACCESS */
-
- /* if PC_ACCESS is not set, we don't know the context */
- /* therefore we return an empty string to be printed */
- buf[0] = '\0';
-
- #endif /* not PC_ACCESS */
-
- return buf;
-}
-
-#else /* not HAVE_SIGINFO_T && not HAVE_SIGCONTEXT_STRUCT */
-
-static void
-setup_signal(void)
-{
- if (signal(SIGBUS, simple_sighandler) == SIG_ERR) {
- perror("cannot set SIGBUS handler");
- exit(1);
- }
-
- if (signal(SIGSEGV, simple_sighandler) == SIG_ERR) {
- perror("cannot set SIGSEGV handler");
- exit(1);
- }
-}
-
-static void
-simple_sighandler(int sig)
-{
- fflush(stdout);
- fprintf(stderr, "*** Mercury runtime: ");
-
- switch (sig)
- {
- case SIGBUS:
- fprintf(stderr, "caught bus error ***\n");
- break;
-
- case SIGSEGV:
- fprintf(stderr, "caught segmentation violation ***\n");
- break;
-
- default:
- fprintf(stderr, "caught unknown signal %d ***\n", sig);
- break;
- }
-
- print_dump_stack();
- dump_prev_locations();
- fprintf(stderr, "exiting from signal handler\n");
- exit(1);
-}
-
-#endif /* not HAVE_SIGINFO_T && not HAVE_SIGCONTEXT_STRUCT */
#ifdef CONSERVATIVE_GC
Index: runtime/mercury_memory.h
===================================================================
RCS file: /home/staff/zs/imp/mercury/runtime/mercury_memory.h,v
retrieving revision 1.4
diff -u -r1.4 mercury_memory.h
--- mercury_memory.h 1998/03/30 03:08:38 1.4
+++ mercury_memory.h 1998/04/29 06:14:16
@@ -18,113 +18,7 @@
#ifndef MERCURY_MEMORY_H
#define MERCURY_MEMORY_H
-#include "mercury_regs.h" /* for NUM_REAL_REGS */
-
-#include <stdlib.h> /* for size_t */
-
-#include "mercury_types.h" /* for Word */
-#include "mercury_std.h" /* for bool */
-
-
-/* these cannot be changed without lots of modifications elsewhere */
-#define MAX_REAL_REG 32 /* r1 .. r32 */
-#define NUM_SPECIAL_REG 5 /* succip, sp, hp, maxfr, curfr */
-
-/* this can be changed at will, including by -D options to the C compiler */
-#ifndef MAX_VIRTUAL_REG
-#define MAX_VIRTUAL_REG 1024
-#endif
-
-/* allocate enough fake_regs to hold both the special regs */
-/* and all the virtual registers */
-#define MAX_FAKE_REG (NUM_SPECIAL_REG + MAX_VIRTUAL_REG)
- /* mr0 .. mr36, mr(37) ... mr(1028) */
-
-/* reserve MAX_FAKE_REG virtual regs, numbered from 0 to MAX_FAKE_REG-1 */
-extern Word fake_reg[MAX_FAKE_REG];
-
-/* used to lookup the fake_reg for a given real reg */
-extern Word virtual_reg_map[MAX_REAL_REG];
-
-/* used for counting register usage */
-extern unsigned long num_uses[MAX_RN];
-
-/*
-** The Mercury runtime uses a number of memory areas or *zones*. These
-** hold the detstack, the nondetstack, the heap, and potentially other
-** areas such as a trail, a "solutions"-heap, and so on.
-** These memory areas are each represented by a structure that contains
-** the following fields:
-** name - a string constant used to name the allocated area
-** id - an integer which together with the name should uniquely
-** identify the allocated area.
-** bottom - the address of the bottom of the allocated area
-** (should be on a page boundary)
-** top - the address one word past the top of the allocated area
-** (should be on a page boundary)
-** min - the address of the lowest part of the allocated that
-** will be used. This may be different to `bottom'
-** so that the use of different memory zones doesn't
-** beat the cache.
-** max - the highest address in this memory area that has been
-** used so far. This is only defined in debugging grades.
-** hardmax - the address of the bottom of the last page of the allocated
-** area. This is one higher than the highest address that
-** can be used in this zone. We never unprotect the
-** last page of a zone so that we retain protection
-** against overrunning the end of the zone. This is
-** obviously only available on platforms that have
-** mprotect.
-** (should be on a page boundary)
-** redzone - the address of the start of the region that has been
-** mprotected as a redzone. Since without SIGINFO
-** it is not possible [portably] to figure out
-** where the fault occured, redzone is only available
-** on platforms that have both mprotect and SIGINFO.
-** (should be on a page boundary)
-** handler - the address of a function to handle accesses in the
-** redzone of this allocated area. This is only
-** specified if mprotect and SIGINFO are available.
-*/
-
-typedef struct MEMORY_ZONE MemoryZone;
-
-typedef bool ZoneHandler(Word *addr, struct MEMORY_ZONE *zone, void *context);
-
-struct MEMORY_ZONE {
- struct MEMORY_ZONE *next; /* the memory zones are organized as a
- ** linked list of free zones and linked
- ** list of used zones. The next field
- ** is NULL or a pointer to the next memory
- ** zone in the list.
- */
- const char *name; /* name identifier */
- int id; /* number */
- Word *bottom; /* beginning of the allocated area */
- Word *top; /* end of the allocated area */
- Word *min; /* lowest word of the area to be used */
- Word *max; /* highest word of the area to be used;
- computed only if MR_LOWLEVEL_DEBUG is
- enabled */
-#ifdef HAVE_MPROTECT
- Word *redzone_base; /* beginning of the original redzone */
- Word *redzone; /* beginning of the current redzone */
- Word *hardmax; /* last page of the zone which can't be
- unprotected */
- #ifdef HAVE_SIGINFO
- ZoneHandler *handler; /* handler for page faults in the redzone */
- #endif /* HAVE_SIGINFO */
-#endif /* HAVE_MPROTECT */
-};
-
-#define MAX_ZONES 16
-
-extern MemoryZone *zone_table;
-
- /* A linked list of all the unused zones */
-extern MemoryZone *free_memory_zones;
- /* A linked list of all the used zones */
-extern MemoryZone *used_memory_zones;
+#include "mercury_memory_zones.h"
extern MemoryZone *detstack_zone;
extern MemoryZone *nondetstack_zone;
@@ -139,73 +33,21 @@
#endif
/*
-** create_zone(Name, Id, Size, Offset, RedZoneSize, FaultHandler)
-** allocates a new memory zone with name Name, and number Id, size
-** Size (in bytes - which gets rounded up to the nearest multiple of
-** the page size), an offset Offset from the page boundary at which
-** to start using the memory region (used to help avoid beating the cache),
-** the amount Redzone of memory (in bytes) to be protected as a redzone
-** (must be less than Size), and the address of a function to handle
-** memory references in the redzone.
-** If it fails to allocate or protect the zone, then it exits.
-** If mprotect or SIGINFO are unavailable, then the last two arguments
-** are ignored.
-*/
-
-MemoryZone *create_zone(const char *name, int id,
- size_t size, size_t offset, size_t redsize,
- ZoneHandler *handler);
-
-/*
-** construct_zone(Name, Id, Base, Size, Offset, RedZoneSize, FaultHandler)
-** has the same behaviour as create_zone, except instread of allocating
-** the memory, it takes a pointer to a region of memory that must be at
-** least Size bytes, or if HAVE_MPROTECT is defined, then it must be at
-** least Size + unit[*] bytes.
-** If it fails to protect the redzone then it exits
-** If mprotect or SIGINFO are unavailable, then the last two arguments
-** are ignored.
-**
-** [*] unit is a global variable containing the page size in bytes
+** round_up(amount, align) returns `amount' rounded up to the nearest
+** alignment boundary. `align' must be a power of 2.
*/
-MemoryZone *construct_zone(const char *name, int Id, Word *base,
- size_t size, size_t offset, size_t redsize,
- ZoneHandler *handler);
+#define round_up(amount, align) ((((amount) - 1) | ((align) - 1)) + 1)
-/*
-** reset_zone(Zone) resets the redzone on the given MemoryZone to the
-** original zone specified in the call to {create,construct}_zone() if
-** HAVE_MPROTECT and HAVE_SIGINFO. If either HAVE_MPROTECT or HAVE_SIGINFO
-** are not defined, it does nothing.
+/*
+** For these functions, see the comments in mercury_memory.c and
+** mercury_engine.c
*/
-void reset_zone(MemoryZone *zone);
-
-/*
-** default_handler is a function that can be passed to create_zone to
-** unprotect enough of the redzone to allow the access to succeed, or
-** fail if there is no space left in the zone.
-*/
-ZoneHandler default_handler;
-
-/*
-** null_handler is a function that can be passed to create_zone which always
-** fails.
-*/
-ZoneHandler null_handler;
-
-/* for these functions, see the comments in memory.c and engine.mod */
extern void init_memory(void);
extern void init_heap(void);
extern void debug_memory(void);
/*
-** next_offset() returns sucessive offsets across the primary cache. Useful
-** when calling {create,construct}_zone().
-*/
-extern size_t next_offset(void);
-
-/*
** allocate_bytes() allocates the given number of bytes.
**
** allocate_object(type) allocates space for an object of the specified type.
@@ -246,5 +88,14 @@
#include <stddef.h> /* for size_t */
void *checked_malloc(size_t n);
void *checked_realloc(void *old, size_t n);
+
+/*
+** `unit' is the size of the minimum unit of memory we allocate (in bytes).
+** `page_size' is the size of a single page of memory.
+*/
+
+extern size_t unit;
+extern size_t page_size;
+
#endif /* not MERCURY_MEMORY_H */
New File: runtime/mercury_memory_handlers.c
===================================================================
/*
** Copyright (C) 1998 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/
/*
** This module defines the signal handlers for memory zones.
** These handlers are invoked when memory is accessed outside of
** the memory zones, or at the protected region at the end of a
** memory zone (if available).
*/
/*---------------------------------------------------------------------------*/
#include "mercury_imp.h"
#ifdef HAVE_SIGCONTEXT_STRUCT
/*
** Some versions of Linux call it struct sigcontext_struct, some call it
** struct sigcontext. The following #define eliminates the differences.
*/
#define sigcontext_struct sigcontext /* must be before #include <signal.h> */
/*
** On some systems (e.g. most versions of Linux) we need to #define
** __KERNEL__ to get sigcontext_struct from <signal.h>.
** This stuff must come before anything else that might include <signal.h>,
** otherwise the #define __KERNEL__ may not work.
*/
#define __KERNEL__
#include <signal.h> /* must come third */
#undef __KERNEL__
/*
** Some versions of Linux define it in <signal.h>, others define it in
** <asm/sigcontext.h>. We try both.
*/
#ifdef HAVE_ASM_SIGCONTEXT
#include <asm/sigcontext.h>
#endif
#else
#include <signal.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_SYS_SIGINFO
#include <sys/siginfo.h>
#endif
#ifdef HAVE_MPROTECT
#include <sys/mman.h>
#endif
#ifdef HAVE_UCONTEXT
#include <ucontext.h>
#endif
#ifdef HAVE_SYS_UCONTEXT
#include <sys/ucontext.h>
#endif
#include "mercury_imp.h"
#include "mercury_trace.h"
#include "mercury_memory_zones.h"
#include "mercury_memory_handlers.h"
/*---------------------------------------------------------------------------*/
#ifdef HAVE_SIGINFO
#if defined(HAVE_SIGCONTEXT_STRUCT)
static void complex_sighandler(int, struct sigcontext_struct);
#elif defined(HAVE_SIGINFO_T)
static void complex_bushandler(int, siginfo_t *, void *);
static void complex_segvhandler(int, siginfo_t *, void *);
#else
#error "HAVE_SIGINFO defined but don't know how to get it"
#endif
#else
static void simple_sighandler(int);
#endif
/*
** round_up(amount, align) returns `amount' rounded up to the nearest
** alignment boundary. `align' must be a power of 2.
*/
static void setup_mprotect(void);
static void print_dump_stack(void);
#ifdef HAVE_SIGINFO
static bool try_munprotect(void *address, void *context);
static char *explain_context(void *context);
#endif /* HAVE_SIGINFO */
#define STDERR 2
#if defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
/* try_munprotect is only useful if we have SIGINFO */
/*
** fatal_abort() prints an error message, possibly a stack dump, and then exits.
** It is like fatal_error(), except that it is safe to call
** from a signal handler.
*/
static void
fatal_abort(void *context, const char *main_msg, int dump)
{
char *context_msg;
context_msg = explain_context(context);
write(STDERR, main_msg, strlen(main_msg));
write(STDERR, context_msg, strlen(context_msg));
MR_trace_report_raw(STDERR);
if (dump) {
print_dump_stack();
}
_exit(1);
}
static bool
try_munprotect(void *addr, void *context)
{
Word * fault_addr;
Word * new_zone;
MemoryZone *zone;
fault_addr = (Word *) addr;
zone = get_used_memory_zones();
if (memdebug) {
fprintf(stderr, "caught fault at %p\n", (void *)addr);
}
while(zone != NULL) {
if (memdebug) {
fprintf(stderr, "checking %s#%d: %p - %p\n",
zone->name, zone->id, (void *) zone->redzone,
(void *) zone->top);
}
if (zone->redzone <= fault_addr && fault_addr <= zone->top) {
if (memdebug) {
fprintf(stderr, "address is in %s#%d redzone\n",
zone->name, zone->id);
}
return zone->handler(fault_addr, zone, context);
}
zone = zone->next;
}
if (memdebug) {
fprintf(stderr, "address not in any redzone.\n");
}
return FALSE;
} /* end try_munprotect() */
bool
default_handler(Word *fault_addr, MemoryZone *zone, void *context)
{
Word *new_zone;
size_t zone_size;
new_zone = (Word *) round_up((Unsigned) fault_addr + sizeof(Word), unit);
if (new_zone <= zone->hardmax) {
zone_size = (char *)new_zone - (char *)zone->redzone;
if (memdebug) {
fprintf(stderr, "trying to unprotect %s#%d from %p to %p (%x)\n",
zone->name, zone->id, (void *) zone->redzone, (void *) new_zone,
(int)zone_size);
}
if (mprotect((char *)zone->redzone, zone_size,
PROT_READ|PROT_WRITE) < 0)
{
char buf[2560];
sprintf(buf, "Mercury runtime: cannot unprotect %s#%d zone",
zone->name, zone->id);
perror(buf);
exit(1);
}
zone->redzone = new_zone;
if (memdebug) {
fprintf(stderr, "successful: %s#%d redzone now %p to %p\n",
zone->name, zone->id, (void *) zone->redzone,
(void *) zone->top);
}
return TRUE;
} else {
char buf[2560];
if (memdebug) {
fprintf(stderr, "can't unprotect last page of %s#%d\n",
zone->name, zone->id);
fflush(stdout);
}
sprintf(buf, "\nMercury runtime: memory zone %s#%d overflowed\n",
zone->name, zone->id);
fatal_abort(context, buf, TRUE);
}
return FALSE;
} /* end default_handler() */
bool
null_handler(Word *fault_addr, MemoryZone *zone, void *context)
{
return FALSE;
}
#else
/* not HAVE_MPROTECT || not HAVE_SIGINFO */
static bool
try_munprotect(void *addr, void *context)
{
return FALSE;
}
bool
default_handler(Word *fault_addr, MemoryZone *zone, void *context)
{
return FALSE;
}
bool
null_handler(Word *fault_addr, MemoryZone *zone, void *context)
{
return FALSE;
}
#endif /* not HAVE_MPROTECT || not HAVE_SIGINFO */
#if defined(HAVE_SIGCONTEXT_STRUCT)
void
setup_signal(void)
{
if (signal(SIGBUS, (void(*)(int)) complex_sighandler) == SIG_ERR)
{
perror("cannot set SIGBUS handler");
exit(1);
}
if (signal(SIGSEGV, (void(*)(int)) complex_sighandler) == SIG_ERR)
{
perror("cannot set SIGSEGV handler");
exit(1);
}
}
static void
complex_sighandler(int sig, struct sigcontext_struct sigcontext)
{
void *address = (void *) sigcontext.cr2;
#ifdef PC_ACCESS
void *pc_at_signal = (void *) sigcontext.PC_ACCESS;
#endif
switch(sig) {
case SIGSEGV:
/*
** If we're debugging, print the segv explanation
** messages before we call try_munprotect. But if
** we're not debugging, only print them if
** try_munprotect fails.
*/
if (memdebug) {
fflush(stdout);
fprintf(stderr, "\n*** Mercury runtime: "
"caught segmentation violation ***\n");
}
if (try_munprotect(address, &sigcontext)) {
if (memdebug) {
fprintf(stderr, "returning from "
"signal handler\n\n");
}
return;
}
if (!memdebug) {
fflush(stdout);
fprintf(stderr, "\n*** Mercury runtime: "
"caught segmentation violation ***\n");
}
break;
case SIGBUS:
fflush(stdout);
fprintf(stderr, "\n*** Mercury runtime: "
"caught bus error ***\n");
break;
default:
fflush(stdout);
fprintf(stderr, "\n*** Mercury runtime: "
"caught unknown signal %d ***\n", sig);
break;
}
#ifdef PC_ACCESS
fprintf(stderr, "PC at signal: %ld (%lx)\n",
(long) pc_at_signal, (long) pc_at_signal);
#endif
fprintf(stderr, "address involved: %p\n", address);
MR_trace_report(stderr);
print_dump_stack();
dump_prev_locations();
fprintf(stderr, "exiting from signal handler\n");
exit(1);
} /* end complex_sighandler() */
static char *
explain_context(void *the_context)
{
static char buf[100];
#ifdef PC_ACCESS
struct sigcontext_struct *context = the_context;
void *pc_at_signal = (void *) context->PC_ACCESS;
sprintf(buf, "PC at signal: %ld (%lx)\n",
(long)pc_at_signal, (long)pc_at_signal);
#else
buf[0] = '\0';
#endif
return buf;
}
#elif defined(HAVE_SIGINFO_T)
void
setup_signal(void)
{
struct sigaction act;
act.sa_flags = SA_SIGINFO | SA_RESTART;
if (sigemptyset(&act.sa_mask) != 0) {
perror("Mercury runtime: cannot set clear signal mask");
exit(1);
}
act.SIGACTION_FIELD = complex_bushandler;
if (sigaction(SIGBUS, &act, NULL) != 0) {
perror("Mercury runtime: cannot set SIGBUS handler");
exit(1);
}
act.SIGACTION_FIELD = complex_segvhandler;
if (sigaction(SIGSEGV, &act, NULL) != 0) {
perror("Mercury runtime: cannot set SIGSEGV handler");
exit(1);
}
}
static void
complex_bushandler(int sig, siginfo_t *info, void *context)
{
fflush(stdout);
if (sig != SIGBUS || !info || info->si_signo != SIGBUS) {
fprintf(stderr, "\n*** Mercury runtime: ");
fprintf(stderr, "caught strange bus error ***\n");
exit(1);
}
fprintf(stderr, "\n*** Mercury runtime: ");
fprintf(stderr, "caught bus error ***\n");
if (info->si_code > 0) {
fprintf(stderr, "cause: ");
switch (info->si_code)
{
case BUS_ADRALN:
fprintf(stderr, "invalid address alignment\n");
break;
case BUS_ADRERR:
fprintf(stderr, "non-existent physical address\n");
break;
case BUS_OBJERR:
fprintf(stderr, "object specific hardware error\n");
break;
default:
fprintf(stderr, "unknown\n");
break;
} /* end switch */
fprintf(stderr, "%s", explain_context(context));
fprintf(stderr, "address involved: %p\n",
(void *) info->si_addr);
} /* end if */
MR_trace_report(stderr);
print_dump_stack();
dump_prev_locations();
fprintf(stderr, "exiting from signal handler\n");
exit(1);
} /* end complex_bushandler() */
static void
explain_segv(siginfo_t *info, void *context)
{
fflush(stdout);
fprintf(stderr, "\n*** Mercury runtime: ");
fprintf(stderr, "caught segmentation violation ***\n");
if (!info) {
return;
}
if (info->si_code > 0) {
fprintf(stderr, "cause: ");
switch (info->si_code)
{
case SEGV_MAPERR:
fprintf(stderr, "address not mapped to object\n");
break;
case SEGV_ACCERR:
fprintf(stderr, "bad permissions for mapped object\n");
break;
default:
fprintf(stderr, "unknown\n");
break;
}
fprintf(stderr, "%s", explain_context(context));
fprintf(stderr, "address involved: %p\n",
(void *) info->si_addr);
} /* end if */
} /* end explain_segv() */
static void
complex_segvhandler(int sig, siginfo_t *info, void *context)
{
if (sig != SIGSEGV || !info || info->si_signo != SIGSEGV) {
fprintf(stderr, "\n*** Mercury runtime: ");
fprintf(stderr, "caught strange segmentation violation ***\n");
exit(1);
}
/*
** If we're debugging, print the segv explanation messages
** before we call try_munprotect. But if we're not debugging,
** only print them if try_munprotect fails.
*/
if (memdebug) {
explain_segv(info, context);
}
if (try_munprotect(info->si_addr, context)) {
if (memdebug) {
fprintf(stderr, "returning from signal handler\n\n");
}
return;
}
if (!memdebug) {
explain_segv(info, context);
}
MR_trace_report(stderr);
print_dump_stack();
dump_prev_locations();
fprintf(stderr, "exiting from signal handler\n");
exit(1);
} /* end complex_segvhandler */
static char *
explain_context(void *the_context)
{
static char buf[100];
#ifdef PC_ACCESS
ucontext_t *context = the_context;
#ifdef PC_ACCESS_GREG
sprintf(buf, "PC at signal: %ld (%lx)\n",
(long) context->uc_mcontext.gregs[PC_ACCESS],
(long) context->uc_mcontext.gregs[PC_ACCESS]);
#else
sprintf(buf, "PC at signal: %ld (%lx)\n",
(long) context->uc_mcontext.PC_ACCESS,
(long) context->uc_mcontext.PC_ACCESS);
#endif
#else /* not PC_ACCESS */
/* if PC_ACCESS is not set, we don't know the context */
/* therefore we return an empty string to be printed */
buf[0] = '\0';
#endif /* not PC_ACCESS */
return buf;
}
#else /* not HAVE_SIGINFO_T && not HAVE_SIGCONTEXT_STRUCT */
void
setup_signal(void)
{
if (signal(SIGBUS, simple_sighandler) == SIG_ERR) {
perror("cannot set SIGBUS handler");
exit(1);
}
if (signal(SIGSEGV, simple_sighandler) == SIG_ERR) {
perror("cannot set SIGSEGV handler");
exit(1);
}
}
static void
simple_sighandler(int sig)
{
fflush(stdout);
fprintf(stderr, "*** Mercury runtime: ");
switch (sig)
{
case SIGBUS:
fprintf(stderr, "caught bus error ***\n");
break;
case SIGSEGV:
fprintf(stderr, "caught segmentation violation ***\n");
break;
default:
fprintf(stderr, "caught unknown signal %d ***\n", sig);
break;
}
print_dump_stack();
dump_prev_locations();
fprintf(stderr, "exiting from signal handler\n");
exit(1);
}
#endif /* not HAVE_SIGINFO_T && not HAVE_SIGCONTEXT_STRUCT */
#ifndef MR_LOWLEVEL_DEBUG
static void
print_dump_stack(void)
{
const char *msg =
"You can get a stack dump by using `--low-level-debug'\n";
write(STDERR, msg, strlen(msg));
}
#else /* MR_LOWLEVEL_DEBUG */
static void
print_dump_stack(void)
{
int i;
int start;
int count;
char buf[2560];
strcpy(buf, "A dump of the det stack follows\n\n");
write(STDERR, buf, strlen(buf));
i = 0;
while (i < dumpindex) {
start = i;
count = 1;
i++;
while (i < dumpindex &&
strcmp(((char **)(dumpstack_zone->min))[i],
((char **)(dumpstack_zone->min))[start]) == 0)
{
count++;
i++;
}
if (count > 1) {
sprintf(buf, "%s * %d\n",
((char **)(dumpstack_zone->min))[start], count);
} else {
sprintf(buf, "%s\n",
((char **)(dumpstack_zone->min))[start]);
}
write(STDERR, buf, strlen(buf));
} /* end while */
strcpy(buf, "\nend of stack dump\n");
write(STDERR, buf, strlen(buf));
} /* end print_dump_stack() */
#endif /* MR_LOWLEVEL_DEBUG */
New File: runtime/mercury_memory_handlers.h
===================================================================
/*
** Copyright (C) 1998 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/
/*
** mercury_memory_handlers.h - signal handlers for the memory zones.
**
** This defines various signal handlers for memory access violations,
** including accesses to the redzones at the end of each zone.
*/
#ifndef MERCURY_MEMORY_HANDLERS_H
#define MERCURY_MEMORY_HANDLERS_H
#include "mercury_memory_zones.h"
/*
** default_handler is a function that can be passed to create_zone to
** unprotect enough of the redzone to allow the access to succeed, or
** fail if there is no space left in the zone.
*/
ZoneHandler default_handler;
/*
** null_handler is a function that can be passed to create_zone which always
** fails.
*/
ZoneHandler null_handler;
/*
**
** setup_signal() will setup the default signal handlers.
**
*/
void setup_signal(void);
#endif /* not MERCURY_MEMORY_HANDLERS_H */
New File: runtime/mercury_memory_zones.c
===================================================================
/*
** Copyright (C) 1998 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/
/*
** This module defines the MemoryZone data structure and operations
** for managing the memory zones.
**
** The offset of each zone can be supplied to allow us to control how
** they map onto direct mapped caches. The provided next_offset()
** function can be used to generate these offsets.
**
** We allocate a large arena, preferably aligned on a boundary that
** is a multiple of both the page size and the primary cache size.
**
** If the operating system of the machine supports the mprotect syscall,
** we also protect a chunk at the end of each area against access,
** thus detecting area overflow.
*/
/*---------------------------------------------------------------------------*/
#include "mercury_imp.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_SYS_SIGINFO
#include <sys/siginfo.h>
#endif
#ifdef HAVE_MPROTECT
#include <sys/mman.h>
#endif
#ifdef HAVE_UCONTEXT
#include <ucontext.h>
#endif
#ifdef HAVE_SYS_UCONTEXT
#include <sys/ucontext.h>
#endif
#include "mercury_imp.h"
#include "mercury_trace.h"
/*---------------------------------------------------------------------------*/
#ifdef CONSERVATIVE_GC
#define memalign(a,s) GC_MALLOC_UNCOLLECTABLE(s)
#elif defined(HAVE_MEMALIGN)
extern void *memalign(size_t, size_t);
#else
#define memalign(a,s) malloc(s)
#endif
/*
** DESCRIPTION
** The function mprotect() changes the access protections on
** the mappings specified by the range [addr, addr + len) to be
** that specified by prot. Legitimate values for prot are the
** same as those permitted for mmap and are defined in
** <sys/mman.h> as:
**
** PROT_READ page can be read
** PROT_WRITE page can be written
** PROT_EXEC page can be executed
** PROT_NONE page can not be accessed
*/
#ifdef HAVE_MPROTECT
#ifdef CONSERVATIVE_GC
/*
** The conservative garbage collectors scans through
** all these areas, so we need to allow reads.
** XXX This probably causes efficiency problems:
** too much memory for the GC to scan, and it probably
** all gets paged in.
*/
#define MY_PROT PROT_READ
#else
#define MY_PROT PROT_NONE
#endif
/* The BSDI BSD/386 1.1 headers don't define PROT_NONE */
#ifndef PROT_NONE
#define PROT_NONE 0
#endif
#endif /* HAVE_MPROTECT */
/*---------------------------------------------------------------------------*/
Word fake_reg[MAX_FAKE_REG];
Word virtual_reg_map[MAX_REAL_REG] = VIRTUAL_REG_MAP_BODY;
unsigned long num_uses[MAX_RN];
#define MAX_ZONES 16
static MemoryZone *zone_table;
static MemoryZone *used_memory_zones;
static MemoryZone *free_memory_zones;
static MemoryZone *get_zone(void);
static void unget_zone(MemoryZone *zone);
/*
** We manage the handing out of offets through the cache by
** computing the offsets once and storing them in an array
** (in shared memory if necessary). We then maintain a global
** counter used to index the array which we increment (modulo
** the size of the array) after handing out each offset.
*/
#define CACHE_SLICES 8
static size_t *offset_vector;
static int *offset_counter;
static SpinLock *offset_lock;
size_t next_offset(void);
void
init_memory_arena()
{
#ifdef PARALLEL
#ifndef CONSERVATIVE_GC
if (numprocs > 1) {
fatal_error("shared memory not implemented");
}
#else
if (numprocs > 1) {
fatal_error("shared memory not implemented with conservative gc");
}
#endif
#endif
}
void
init_zones()
{
int i;
size_t fake_reg_offset;
/*
** Allocate the MemoryZone table.
*/
zone_table = allocate_array(MemoryZone, MAX_ZONES);
/*
** Initialize the MemoryZone table.
*/
used_memory_zones = NULL;
free_memory_zones = zone_table;
for(i = 0; i < MAX_ZONES; i++) {
zone_table[i].name = "unused";
zone_table[i].id = i;
zone_table[i].bottom = NULL;
zone_table[i].top = NULL;
zone_table[i].min = NULL;
#ifdef MR_LOWLEVEL_DEBUG
zone_table[i].max = NULL;
#endif
#ifdef HAVE_MPROTECT
#ifdef HAVE_SIGINFO
zone_table[i].redzone = NULL;
#endif
zone_table[i].hardmax = NULL;
#endif
if (i+1 < MAX_ZONES) {
zone_table[i].next = &(zone_table[i+1]);
} else {
zone_table[i].next = NULL;
}
}
offset_counter = allocate_object(int);
*offset_counter = 0;
offset_vector = allocate_array(size_t, CACHE_SLICES - 1);
fake_reg_offset = (Unsigned) fake_reg % pcache_size;
for (i = 0; i < CACHE_SLICES - 1; i++) {
offset_vector[i] =
(fake_reg_offset + pcache_size / CACHE_SLICES)
% pcache_size;
}
} /* end init_zones() */
MemoryZone *
get_zone(void)
{
MemoryZone *zone;
/*
** unlink the first zone on the free-list,
** link it onto the used-list and return it.
*/
zone = free_memory_zones;
if (zone == NULL) {
fatal_error("no more memory zones");
}
free_memory_zones = free_memory_zones->next;
zone->next = used_memory_zones;
used_memory_zones = zone;
return zone;
}
void
unget_zone(MemoryZone *zone)
{
MemoryZone *prev, *tmp;
/*
** Find the zone on the used list, and unlink it from
** the list, then link it onto the start of the free-list.
*/
for(prev = NULL, tmp = used_memory_zones;
tmp && tmp != zone; prev = tmp, tmp = tmp->next)
{
/* VOID */
}
if (tmp == NULL) {
fatal_error("memory zone not found!");
}
if (prev == NULL) {
used_memory_zones = used_memory_zones->next;
} else {
prev->next = tmp->next;
}
zone->next = free_memory_zones;
free_memory_zones = zone;
}
/*
** successive calls to next_offset return offsets modulo the primary
** cache size (carefully avoiding ever giving an offset that clashes
** with fake_reg_array). This is used to give different memory zones
** different starting points across the caches so that it is better
** utilized.
** An alternative implementation would be to increment the offset by
** a fixed amount (eg 2Kb) so that as primary caches get bigger, we
** allocate more offsets across them.
*/
size_t
next_offset(void)
{
size_t offset;
get_lock(offset_lock);
offset = offset_vector[*offset_counter];
*offset_counter = (*offset_counter + 1) % (CACHE_SLICES - 1);
release_lock(offset_lock);
return offset;
}
MemoryZone *
create_zone(const char *name, int id, size_t size,
size_t offset, size_t redsize,
bool ((*handler)(Word *addr, MemoryZone *zone, void *context)))
{
Word *base;
size_t total_size;
/*
** total allocation is:
** unit (roundup to page boundary)
** size (including redzone)
** unit (an extra page for protection if
** mprotect is being used)
*/
#ifdef HAVE_MPROTECT
total_size = size + 2 * unit;
#else
total_size = size + unit;
#endif
#ifdef PARALLEL
if (numprocs > 1) {
fatal_error("shared memory not supported yet");
}
#endif
base = memalign(unit, total_size);
if (base == NULL) {
char buf[2560];
sprintf(buf, "unable allocate memory zone: %s#%d", name, id);
fatal_error(buf);
}
return construct_zone(name, id, base, size, offset, redsize, handler);
} /* end create_zone() */
MemoryZone *
construct_zone(const char *name, int id, Word *base,
size_t size, size_t offset, size_t redsize,
ZoneHandler handler)
{
MemoryZone *zone;
size_t total_size;
if (base == NULL) {
fatal_error("construct_zone called with NULL pointer");
}
zone = get_zone();
zone->name = name;
zone->id = id;
#if defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
zone->handler = handler;
#endif
zone->bottom = base;
#ifdef HAVE_MPROTECT
total_size = size + unit;
#else
total_size = size;
#endif
zone->top = (Word *) ((char *)base+total_size);
zone->min = (Word *) ((char *)base+offset);
#ifdef MR_LOWLEVEL_DEBUG
zone->max = zone->min;
#endif
/*
** setup the redzone+hardzone
*/
#ifdef HAVE_MPROTECT
#ifdef HAVE_SIGINFO
zone->redzone_base = zone->redzone = (Word *)
round_up((Unsigned)base + size - redsize, unit);
if (mprotect((char *)zone->redzone, redsize + unit, MY_PROT) < 0) {
char buf[2560];
sprintf(buf, "unable to set %s#%d redzone\n"
"base=%p, redzone=%p",
zone->name, zone->id, zone->bottom, zone->redzone);
fatal_error(buf);
}
#else /* not HAVE_SIGINFO */
zone->hardmax = (Word *) ((char *)zone->top-unit);
if (mprotect((char *)zone->hardmax, unit, MY_PROT) < 0) {
char buf[2560];
sprintf(buf, "unable to set %s#%d hardmax\n"
"base=%p, hardmax=%p",
zone->name, zone->id, zone->bottom, zone->hardmax);
fatal_error(buf);
}
#endif /* not HAVE_SIGINFO */
#endif /* not HAVE_MPROTECT */
return zone;
} /* end construct_zone() */
void
reset_zone(MemoryZone *zone)
{
#if defined(HAVE_MPROTECT) && defined(HAVE_SIGINFO)
zone->redzone = zone->redzone_base;
if (mprotect((char *)zone->redzone,
((char *)zone->top) - ((char *) zone->redzone), MY_PROT) < 0)
{
char buf[2560];
sprintf(buf, "unable to reset %s#%d redzone\n"
"base=%p, redzone=%p",
zone->name, zone->id, zone->bottom, zone->redzone);
fatal_error(buf);
}
#endif
}
MemoryZone *
get_used_memory_zones(void)
{
return used_memory_zones;
}
void
debug_memory(void)
{
MemoryZone *zone;
fprintf(stderr, "\n");
fprintf(stderr, "pcache_size = %lu (0x%lx)\n",
(unsigned long) pcache_size, (unsigned long) pcache_size);
fprintf(stderr, "page_size = %lu (0x%lx)\n",
(unsigned long) page_size, (unsigned long) page_size);
fprintf(stderr, "unit = %lu (0x%lx)\n",
(unsigned long) unit, (unsigned long) unit);
fprintf(stderr, "\n");
fprintf(stderr, "fake_reg = %p (offset %ld)\n",
(void *) fake_reg, (long) fake_reg & (unit-1));
fprintf(stderr, "\n");
for (zone = used_memory_zones; zone; zone = zone->next)
{
fprintf(stderr, "%-16s#%d-base = %p\n",
zone->name, zone->id, (void *) zone->bottom);
fprintf(stderr, "%-16s#%d-min = %p\n",
zone->name, zone->id, (void *) zone->min);
fprintf(stderr, "%-16s#%d-top = %p\n",
zone->name, zone->id, (void *) zone->top);
#ifdef HAVE_MPROTECT
#ifdef HAVE_SIGINFO
fprintf(stderr, "%-16s#%d-redzone = %p\n",
zone->name, zone->id, (void *) zone->redzone);
#endif /* HAVE_SIGINFO */
fprintf(stderr, "%-16s#%d-hardmax = %p\n",
zone->name, zone->id, (void *) zone->hardmax);
fprintf(stderr, "%-16s#%d-size = %lu\n",
zone->name, zone->id, (unsigned long)
((char *)zone->hardmax - (char *)zone->min));
#else
fprintf(stderr, "%-16s#%d-size = %lu\n",
zone->name, zone->id, (unsigned long)
((char *)zone->top - (char *)zone->min));
#endif /* HAVE_MPROTECT */
fprintf(stderr, "\n");
}
}
New File: runtime/mercury_memory_zones.h
===================================================================
/*
** Copyright (C) 1998 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/
/*
** mercury_memory_zones.h - functions and data structures for handling
** memory zones.
**
** This defines a generic memory zone handler, which can be used for
** stacks and heaps in the Mercury runtime. It provides functions for
** generating offsets so that different memory zones begin at different
** offsets (improves performance with direct mapped caches). It also
** handles the fake_reg array for holding Mercury virtual registers.
*/
#ifndef MERCURY_MEMORY_ZONES_H
#define MERCURY_MEMORY_ZONES_H
#include "mercury_regs.h" /* for NUM_REAL_REGS */
#include <stdlib.h> /* for size_t */
#include "mercury_types.h" /* for Word */
#include "mercury_std.h" /* for bool */
/* these cannot be changed without lots of modifications elsewhere */
#define MAX_REAL_REG 32 /* r1 .. r32 */
#define NUM_SPECIAL_REG 5 /* succip, sp, hp, maxfr, curfr */
/* this can be changed at will, including by -D options to the C compiler */
#ifndef MAX_VIRTUAL_REG
#define MAX_VIRTUAL_REG 1024
#endif
/* allocate enough fake_regs to hold both the special regs */
/* and all the virtual registers */
#define MAX_FAKE_REG (NUM_SPECIAL_REG + MAX_VIRTUAL_REG)
/* mr0 .. mr36, mr(37) ... mr(1028) */
/* reserve MAX_FAKE_REG virtual regs, numbered from 0 to MAX_FAKE_REG-1 */
extern Word fake_reg[MAX_FAKE_REG];
/* used to lookup the fake_reg for a given real reg */
extern Word virtual_reg_map[MAX_REAL_REG];
/* used for counting register usage */
extern unsigned long num_uses[MAX_RN];
/*
** The Mercury runtime uses a number of memory areas or *zones*. These
** hold the detstack, the nondetstack, the heap, and potentially other
** areas such as a trail, a "solutions"-heap, and so on.
** These memory areas are each represented by a structure that contains
** the following fields:
** name - a string constant used to name the allocated area
** id - an integer which together with the name should uniquely
** identify the allocated area.
** bottom - the address of the bottom of the allocated area
** (should be on a page boundary)
** top - the address one word past the top of the allocated area
** (should be on a page boundary)
** min - the address of the lowest part of the allocated that
** will be used. This may be different to `bottom'
** so that the use of different memory zones doesn't
** beat the cache.
** max - the highest address in this memory area that has been
** used so far. This is only defined in debugging grades.
** hardmax - the address of the bottom of the last page of the allocated
** area. This is one higher than the highest address that
** can be used in this zone. We never unprotect the
** last page of a zone so that we retain protection
** against overrunning the end of the zone. This is
** obviously only available on platforms that have
** mprotect.
** (should be on a page boundary)
** redzone - the address of the start of the region that has been
** mprotected as a redzone. Since without SIGINFO
** it is not possible [portably] to figure out
** where the fault occured, redzone is only available
** on platforms that have both mprotect and SIGINFO.
** (should be on a page boundary)
** handler - the address of a function to handle accesses in the
** redzone of this allocated area. This is only
** specified if mprotect and SIGINFO are available.
*/
typedef struct MEMORY_ZONE MemoryZone;
typedef bool ZoneHandler(Word *addr, struct MEMORY_ZONE *zone, void *context);
struct MEMORY_ZONE {
struct MEMORY_ZONE *next; /* the memory zones are organized as a
** linked list of free zones and linked
** list of used zones. The next field
** is NULL or a pointer to the next memory
** zone in the list.
*/
const char *name; /* name identifier */
int id; /* number */
Word *bottom; /* beginning of the allocated area */
Word *top; /* end of the allocated area */
Word *min; /* lowest word of the area to be used */
Word *max; /* highest word of the area to be used;
computed only if MR_LOWLEVEL_DEBUG is
enabled */
#ifdef HAVE_MPROTECT
Word *redzone_base; /* beginning of the original redzone */
Word *redzone; /* beginning of the current redzone */
Word *hardmax; /* last page of the zone which can't be
unprotected */
#ifdef HAVE_SIGINFO
ZoneHandler *handler; /* handler for page faults in the redzone */
#endif /* HAVE_SIGINFO */
#endif /* HAVE_MPROTECT */
};
/*
** init_memory_arena() allocates (if necessary) the top-level memory pool
** from which all allocations should come. If PARALLEL is defined, then
** this pool should be shared memory. In the absence of PARALLEL, it
** doesn't need to do anything, since with CONSERVATIVE_GC, the collector
** manages the heap, and without GC, we can allocate memory using memalign
** or malloc.
*/
void init_memory_arena(void);
/*
** init_zones() initializes the memory zone pool and the offset
** generator. It should be used before any zones are created or
** offsets requested.
*/
void init_zones(void);
/*
** create_zone(Name, Id, Size, Offset, RedZoneSize, FaultHandler)
** allocates a new memory zone with name Name, and number Id, size
** Size (in bytes - which gets rounded up to the nearest multiple of
** the page size), an offset Offset from the page boundary at which
** to start using the memory region (used to help avoid beating the cache),
** the amount Redzone of memory (in bytes) to be protected as a redzone
** (must be less than Size), and the address of a function to handle
** memory references in the redzone.
** If it fails to allocate or protect the zone, then it exits.
** If mprotect or SIGINFO are unavailable, then the last two arguments
** are ignored.
*/
MemoryZone *create_zone(const char *name, int id,
size_t size, size_t offset, size_t redsize,
ZoneHandler *handler);
/*
** construct_zone(Name, Id, Base, Size, Offset, RedZoneSize, FaultHandler)
** has the same behaviour as create_zone, except instread of allocating
** the memory, it takes a pointer to a region of memory that must be at
** least Size bytes, or if HAVE_MPROTECT is defined, then it must be at
** least Size + unit[*] bytes.
** If it fails to protect the redzone then it exits
** If mprotect or SIGINFO are unavailable, then the last two arguments
** are ignored.
**
** [*] unit is a global variable containing the page size in bytes
*/
MemoryZone *construct_zone(const char *name, int Id, Word *base,
size_t size, size_t offset, size_t redsize,
ZoneHandler *handler);
/*
** reset_zone(Zone) resets the redzone on the given MemoryZone to the
** original zone specified in the call to {create,construct}_zone() if
** HAVE_MPROTECT and HAVE_SIGINFO. If either HAVE_MPROTECT or HAVE_SIGINFO
** are not defined, it does nothing.
*/
void reset_zone(MemoryZone *zone);
/*
** get_used_memory_zones() returns a pointer to the linked list of
** used memory zones.
*/
MemoryZone *get_used_memory_zones(void);
/*
** debug_memory() prints out debugging information about the current
** memory zones.
*/
void debug_memory(void);
/*
** next_offset() returns sucessive offsets across the primary cache. Useful
** when calling {create,construct}_zone().
*/
extern size_t next_offset(void);
#endif /* not MERCURY_MEMORY_ZONES_H */
--
Tyson Dowd # There isn't any reason why Linux can't be
# implemented as an enterprise computing solution.
trd at cs.mu.oz.au # Find out what you've been missing while you've
http://www.cs.mu.oz.au/~trd # been rebooting Windows NT. -- InfoWorld, 1998.
More information about the developers
mailing list