X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=rts%2Fsm%2FStorage.c;h=5d371b9bf156958fd7065648e50e75ce6628a704;hp=d37a076ab624699e41864186f3b0406f9d7c4339;hb=5270423a6afe69f1dc57e5e5a474812182718d40;hpb=b339c8b1d0f239031802555b454062e9430ec8bb diff --git a/rts/sm/Storage.c b/rts/sm/Storage.c index d37a076..5d371b9 100644 --- a/rts/sm/Storage.c +++ b/rts/sm/Storage.c @@ -13,18 +13,15 @@ #include "PosixSource.h" #include "Rts.h" + +#include "Storage.h" #include "RtsUtils.h" -#include "RtsFlags.h" #include "Stats.h" -#include "Hooks.h" #include "BlockAlloc.h" -#include "MBlock.h" #include "Weak.h" #include "Sanity.h" #include "Arena.h" -#include "OSThreads.h" #include "Capability.h" -#include "Storage.h" #include "Schedule.h" #include "RetainerProfile.h" // for counting memory blocks (memInventory) #include "OSMem.h" @@ -32,9 +29,10 @@ #include "GC.h" #include "Evac.h" -#include #include +#include "ffi.h" + /* * All these globals require sm_mutex to access in THREADED_RTS mode. */ @@ -42,14 +40,14 @@ StgClosure *caf_list = NULL; StgClosure *revertible_caf_list = NULL; rtsBool keepCAFs; -bdescr *pinned_object_block; /* allocate pinned objects into this block */ -nat alloc_blocks; /* number of allocate()d blocks since GC */ -nat alloc_blocks_lim; /* approximate limit on alloc_blocks */ +nat alloc_blocks_lim; /* GC if n_large_blocks in any nursery + * reaches this. */ + +static bdescr *exec_block; generation *generations = NULL; /* all the generations */ generation *g0 = NULL; /* generation 0, for convenience */ generation *oldest_gen = NULL; /* oldest generation, for convenience */ -step *g0s0 = NULL; /* generation 0, step 0, for convenience */ nat total_steps = 0; step *all_steps = NULL; /* single array of steps */ @@ -65,19 +63,9 @@ step *nurseries = NULL; /* array of nurseries, >1 only if THREADED_RTS * * simultaneous access by two STG threads. */ Mutex sm_mutex; -/* - * This mutex is used by atomicModifyMutVar# only - */ -Mutex atomic_modify_mutvar_mutex; #endif - -/* - * Forward references - */ -static void *stgAllocForGMP (size_t size_in_bytes); -static void *stgReallocForGMP (void *ptr, size_t old_size, size_t new_size); -static void stgDeallocForGMP (void *ptr, size_t size); +static void allocNurseries ( void ); static void initStep (step *stp, int g, int s) @@ -87,6 +75,7 @@ initStep (step *stp, int g, int s) stp->blocks = NULL; stp->n_blocks = 0; stp->n_words = 0; + stp->live_estimate = 0; stp->old_blocks = NULL; stp->n_old_blocks = 0; stp->gen = &generations[g]; @@ -95,10 +84,10 @@ initStep (step *stp, int g, int s) stp->n_large_blocks = 0; stp->scavenged_large_objects = NULL; stp->n_scavenged_large_blocks = 0; - stp->is_compacted = 0; + stp->mark = 0; + stp->compact = 0; stp->bitmap = NULL; #ifdef THREADED_RTS - initSpinLock(&stp->sync_todo); initSpinLock(&stp->sync_large_objects); #endif stp->threads = END_TSO_QUEUE; @@ -122,7 +111,7 @@ initStorage( void ) * doing something reasonable. */ /* We use the NOT_NULL variant or gcc warns that the test is always true */ - ASSERT(LOOKS_LIKE_INFO_PTR_NOT_NULL(&stg_BLACKHOLE_info)); + ASSERT(LOOKS_LIKE_INFO_PTR_NOT_NULL((StgWord)&stg_BLACKHOLE_info)); ASSERT(LOOKS_LIKE_CLOSURE_PTR(&stg_dummy_ret_closure)); ASSERT(!HEAP_ALLOCED(&stg_dummy_ret_closure)); @@ -143,7 +132,6 @@ initStorage( void ) #if defined(THREADED_RTS) initMutex(&sm_mutex); - initMutex(&atomic_modify_mutvar_mutex); #endif ACQUIRE_SM_LOCK; @@ -153,14 +141,6 @@ initStorage( void ) * sizeof(struct generation_), "initStorage: gens"); - /* allocate all the steps into an array. It is important that we do - it this way, because we need the invariant that two step pointers - can be directly compared to see which is the oldest. - Remember that the last generation has only one step. */ - total_steps = 1 + (RtsFlags.GcFlags.generations - 1) * RtsFlags.GcFlags.steps; - all_steps = stgMallocBytes(total_steps * sizeof(struct step_), - "initStorage: steps"); - /* Initialise all generations */ for(g = 0; g < RtsFlags.GcFlags.generations; g++) { gen = &generations[g]; @@ -176,6 +156,14 @@ initStorage( void ) g0 = &generations[0]; oldest_gen = &generations[RtsFlags.GcFlags.generations-1]; + /* allocate all the steps into an array. It is important that we do + it this way, because we need the invariant that two step pointers + can be directly compared to see which is the oldest. + Remember that the last generation has only one step. */ + total_steps = 1 + (RtsFlags.GcFlags.generations - 1) * RtsFlags.GcFlags.steps; + all_steps = stgMallocBytes(total_steps * sizeof(struct step_), + "initStorage: steps"); + /* Allocate step structures in each generation */ if (RtsFlags.GcFlags.generations > 1) { /* Only for multiple-generations */ @@ -197,11 +185,7 @@ initStorage( void ) g0->steps = all_steps; } -#ifdef THREADED_RTS n_nurseries = n_capabilities; -#else - n_nurseries = 1; -#endif nurseries = stgMallocBytes (n_nurseries * sizeof(struct step_), "initStorage: nurseries"); @@ -230,16 +214,17 @@ initStorage( void ) } /* The oldest generation has one step. */ - if (RtsFlags.GcFlags.compact) { + if (RtsFlags.GcFlags.compact || RtsFlags.GcFlags.sweep) { if (RtsFlags.GcFlags.generations == 1) { - errorBelch("WARNING: compaction is incompatible with -G1; disabled"); + errorBelch("WARNING: compact/sweep is incompatible with -G1; disabled"); } else { - oldest_gen->steps[0].is_compacted = 1; + oldest_gen->steps[0].mark = 1; + if (RtsFlags.GcFlags.compact) + oldest_gen->steps[0].compact = 1; } } generations[0].max_blocks = 0; - g0s0 = &generations[0].steps[0]; /* The allocation area. Policy: keep the allocation area * small to begin with, even if we have a large suggested heap @@ -254,18 +239,19 @@ initStorage( void ) revertible_caf_list = NULL; /* initialise the allocate() interface */ - alloc_blocks = 0; alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize; - /* Tell GNU multi-precision pkg about our custom alloc functions */ - mp_set_memory_functions(stgAllocForGMP, stgReallocForGMP, stgDeallocForGMP); + exec_block = NULL; #ifdef THREADED_RTS initSpinLock(&gc_alloc_block_sync); - initSpinLock(&recordMutableGen_sync); whitehole_spin = 0; #endif + N = 0; + + initGcThreads(); + IF_DEBUG(gc, statDescribeGens()); RELEASE_SM_LOCK; @@ -280,14 +266,14 @@ exitStorage (void) void freeStorage (void) { - stgFree(g0s0); // frees all the steps + stgFree(all_steps); // frees all the steps stgFree(generations); freeAllMBlocks(); #if defined(THREADED_RTS) closeMutex(&sm_mutex); - closeMutex(&atomic_modify_mutvar_mutex); #endif stgFree(nurseries); + freeGcThreads(); } /* ----------------------------------------------------------------------------- @@ -336,6 +322,7 @@ newCAF(StgClosure* caf) { ACQUIRE_SM_LOCK; +#ifdef DYNAMIC if(keepCAFs) { // HACK: @@ -353,6 +340,7 @@ newCAF(StgClosure* caf) caf_list = caf; } else +#endif { /* Put this CAF on the mutable list for the old generation. * This is a HACK - the IND_STATIC closure doesn't really have @@ -362,7 +350,7 @@ newCAF(StgClosure* caf) * any more and can use it as a STATIC_LINK. */ ((StgIndStatic *)caf)->saved_info = NULL; - recordMutableGen(caf, oldest_gen); + recordMutableGen(caf, oldest_gen->no); } RELEASE_SM_LOCK; @@ -415,8 +403,7 @@ allocNursery (step *stp, bdescr *tail, nat blocks) if (tail != NULL) { tail->u.back = bd; } - bd->step = stp; - bd->gen_no = 0; + initBdescr(bd, stp); bd->flags = 0; bd->free = bd->start; tail = bd; @@ -428,7 +415,6 @@ allocNursery (step *stp, bdescr *tail, nat blocks) static void assignNurseriesToCapabilities (void) { -#ifdef THREADED_RTS nat i; for (i = 0; i < n_nurseries; i++) { @@ -436,14 +422,9 @@ assignNurseriesToCapabilities (void) capabilities[i].r.rCurrentNursery = nurseries[i].blocks; capabilities[i].r.rCurrentAlloc = NULL; } -#else /* THREADED_RTS */ - MainCapability.r.rNursery = &nurseries[0]; - MainCapability.r.rCurrentNursery = nurseries[0].blocks; - MainCapability.r.rCurrentAlloc = NULL; -#endif } -void +static void allocNurseries( void ) { nat i; @@ -474,6 +455,10 @@ resetNurseries( void ) ASSERT(bd->step == stp); IF_DEBUG(sanity,memset(bd->start, 0xaa, BLOCK_SIZE)); } + // these large objects are dead, since we have just GC'd + freeChain(stp->large_objects); + stp->large_objects = NULL; + stp->n_large_blocks = 0; } assignNurseriesToCapabilities(); } @@ -486,6 +471,7 @@ countNurseryBlocks (void) for (i = 0; i < n_nurseries; i++) { blocks += nurseries[i].n_blocks; + blocks += nurseries[i].n_large_blocks; } return blocks; } @@ -570,123 +556,46 @@ move_TSO (StgTSO *src, StgTSO *dest) } /* ----------------------------------------------------------------------------- - The allocate() interface - - allocateInGen() function allocates memory directly into a specific - generation. It always succeeds, and returns a chunk of memory n - words long. n can be larger than the size of a block if necessary, - in which case a contiguous block group will be allocated. - - allocate(n) is equivalent to allocateInGen(g0). + split N blocks off the front of the given bdescr, returning the + new block group. We add the remainder to the large_blocks list + in the same step as the original block. -------------------------------------------------------------------------- */ -StgPtr -allocateInGen (generation *g, nat n) -{ - step *stp; - bdescr *bd; - StgPtr ret; - - ACQUIRE_SM_LOCK; - - TICK_ALLOC_HEAP_NOCTR(n); - CCS_ALLOC(CCCS,n); - - stp = &g->steps[0]; - - if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) - { - nat req_blocks = (lnat)BLOCK_ROUND_UP(n*sizeof(W_)) / BLOCK_SIZE; - - // Attempting to allocate an object larger than maxHeapSize - // should definitely be disallowed. (bug #1791) - if (RtsFlags.GcFlags.maxHeapSize > 0 && - req_blocks >= RtsFlags.GcFlags.maxHeapSize) { - heapOverflow(); - } - - bd = allocGroup(req_blocks); - dbl_link_onto(bd, &stp->large_objects); - stp->n_large_blocks += bd->blocks; // might be larger than req_blocks - bd->gen_no = g->no; - bd->step = stp; - bd->flags = BF_LARGE; - bd->free = bd->start + n; - ret = bd->start; - } - else - { - // small allocation (blocks; - if (bd == NULL || bd->free + n > bd->start + BLOCK_SIZE_W) { - bd = allocBlock(); - bd->gen_no = g->no; - bd->step = stp; - bd->flags = 0; - bd->link = stp->blocks; - stp->blocks = bd; - stp->n_blocks++; - alloc_blocks++; - } - ret = bd->free; - bd->free += n; - } - - RELEASE_SM_LOCK; - - return ret; -} - -StgPtr -allocate (nat n) -{ - return allocateInGen(g0,n); -} - -lnat -allocatedBytes( void ) -{ - lnat allocated; - - allocated = alloc_blocks * BLOCK_SIZE_W; - if (pinned_object_block != NULL) { - allocated -= (pinned_object_block->start + BLOCK_SIZE_W) - - pinned_object_block->free; - } - - return allocated; -} - -// split N blocks off the start of the given bdescr, returning the -// remainder as a new block group. We treat the remainder as if it -// had been freshly allocated in generation 0. bdescr * splitLargeBlock (bdescr *bd, nat blocks) { bdescr *new_bd; + ACQUIRE_SM_LOCK; + + ASSERT(countBlocks(bd->step->large_objects) == bd->step->n_large_blocks); + // subtract the original number of blocks from the counter first bd->step->n_large_blocks -= bd->blocks; new_bd = splitBlockGroup (bd, blocks); - - dbl_link_onto(new_bd, &g0s0->large_objects); - g0s0->n_large_blocks += new_bd->blocks; - new_bd->gen_no = g0s0->no; - new_bd->step = g0s0; - new_bd->flags = BF_LARGE; + initBdescr(new_bd, bd->step); + new_bd->flags = BF_LARGE | (bd->flags & BF_EVACUATED); + // if new_bd is in an old generation, we have to set BF_EVACUATED new_bd->free = bd->free; + dbl_link_onto(new_bd, &bd->step->large_objects); + + ASSERT(new_bd->free <= new_bd->start + new_bd->blocks * BLOCK_SIZE_W); // add the new number of blocks to the counter. Due to the gaps - // for block descriptor, new_bd->blocks + bd->blocks might not be + // for block descriptors, new_bd->blocks + bd->blocks might not be // equal to the original bd->blocks, which is why we do it this way. - bd->step->n_large_blocks += bd->blocks; + bd->step->n_large_blocks += bd->blocks + new_bd->blocks; + + ASSERT(countBlocks(bd->step->large_objects) == bd->step->n_large_blocks); + + RELEASE_SM_LOCK; return new_bd; -} +} /* ----------------------------------------------------------------------------- - allocateLocal() + allocate() This allocates memory in the current thread - it is intended for use primarily from STG-land where we have a Capability. It is @@ -699,13 +608,38 @@ splitLargeBlock (bdescr *bd, nat blocks) -------------------------------------------------------------------------- */ StgPtr -allocateLocal (Capability *cap, nat n) +allocate (Capability *cap, lnat n) { bdescr *bd; StgPtr p; + step *stp; if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) { - return allocateInGen(g0,n); + lnat req_blocks = (lnat)BLOCK_ROUND_UP(n*sizeof(W_)) / BLOCK_SIZE; + + // Attempting to allocate an object larger than maxHeapSize + // should definitely be disallowed. (bug #1791) + if (RtsFlags.GcFlags.maxHeapSize > 0 && + req_blocks >= RtsFlags.GcFlags.maxHeapSize) { + heapOverflow(); + // heapOverflow() doesn't exit (see #2592), but we aren't + // in a position to do a clean shutdown here: we + // either have to allocate the memory or exit now. + // Allocating the memory would be bad, because the user + // has requested that we not exceed maxHeapSize, so we + // just exit. + stg_exit(EXIT_HEAPOVERFLOW); + } + + stp = &nurseries[cap->no]; + + bd = allocGroup(req_blocks); + dbl_link_onto(bd, &stp->large_objects); + stp->n_large_blocks += bd->blocks; // might be larger than req_blocks + initBdescr(bd, stp); + bd->flags = BF_LARGE; + bd->free = bd->start + n; + return bd->start; } /* small allocation (r.rNursery->n_blocks++; RELEASE_SM_LOCK; - bd->gen_no = 0; - bd->step = cap->r.rNursery; + initBdescr(bd, cap->r.rNursery); bd->flags = 0; - // NO: alloc_blocks++; - // calcAllocated() uses the size of the nursery, and we've - // already bumpted nursery->n_blocks above. + // If we had to allocate a new block, then we'll GC + // pretty quickly now, because MAYBE_GC() will + // notice that CurrentNursery->link is NULL. } else { // we have a block in the nursery: take it and put // it at the *front* of the nursery list, and use it @@ -776,45 +709,41 @@ allocateLocal (Capability *cap, nat n) ------------------------------------------------------------------------- */ StgPtr -allocatePinned( nat n ) +allocatePinned (Capability *cap, lnat n) { StgPtr p; - bdescr *bd = pinned_object_block; + bdescr *bd; + step *stp; // If the request is for a large object, then allocate() // will give us a pinned object anyway. if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) { - return allocate(n); + p = allocate(cap, n); + Bdescr(p)->flags |= BF_PINNED; + return p; } - ACQUIRE_SM_LOCK; - TICK_ALLOC_HEAP_NOCTR(n); CCS_ALLOC(CCCS,n); - // we always return 8-byte aligned memory. bd->free must be - // 8-byte aligned to begin with, so we just round up n to - // the nearest multiple of 8 bytes. - if (sizeof(StgWord) == 4) { - n = (n+1) & ~1; - } - + bd = cap->pinned_object_block; + // If we don't have a block of pinned objects yet, or the current // one isn't large enough to hold the new object, allocate a new one. if (bd == NULL || (bd->free + n) > (bd->start + BLOCK_SIZE_W)) { - pinned_object_block = bd = allocBlock(); - dbl_link_onto(bd, &g0s0->large_objects); - g0s0->n_large_blocks++; - bd->gen_no = 0; - bd->step = g0s0; + ACQUIRE_SM_LOCK + cap->pinned_object_block = bd = allocBlock(); + RELEASE_SM_LOCK + stp = &nurseries[cap->no]; + dbl_link_onto(bd, &stp->large_objects); + stp->n_large_blocks++; + initBdescr(bd, stp); bd->flags = BF_PINNED | BF_LARGE; bd->free = bd->start; - alloc_blocks++; } p = bd->free; bd->free += n; - RELEASE_SM_LOCK; return p; } @@ -850,7 +779,7 @@ void setTSOLink (Capability *cap, StgTSO *tso, StgTSO *target) { bdescr *bd; - if ((tso->flags & (TSO_DIRTY|TSO_LINK_DIRTY)) == 0) { + if (tso->dirty == 0 && (tso->flags & TSO_LINK_DIRTY) == 0) { tso->flags |= TSO_LINK_DIRTY; bd = Bdescr((StgPtr)tso); if (bd->gen_no > 0) recordMutableCap((StgClosure*)tso,cap,bd->gen_no); @@ -862,11 +791,11 @@ void dirty_TSO (Capability *cap, StgTSO *tso) { bdescr *bd; - if ((tso->flags & (TSO_DIRTY|TSO_LINK_DIRTY)) == 0) { + if (tso->dirty == 0 && (tso->flags & TSO_LINK_DIRTY) == 0) { bd = Bdescr((StgPtr)tso); if (bd->gen_no > 0) recordMutableCap((StgClosure*)tso,cap,bd->gen_no); } - tso->flags |= TSO_DIRTY; + tso->dirty = 1; } /* @@ -887,61 +816,6 @@ dirty_MVAR(StgRegTable *reg, StgClosure *p) } /* ----------------------------------------------------------------------------- - Allocation functions for GMP. - - These all use the allocate() interface - we can't have any garbage - collection going on during a gmp operation, so we use allocate() - which always succeeds. The gmp operations which might need to - allocate will ask the storage manager (via doYouWantToGC()) whether - a garbage collection is required, in case we get into a loop doing - only allocate() style allocation. - -------------------------------------------------------------------------- */ - -static void * -stgAllocForGMP (size_t size_in_bytes) -{ - StgArrWords* arr; - nat data_size_in_words, total_size_in_words; - - /* round up to a whole number of words */ - data_size_in_words = (size_in_bytes + sizeof(W_) + 1) / sizeof(W_); - total_size_in_words = sizeofW(StgArrWords) + data_size_in_words; - - /* allocate and fill it in. */ -#if defined(THREADED_RTS) - arr = (StgArrWords *)allocateLocal(myTask()->cap, total_size_in_words); -#else - arr = (StgArrWords *)allocateLocal(&MainCapability, total_size_in_words); -#endif - SET_ARR_HDR(arr, &stg_ARR_WORDS_info, CCCS, data_size_in_words); - - /* and return a ptr to the goods inside the array */ - return arr->payload; -} - -static void * -stgReallocForGMP (void *ptr, size_t old_size, size_t new_size) -{ - void *new_stuff_ptr = stgAllocForGMP(new_size); - nat i = 0; - char *p = (char *) ptr; - char *q = (char *) new_stuff_ptr; - - for (; i < old_size; i++, p++, q++) { - *q = *p; - } - - return(new_stuff_ptr); -} - -static void -stgDeallocForGMP (void *ptr STG_UNUSED, - size_t size STG_UNUSED) -{ - /* easy for us: the garbage collector does the dealloc'n */ -} - -/* ----------------------------------------------------------------------------- * Stats and stuff * -------------------------------------------------------------------------- */ @@ -959,14 +833,11 @@ calcAllocated( void ) { nat allocated; bdescr *bd; + nat i; - allocated = allocatedBytes(); - allocated += countNurseryBlocks() * BLOCK_SIZE_W; + allocated = countNurseryBlocks() * BLOCK_SIZE_W; - { -#ifdef THREADED_RTS - nat i; - for (i = 0; i < n_nurseries; i++) { + for (i = 0; i < n_capabilities; i++) { Capability *cap; for ( bd = capabilities[i].r.rCurrentNursery->link; bd != NULL; bd = bd->link ) { @@ -978,18 +849,10 @@ calcAllocated( void ) allocated -= (cap->r.rCurrentNursery->start + BLOCK_SIZE_W) - cap->r.rCurrentNursery->free; } - } -#else - bdescr *current_nursery = MainCapability.r.rCurrentNursery; - - for ( bd = current_nursery->link; bd != NULL; bd = bd->link ) { - allocated -= BLOCK_SIZE_W; - } - if (current_nursery->free < current_nursery->start + BLOCK_SIZE_W) { - allocated -= (current_nursery->start + BLOCK_SIZE_W) - - current_nursery->free; - } -#endif + if (cap->pinned_object_block != NULL) { + allocated -= (cap->pinned_object_block->start + BLOCK_SIZE_W) - + cap->pinned_object_block->free; + } } total_allocated += allocated; @@ -1006,16 +869,12 @@ calcLiveBlocks(void) lnat live = 0; step *stp; - if (RtsFlags.GcFlags.generations == 1) { - return g0s0->n_large_blocks + g0s0->n_blocks; - } - for (g = 0; g < RtsFlags.GcFlags.generations; g++) { for (s = 0; s < generations[g].n_steps; s++) { /* approximate amount of live data (doesn't take into account slop * at end of each block). */ - if (g == 0 && s == 0) { + if (g == 0 && s == 0 && RtsFlags.GcFlags.generations > 1) { continue; } stp = &generations[g].steps[s]; @@ -1032,6 +891,7 @@ countOccupied(bdescr *bd) words = 0; for (; bd != NULL; bd = bd->link) { + ASSERT(bd->free <= bd->start + bd->blocks * BLOCK_SIZE_W); words += bd->free - bd->start; } return words; @@ -1046,14 +906,10 @@ calcLiveWords(void) lnat live; step *stp; - if (RtsFlags.GcFlags.generations == 1) { - return g0s0->n_words + countOccupied(g0s0->large_objects); - } - live = 0; for (g = 0; g < RtsFlags.GcFlags.generations; g++) { for (s = 0; s < generations[g].n_steps; s++) { - if (g == 0 && s == 0) continue; + if (g == 0 && s == 0 && RtsFlags.GcFlags.generations > 1) continue; stp = &generations[g].steps[s]; live += stp->n_words + countOccupied(stp->large_objects); } @@ -1079,14 +935,27 @@ calcNeeded(void) for (s = 0; s < generations[g].n_steps; s++) { if (g == 0 && s == 0) { continue; } stp = &generations[g].steps[s]; + + // we need at least this much space + needed += stp->n_blocks + stp->n_large_blocks; + + // any additional space needed to collect this gen next time? if (g == 0 || // always collect gen 0 (generations[g].steps[0].n_blocks + generations[g].steps[0].n_large_blocks - > generations[g].max_blocks - && stp->is_compacted == 0)) { - needed += 2 * stp->n_blocks + stp->n_large_blocks; - } else { - needed += stp->n_blocks + stp->n_large_blocks; + > generations[g].max_blocks)) { + // we will collect this gen next time + if (stp->mark) { + // bitmap: + needed += stp->n_blocks / BITS_IN(W_); + // mark stack: + needed += stp->n_blocks / 100; + } + if (stp->compact) { + continue; // no additional space needed for compaction + } else { + needed += stp->n_blocks; + } } } } @@ -1113,9 +982,37 @@ calcNeeded(void) should be modified to use allocateExec instead of VirtualAlloc. ------------------------------------------------------------------------- */ -static bdescr *exec_block; +#if defined(linux_HOST_OS) + +// On Linux we need to use libffi for allocating executable memory, +// because it knows how to work around the restrictions put in place +// by SELinux. + +void *allocateExec (nat bytes, void **exec_ret) +{ + void **ret, **exec; + ACQUIRE_SM_LOCK; + ret = ffi_closure_alloc (sizeof(void *) + (size_t)bytes, (void**)&exec); + RELEASE_SM_LOCK; + if (ret == NULL) return ret; + *ret = ret; // save the address of the writable mapping, for freeExec(). + *exec_ret = exec + 1; + return (ret + 1); +} + +// freeExec gets passed the executable address, not the writable address. +void freeExec (void *addr) +{ + void *writable; + writable = *((void**)addr - 1); + ACQUIRE_SM_LOCK; + ffi_closure_free (writable); + RELEASE_SM_LOCK +} -void *allocateExec (nat bytes) +#else + +void *allocateExec (nat bytes, void **exec_ret) { void *ret; nat n; @@ -1151,6 +1048,7 @@ void *allocateExec (nat bytes) exec_block->free += n + 1; RELEASE_SM_LOCK + *exec_ret = ret; return ret; } @@ -1188,6 +1086,8 @@ void freeExec (void *addr) RELEASE_SM_LOCK } +#endif /* mingw32_HOST_OS */ + /* ----------------------------------------------------------------------------- Debugging @@ -1252,6 +1152,51 @@ stepBlocks (step *stp) countAllocdBlocks(stp->large_objects); } +// If memInventory() calculates that we have a memory leak, this +// function will try to find the block(s) that are leaking by marking +// all the ones that we know about, and search through memory to find +// blocks that are not marked. In the debugger this can help to give +// us a clue about what kind of block leaked. In the future we might +// annotate blocks with their allocation site to give more helpful +// info. +static void +findMemoryLeak (void) +{ + nat g, s, i; + for (g = 0; g < RtsFlags.GcFlags.generations; g++) { + for (i = 0; i < n_capabilities; i++) { + markBlocks(capabilities[i].mut_lists[g]); + } + markBlocks(generations[g].mut_list); + for (s = 0; s < generations[g].n_steps; s++) { + markBlocks(generations[g].steps[s].blocks); + markBlocks(generations[g].steps[s].large_objects); + } + } + + for (i = 0; i < n_nurseries; i++) { + markBlocks(nurseries[i].blocks); + markBlocks(nurseries[i].large_objects); + } + +#ifdef PROFILING + // TODO: + // if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_RETAINER) { + // markRetainerBlocks(); + // } +#endif + + // count the blocks allocated by the arena allocator + // TODO: + // markArenaBlocks(); + + // count the blocks containing executable memory + markBlocks(exec_block); + + reportUnmarkedBlocks(); +} + + void memInventory (rtsBool show) { @@ -1308,6 +1253,7 @@ memInventory (rtsBool show) #define MB(n) (((n) * BLOCK_SIZE_W) / ((1024*1024)/sizeof(W_))) leak = live_blocks + free_blocks != mblocks_allocated * BLOCKS_PER_MBLOCK; + if (show || leak) { if (leak) { @@ -1336,6 +1282,13 @@ memInventory (rtsBool show) mblocks_allocated * BLOCKS_PER_MBLOCK, mblocks_allocated); } } + + if (leak) { + debugBelch("\n"); + findMemoryLeak(); + } + ASSERT(n_alloc_blocks == live_blocks); + ASSERT(!leak); } @@ -1345,35 +1298,36 @@ checkSanity( void ) { nat g, s; - if (RtsFlags.GcFlags.generations == 1) { - checkHeap(g0s0->blocks); - checkChain(g0s0->large_objects); - } else { - - for (g = 0; g < RtsFlags.GcFlags.generations; g++) { - for (s = 0; s < generations[g].n_steps; s++) { - if (g == 0 && s == 0) { continue; } - ASSERT(countBlocks(generations[g].steps[s].blocks) - == generations[g].steps[s].n_blocks); - ASSERT(countBlocks(generations[g].steps[s].large_objects) - == generations[g].steps[s].n_large_blocks); - checkHeap(generations[g].steps[s].blocks); - checkChain(generations[g].steps[s].large_objects); - if (g > 0) { - checkMutableList(generations[g].mut_list, g); - } - } - } - - for (s = 0; s < n_nurseries; s++) { - ASSERT(countBlocks(nurseries[s].blocks) - == nurseries[s].n_blocks); - ASSERT(countBlocks(nurseries[s].large_objects) - == nurseries[s].n_large_blocks); - } - - checkFreeListSanity(); + for (g = 0; g < RtsFlags.GcFlags.generations; g++) { + for (s = 0; s < generations[g].n_steps; s++) { + if (g == 0 && s == 0 && RtsFlags.GcFlags.generations > 1) { + continue; + } + ASSERT(countBlocks(generations[g].steps[s].blocks) + == generations[g].steps[s].n_blocks); + ASSERT(countBlocks(generations[g].steps[s].large_objects) + == generations[g].steps[s].n_large_blocks); + checkHeap(generations[g].steps[s].blocks); + checkLargeObjects(generations[g].steps[s].large_objects); + } } + + for (s = 0; s < n_nurseries; s++) { + ASSERT(countBlocks(nurseries[s].blocks) + == nurseries[s].n_blocks); + ASSERT(countBlocks(nurseries[s].large_objects) + == nurseries[s].n_large_blocks); + } + + checkFreeListSanity(); + +#if defined(THREADED_RTS) + // check the stacks too in threaded mode, because we don't do a + // full heap sanity check in this case (see checkHeap()) + checkMutableLists(rtsTrue); +#else + checkMutableLists(rtsFalse); +#endif } /* Nursery sanity check */