[project @ 2002-03-21 11:23:59 by sebc]
[ghc-hetmet.git] / ghc / rts / Storage.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Storage.c,v 1.60 2002/03/21 11:23:59 sebc Exp $
3  *
4  * (c) The GHC Team, 1998-1999
5  *
6  * Storage manager front end
7  *
8  * ---------------------------------------------------------------------------*/
9
10 #include "PosixSource.h"
11 #include "Rts.h"
12 #include "RtsUtils.h"
13 #include "RtsFlags.h"
14 #include "Stats.h"
15 #include "Hooks.h"
16 #include "BlockAlloc.h"
17 #include "MBlock.h"
18 #include "Weak.h"
19 #include "Sanity.h"
20 #include "Arena.h"
21
22 #include "Storage.h"
23 #include "Schedule.h"
24 #include "OSThreads.h"
25 #include "StoragePriv.h"
26
27 #include "RetainerProfile.h"    // for counting memory blocks (memInventory)
28
29 #ifdef darwin_TARGET_OS
30 #include <mach/mach.h>
31 #include <mach/task.h>
32 #include <mach/message.h>
33 #include <mach/vm_prot.h>
34 #include <mach/vm_region.h>
35 #include <mach-o/getsect.h>
36 unsigned long macho_etext = 0;
37 unsigned long macho_edata = 0;
38 #define IN_RANGE(base,size,x) (((P_)base) <= ((P_)x) && ((P_)x) < ((P_)((unsigned long)base + size)))
39 static void macosx_get_memory_layout(void)
40 {
41   vm_address_t address;
42   vm_size_t size;
43   struct vm_region_basic_info info;
44   mach_msg_type_number_t info_count;
45   mach_port_t object_name;
46   task_t task = mach_task_self();
47   P_ in_text = ((P_*)(&stg_BLACKHOLE_info))[0];
48   P_ in_data = (P_)&stg_dummy_ret_closure;
49
50   address = 0; /* VM_MIN_ADDRESS */
51   while (1) {
52     info_count = VM_REGION_BASIC_INFO_COUNT;
53     if (vm_region(task, &address, &size, VM_REGION_BASIC_INFO,
54                   (vm_region_info_t)&info, &info_count, &object_name)
55         != KERN_SUCCESS)
56       break;
57     if (IN_RANGE(address, size, in_text))
58       macho_etext = address + size;
59     if (IN_RANGE(address, size, in_data))
60       macho_edata = address + size;
61     address += size;
62   }
63 }
64 #endif
65
66 StgClosure    *caf_list         = NULL;
67
68 bdescr *small_alloc_list;       /* allocate()d small objects */
69 bdescr *large_alloc_list;       /* allocate()d large objects */
70 bdescr *pinned_object_block;    /* allocate pinned objects into this block */
71 nat alloc_blocks;               /* number of allocate()d blocks since GC */
72 nat alloc_blocks_lim;           /* approximate limit on alloc_blocks */
73
74 StgPtr alloc_Hp    = NULL;      /* next free byte in small_alloc_list */
75 StgPtr alloc_HpLim = NULL;      /* end of block at small_alloc_list   */
76
77 generation *generations;        /* all the generations */
78 generation *g0;                 /* generation 0, for convenience */
79 generation *oldest_gen;         /* oldest generation, for convenience */
80 step *g0s0;                     /* generation 0, step 0, for convenience */
81
82 lnat total_allocated = 0;       /* total memory allocated during run */
83
84 /*
85  * Storage manager mutex:  protects all the above state from
86  * simultaneous access by two STG threads.
87  */
88 #ifdef SMP
89 Mutex sm_mutex = INIT_MUTEX_VAR;
90 #endif
91
92 /*
93  * Forward references
94  */
95 static void *stgAllocForGMP   (size_t size_in_bytes);
96 static void *stgReallocForGMP (void *ptr, size_t old_size, size_t new_size);
97 static void  stgDeallocForGMP (void *ptr, size_t size);
98
99 void
100 initStorage( void )
101 {
102   nat g, s;
103   step *stp;
104   generation *gen;
105
106 #if defined(darwin_TARGET_OS)
107     macosx_get_memory_layout();
108 #endif
109
110     /* Sanity check to make sure we are able to make the distinction
111      * between closures and infotables
112      */
113   if (!LOOKS_LIKE_GHC_INFO(&stg_BLACKHOLE_info)) {
114     barf("LOOKS_LIKE_GHC_INFO+ is incorrectly defined");
115     exit(0);
116   }
117   if (LOOKS_LIKE_GHC_INFO(&stg_dummy_ret_closure)) {
118     barf("LOOKS_LIKE_GHC_INFO- is incorrectly defined");
119     exit(0);
120   }
121   if (LOOKS_LIKE_STATIC_CLOSURE(&stg_BLACKHOLE_info)) {
122     barf("LOOKS_LIKE_STATIC_CLOSURE- is incorrectly defined");
123     exit(0);
124   }
125   if (!LOOKS_LIKE_STATIC_CLOSURE(&stg_dummy_ret_closure)) {
126     barf("LOOKS_LIKE_STATIC_CLOSURE+ is incorrectly defined");
127     exit(0);
128   }
129
130   if (RtsFlags.GcFlags.maxHeapSize != 0 &&
131       RtsFlags.GcFlags.heapSizeSuggestion > 
132       RtsFlags.GcFlags.maxHeapSize) {
133     RtsFlags.GcFlags.maxHeapSize = RtsFlags.GcFlags.heapSizeSuggestion;
134   }
135
136   if (RtsFlags.GcFlags.maxHeapSize != 0 &&
137       RtsFlags.GcFlags.minAllocAreaSize > 
138       RtsFlags.GcFlags.maxHeapSize) {
139       prog_belch("maximum heap size (-M) is smaller than minimum alloc area size (-A)");
140       exit(1);
141   }
142
143   initBlockAllocator();
144   
145 #if defined(SMP)
146   initCondition(&sm_mutex);
147 #endif
148
149   /* allocate generation info array */
150   generations = (generation *)stgMallocBytes(RtsFlags.GcFlags.generations 
151                                              * sizeof(struct _generation),
152                                              "initStorage: gens");
153
154   /* Initialise all generations */
155   for(g = 0; g < RtsFlags.GcFlags.generations; g++) {
156     gen = &generations[g];
157     gen->no = g;
158     gen->mut_list = END_MUT_LIST;
159     gen->mut_once_list = END_MUT_LIST;
160     gen->collections = 0;
161     gen->failed_promotions = 0;
162     gen->max_blocks = 0;
163   }
164
165   /* A couple of convenience pointers */
166   g0 = &generations[0];
167   oldest_gen = &generations[RtsFlags.GcFlags.generations-1];
168
169   /* Allocate step structures in each generation */
170   if (RtsFlags.GcFlags.generations > 1) {
171     /* Only for multiple-generations */
172
173     /* Oldest generation: one step */
174     oldest_gen->n_steps = 1;
175     oldest_gen->steps = 
176       stgMallocBytes(1 * sizeof(struct _step), "initStorage: last step");
177
178     /* set up all except the oldest generation with 2 steps */
179     for(g = 0; g < RtsFlags.GcFlags.generations-1; g++) {
180       generations[g].n_steps = RtsFlags.GcFlags.steps;
181       generations[g].steps  = 
182         stgMallocBytes (RtsFlags.GcFlags.steps * sizeof(struct _step),
183                         "initStorage: steps");
184     }
185     
186   } else {
187     /* single generation, i.e. a two-space collector */
188     g0->n_steps = 1;
189     g0->steps = stgMallocBytes (sizeof(struct _step), "initStorage: steps");
190   }
191
192   /* Initialise all steps */
193   for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
194     for (s = 0; s < generations[g].n_steps; s++) {
195       stp = &generations[g].steps[s];
196       stp->no = s;
197       stp->blocks = NULL;
198       stp->n_blocks = 0;
199       stp->gen = &generations[g];
200       stp->gen_no = g;
201       stp->hp = NULL;
202       stp->hpLim = NULL;
203       stp->hp_bd = NULL;
204       stp->scan = NULL;
205       stp->scan_bd = NULL;
206       stp->large_objects = NULL;
207       stp->n_large_blocks = 0;
208       stp->new_large_objects = NULL;
209       stp->scavenged_large_objects = NULL;
210       stp->n_scavenged_large_blocks = 0;
211       stp->is_compacted = 0;
212       stp->bitmap = NULL;
213     }
214   }
215   
216   /* Set up the destination pointers in each younger gen. step */
217   for (g = 0; g < RtsFlags.GcFlags.generations-1; g++) {
218     for (s = 0; s < generations[g].n_steps-1; s++) {
219       generations[g].steps[s].to = &generations[g].steps[s+1];
220     }
221     generations[g].steps[s].to = &generations[g+1].steps[0];
222   }
223   
224   /* The oldest generation has one step and it is compacted. */
225   if (RtsFlags.GcFlags.compact) {
226       if (RtsFlags.GcFlags.generations == 1) {
227           belch("WARNING: compaction is incompatible with -G1; disabled");
228       } else {
229           oldest_gen->steps[0].is_compacted = 1;
230       }
231   }
232   oldest_gen->steps[0].to = &oldest_gen->steps[0];
233
234   /* generation 0 is special: that's the nursery */
235   generations[0].max_blocks = 0;
236
237   /* G0S0: the allocation area.  Policy: keep the allocation area
238    * small to begin with, even if we have a large suggested heap
239    * size.  Reason: we're going to do a major collection first, and we
240    * don't want it to be a big one.  This vague idea is borne out by 
241    * rigorous experimental evidence.
242    */
243   g0s0 = &generations[0].steps[0];
244
245   allocNurseries();
246
247   weak_ptr_list = NULL;
248   caf_list = NULL;
249    
250   /* initialise the allocate() interface */
251   small_alloc_list = NULL;
252   large_alloc_list = NULL;
253   alloc_blocks = 0;
254   alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize;
255
256   /* Tell GNU multi-precision pkg about our custom alloc functions */
257   mp_set_memory_functions(stgAllocForGMP, stgReallocForGMP, stgDeallocForGMP);
258
259 #if defined(SMP)
260   initMutex(&sm_mutex);
261 #endif
262
263   IF_DEBUG(gc, statDescribeGens());
264 }
265
266 void
267 exitStorage (void)
268 {
269     stat_exit(calcAllocated());
270 }
271
272 /* -----------------------------------------------------------------------------
273    CAF management.
274
275    The entry code for every CAF does the following:
276      
277       - builds a CAF_BLACKHOLE in the heap
278       - pushes an update frame pointing to the CAF_BLACKHOLE
279       - invokes UPD_CAF(), which:
280           - calls newCaf, below
281           - updates the CAF with a static indirection to the CAF_BLACKHOLE
282       
283    Why do we build a BLACKHOLE in the heap rather than just updating
284    the thunk directly?  It's so that we only need one kind of update
285    frame - otherwise we'd need a static version of the update frame too.
286
287    newCaf() does the following:
288        
289       - it puts the CAF on the oldest generation's mut-once list.
290         This is so that we can treat the CAF as a root when collecting
291         younger generations.
292
293    For GHCI, we have additional requirements when dealing with CAFs:
294
295       - we must *retain* all dynamically-loaded CAFs ever entered,
296         just in case we need them again.
297       - we must be able to *revert* CAFs that have been evaluated, to
298         their pre-evaluated form.
299
300       To do this, we use an additional CAF list.  When newCaf() is
301       called on a dynamically-loaded CAF, we add it to the CAF list
302       instead of the old-generation mutable list, and save away its
303       old info pointer (in caf->saved_info) for later reversion.
304
305       To revert all the CAFs, we traverse the CAF list and reset the
306       info pointer to caf->saved_info, then throw away the CAF list.
307       (see GC.c:revertCAFs()).
308
309       -- SDM 29/1/01
310
311    -------------------------------------------------------------------------- */
312
313 void
314 newCAF(StgClosure* caf)
315 {
316   /* Put this CAF on the mutable list for the old generation.
317    * This is a HACK - the IND_STATIC closure doesn't really have
318    * a mut_link field, but we pretend it has - in fact we re-use
319    * the STATIC_LINK field for the time being, because when we
320    * come to do a major GC we won't need the mut_link field
321    * any more and can use it as a STATIC_LINK.
322    */
323   ACQUIRE_SM_LOCK;
324
325   if (is_dynamically_loaded_rwdata_ptr((StgPtr)caf)) {
326       ((StgIndStatic *)caf)->saved_info  = (StgInfoTable *)caf->header.info;
327       ((StgIndStatic *)caf)->static_link = caf_list;
328       caf_list = caf;
329   } else {
330       ((StgIndStatic *)caf)->saved_info = NULL;
331       ((StgMutClosure *)caf)->mut_link = oldest_gen->mut_once_list;
332       oldest_gen->mut_once_list = (StgMutClosure *)caf;
333   }
334
335   RELEASE_SM_LOCK;
336
337 #ifdef PAR
338   /* If we are PAR or DIST then  we never forget a CAF */
339   { globalAddr *newGA;
340     //belch("<##> Globalising CAF %08x %s",caf,info_type(caf));
341     newGA=makeGlobal(caf,rtsTrue); /*given full weight*/
342     ASSERT(newGA);
343   } 
344 #endif /* PAR */
345 }
346
347 /* -----------------------------------------------------------------------------
348    Nursery management.
349    -------------------------------------------------------------------------- */
350
351 void
352 allocNurseries( void )
353
354 #ifdef SMP
355   {
356     Capability *cap;
357     bdescr *bd;
358
359     g0s0->blocks = NULL;
360     g0s0->n_blocks = 0;
361     for (cap = free_capabilities; cap != NULL; cap = cap->link) {
362       cap->r.rNursery = allocNursery(NULL, RtsFlags.GcFlags.minAllocAreaSize);
363       cap->r.rCurrentNursery = cap->r.rNursery;
364       for (bd = cap->r.rNursery; bd != NULL; bd = bd->link) {
365         bd->u.back = (bdescr *)cap;
366       }
367     }
368     /* Set the back links to be equal to the Capability,
369      * so we can do slightly better informed locking.
370      */
371   }
372 #else /* SMP */
373   g0s0->blocks      = allocNursery(NULL, RtsFlags.GcFlags.minAllocAreaSize);
374   g0s0->n_blocks    = RtsFlags.GcFlags.minAllocAreaSize;
375   g0s0->to_blocks   = NULL;
376   g0s0->n_to_blocks = 0;
377   MainCapability.r.rNursery        = g0s0->blocks;
378   MainCapability.r.rCurrentNursery = g0s0->blocks;
379   /* hp, hpLim, hp_bd, to_space etc. aren't used in G0S0 */
380 #endif
381 }
382       
383 void
384 resetNurseries( void )
385 {
386   bdescr *bd;
387 #ifdef SMP
388   Capability *cap;
389   
390   /* All tasks must be stopped */
391   ASSERT(n_free_capabilities == RtsFlags.ParFlags.nNodes);
392
393   for (cap = free_capabilities; cap != NULL; cap = cap->link) {
394     for (bd = cap->r.rNursery; bd; bd = bd->link) {
395       bd->free = bd->start;
396       ASSERT(bd->gen_no == 0);
397       ASSERT(bd->step == g0s0);
398       IF_DEBUG(sanity,memset(bd->start, 0xaa, BLOCK_SIZE));
399     }
400     cap->r.rCurrentNursery = cap->r.rNursery;
401   }
402 #else
403   for (bd = g0s0->blocks; bd; bd = bd->link) {
404     bd->free = bd->start;
405     ASSERT(bd->gen_no == 0);
406     ASSERT(bd->step == g0s0);
407     IF_DEBUG(sanity,memset(bd->start, 0xaa, BLOCK_SIZE));
408   }
409   MainCapability.r.rNursery = g0s0->blocks;
410   MainCapability.r.rCurrentNursery = g0s0->blocks;
411 #endif
412 }
413
414 bdescr *
415 allocNursery (bdescr *tail, nat blocks)
416 {
417   bdescr *bd;
418   nat i;
419
420   // Allocate a nursery: we allocate fresh blocks one at a time and
421   // cons them on to the front of the list, not forgetting to update
422   // the back pointer on the tail of the list to point to the new block.
423   for (i=0; i < blocks; i++) {
424     // @LDV profiling
425     /*
426       processNursery() in LdvProfile.c assumes that every block group in
427       the nursery contains only a single block. So, if a block group is
428       given multiple blocks, change processNursery() accordingly.
429      */
430     bd = allocBlock();
431     bd->link = tail;
432     // double-link the nursery: we might need to insert blocks
433     if (tail != NULL) {
434         tail->u.back = bd;
435     }
436     bd->step = g0s0;
437     bd->gen_no = 0;
438     bd->flags = 0;
439     bd->free = bd->start;
440     tail = bd;
441   }
442   tail->u.back = NULL;
443   return tail;
444 }
445
446 void
447 resizeNursery ( nat blocks )
448 {
449   bdescr *bd;
450   nat nursery_blocks;
451
452 #ifdef SMP
453   barf("resizeNursery: can't resize in SMP mode");
454 #endif
455
456   nursery_blocks = g0s0->n_blocks;
457   if (nursery_blocks == blocks) {
458     return;
459   }
460
461   else if (nursery_blocks < blocks) {
462     IF_DEBUG(gc, fprintf(stderr, "Increasing size of nursery to %d blocks\n", 
463                          blocks));
464     g0s0->blocks = allocNursery(g0s0->blocks, blocks-nursery_blocks);
465   } 
466
467   else {
468     bdescr *next_bd;
469     
470     IF_DEBUG(gc, fprintf(stderr, "Decreasing size of nursery to %d blocks\n", 
471                          blocks));
472
473     bd = g0s0->blocks;
474     while (nursery_blocks > blocks) {
475         next_bd = bd->link;
476         next_bd->u.back = NULL;
477         nursery_blocks -= bd->blocks; // might be a large block
478         freeGroup(bd);
479         bd = next_bd;
480     }
481     g0s0->blocks = bd;
482     // might have gone just under, by freeing a large block, so make
483     // up the difference.
484     if (nursery_blocks < blocks) {
485         g0s0->blocks = allocNursery(g0s0->blocks, blocks-nursery_blocks);
486     }
487   }
488   
489   g0s0->n_blocks = blocks;
490   ASSERT(countBlocks(g0s0->blocks) == g0s0->n_blocks);
491 }
492
493 /* -----------------------------------------------------------------------------
494    The allocate() interface
495
496    allocate(n) always succeeds, and returns a chunk of memory n words
497    long.  n can be larger than the size of a block if necessary, in
498    which case a contiguous block group will be allocated.
499    -------------------------------------------------------------------------- */
500
501 StgPtr
502 allocate( nat n )
503 {
504   bdescr *bd;
505   StgPtr p;
506
507   ACQUIRE_SM_LOCK;
508
509   TICK_ALLOC_HEAP_NOCTR(n);
510   CCS_ALLOC(CCCS,n);
511
512   /* big allocation (>LARGE_OBJECT_THRESHOLD) */
513   /* ToDo: allocate directly into generation 1 */
514   if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) {
515     nat req_blocks =  (lnat)BLOCK_ROUND_UP(n*sizeof(W_)) / BLOCK_SIZE;
516     bd = allocGroup(req_blocks);
517     dbl_link_onto(bd, &g0s0->large_objects);
518     bd->gen_no  = 0;
519     bd->step = g0s0;
520     bd->flags = BF_LARGE;
521     bd->free = bd->start;
522     /* don't add these blocks to alloc_blocks, since we're assuming
523      * that large objects are likely to remain live for quite a while
524      * (eg. running threads), so garbage collecting early won't make
525      * much difference.
526      */
527     alloc_blocks += req_blocks;
528     RELEASE_SM_LOCK;
529     return bd->start;
530
531   /* small allocation (<LARGE_OBJECT_THRESHOLD) */
532   } else if (small_alloc_list == NULL || alloc_Hp + n > alloc_HpLim) {
533     if (small_alloc_list) {
534       small_alloc_list->free = alloc_Hp;
535     }
536     bd = allocBlock();
537     bd->link = small_alloc_list;
538     small_alloc_list = bd;
539     bd->gen_no = 0;
540     bd->step = g0s0;
541     bd->flags = 0;
542     alloc_Hp = bd->start;
543     alloc_HpLim = bd->start + BLOCK_SIZE_W;
544     alloc_blocks++;
545   }
546
547   p = alloc_Hp;
548   alloc_Hp += n;
549   RELEASE_SM_LOCK;
550   return p;
551 }
552
553 lnat
554 allocated_bytes( void )
555 {
556   return (alloc_blocks * BLOCK_SIZE_W - (alloc_HpLim - alloc_Hp));
557 }
558
559 /* ---------------------------------------------------------------------------
560    Allocate a fixed/pinned object.
561
562    We allocate small pinned objects into a single block, allocating a
563    new block when the current one overflows.  The block is chained
564    onto the large_object_list of generation 0 step 0.
565
566    NOTE: The GC can't in general handle pinned objects.  This
567    interface is only safe to use for ByteArrays, which have no
568    pointers and don't require scavenging.  It works because the
569    block's descriptor has the BF_LARGE flag set, so the block is
570    treated as a large object and chained onto various lists, rather
571    than the individual objects being copied.  However, when it comes
572    to scavenge the block, the GC will only scavenge the first object.
573    The reason is that the GC can't linearly scan a block of pinned
574    objects at the moment (doing so would require using the
575    mostly-copying techniques).  But since we're restricting ourselves
576    to pinned ByteArrays, not scavenging is ok.
577
578    This function is called by newPinnedByteArray# which immediately
579    fills the allocated memory with a MutableByteArray#.
580    ------------------------------------------------------------------------- */
581
582 StgPtr
583 allocatePinned( nat n )
584 {
585     StgPtr p;
586     bdescr *bd = pinned_object_block;
587
588     ACQUIRE_SM_LOCK;
589     
590     TICK_ALLOC_HEAP_NOCTR(n);
591     CCS_ALLOC(CCCS,n);
592
593     // If the request is for a large object, then allocate()
594     // will give us a pinned object anyway.
595     if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) {
596         RELEASE_SM_LOCK;
597         return allocate(n);
598     }
599
600     // If we don't have a block of pinned objects yet, or the current
601     // one isn't large enough to hold the new object, allocate a new one.
602     if (bd == NULL || (bd->free + n) > (bd->start + BLOCK_SIZE_W)) {
603         pinned_object_block = bd = allocBlock();
604         dbl_link_onto(bd, &g0s0->large_objects);
605         bd->gen_no = 0;
606         bd->step   = g0s0;
607         bd->flags  = BF_LARGE;
608         bd->free   = bd->start;
609         alloc_blocks++;
610     }
611
612     p = bd->free;
613     bd->free += n;
614     RELEASE_SM_LOCK;
615     return p;
616 }
617
618 /* -----------------------------------------------------------------------------
619    Allocation functions for GMP.
620
621    These all use the allocate() interface - we can't have any garbage
622    collection going on during a gmp operation, so we use allocate()
623    which always succeeds.  The gmp operations which might need to
624    allocate will ask the storage manager (via doYouWantToGC()) whether
625    a garbage collection is required, in case we get into a loop doing
626    only allocate() style allocation.
627    -------------------------------------------------------------------------- */
628
629 static void *
630 stgAllocForGMP (size_t size_in_bytes)
631 {
632   StgArrWords* arr;
633   nat data_size_in_words, total_size_in_words;
634   
635   /* should be a multiple of sizeof(StgWord) (whole no. of limbs) */
636   ASSERT(size_in_bytes % sizeof(W_) == 0);
637   
638   data_size_in_words  = size_in_bytes / sizeof(W_);
639   total_size_in_words = sizeofW(StgArrWords) + data_size_in_words;
640   
641   /* allocate and fill it in. */
642   arr = (StgArrWords *)allocate(total_size_in_words);
643   SET_ARR_HDR(arr, &stg_ARR_WORDS_info, CCCS, data_size_in_words);
644   
645   /* and return a ptr to the goods inside the array */
646   return(BYTE_ARR_CTS(arr));
647 }
648
649 static void *
650 stgReallocForGMP (void *ptr, size_t old_size, size_t new_size)
651 {
652     void *new_stuff_ptr = stgAllocForGMP(new_size);
653     nat i = 0;
654     char *p = (char *) ptr;
655     char *q = (char *) new_stuff_ptr;
656
657     for (; i < old_size; i++, p++, q++) {
658         *q = *p;
659     }
660
661     return(new_stuff_ptr);
662 }
663
664 static void
665 stgDeallocForGMP (void *ptr STG_UNUSED, 
666                   size_t size STG_UNUSED)
667 {
668     /* easy for us: the garbage collector does the dealloc'n */
669 }
670
671 /* -----------------------------------------------------------------------------
672  * Stats and stuff
673  * -------------------------------------------------------------------------- */
674
675 /* -----------------------------------------------------------------------------
676  * calcAllocated()
677  *
678  * Approximate how much we've allocated: number of blocks in the
679  * nursery + blocks allocated via allocate() - unused nusery blocks.
680  * This leaves a little slop at the end of each block, and doesn't
681  * take into account large objects (ToDo).
682  * -------------------------------------------------------------------------- */
683
684 lnat
685 calcAllocated( void )
686 {
687   nat allocated;
688   bdescr *bd;
689
690 #ifdef SMP
691   Capability *cap;
692
693   /* All tasks must be stopped.  Can't assert that all the
694      capabilities are owned by the scheduler, though: one or more
695      tasks might have been stopped while they were running (non-main)
696      threads. */
697   /*  ASSERT(n_free_capabilities == RtsFlags.ParFlags.nNodes); */
698
699   allocated = 
700     n_free_capabilities * RtsFlags.GcFlags.minAllocAreaSize * BLOCK_SIZE_W
701     + allocated_bytes();
702
703   for (cap = free_capabilities; cap != NULL; cap = cap->link) {
704     for ( bd = cap->r.rCurrentNursery->link; bd != NULL; bd = bd->link ) {
705       allocated -= BLOCK_SIZE_W;
706     }
707     if (cap->r.rCurrentNursery->free < cap->r.rCurrentNursery->start 
708         + BLOCK_SIZE_W) {
709       allocated -= (cap->r.rCurrentNursery->start + BLOCK_SIZE_W)
710         - cap->r.rCurrentNursery->free;
711     }
712   }
713
714 #else /* !SMP */
715   bdescr *current_nursery = MainCapability.r.rCurrentNursery;
716
717   allocated = (g0s0->n_blocks * BLOCK_SIZE_W) + allocated_bytes();
718   for ( bd = current_nursery->link; bd != NULL; bd = bd->link ) {
719     allocated -= BLOCK_SIZE_W;
720   }
721   if (current_nursery->free < current_nursery->start + BLOCK_SIZE_W) {
722     allocated -= (current_nursery->start + BLOCK_SIZE_W)
723       - current_nursery->free;
724   }
725 #endif
726
727   total_allocated += allocated;
728   return allocated;
729 }  
730
731 /* Approximate the amount of live data in the heap.  To be called just
732  * after garbage collection (see GarbageCollect()).
733  */
734 extern lnat 
735 calcLive(void)
736 {
737   nat g, s;
738   lnat live = 0;
739   step *stp;
740
741   if (RtsFlags.GcFlags.generations == 1) {
742     live = (g0s0->n_to_blocks - 1) * BLOCK_SIZE_W + 
743       ((lnat)g0s0->hp_bd->free - (lnat)g0s0->hp_bd->start) / sizeof(W_);
744     return live;
745   }
746
747   for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
748     for (s = 0; s < generations[g].n_steps; s++) {
749       /* approximate amount of live data (doesn't take into account slop
750        * at end of each block).
751        */
752       if (g == 0 && s == 0) { 
753           continue; 
754       }
755       stp = &generations[g].steps[s];
756       live += (stp->n_large_blocks + stp->n_blocks - 1) * BLOCK_SIZE_W;
757       if (stp->hp_bd != NULL) {
758           live += ((lnat)stp->hp_bd->free - (lnat)stp->hp_bd->start) 
759               / sizeof(W_);
760       }
761     }
762   }
763   return live;
764 }
765
766 /* Approximate the number of blocks that will be needed at the next
767  * garbage collection.
768  *
769  * Assume: all data currently live will remain live.  Steps that will
770  * be collected next time will therefore need twice as many blocks
771  * since all the data will be copied.
772  */
773 extern lnat 
774 calcNeeded(void)
775 {
776     lnat needed = 0;
777     nat g, s;
778     step *stp;
779     
780     for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
781         for (s = 0; s < generations[g].n_steps; s++) {
782             if (g == 0 && s == 0) { continue; }
783             stp = &generations[g].steps[s];
784             if (generations[g].steps[0].n_blocks +
785                 generations[g].steps[0].n_large_blocks 
786                 > generations[g].max_blocks
787                 && stp->is_compacted == 0) {
788                 needed += 2 * stp->n_blocks;
789             } else {
790                 needed += stp->n_blocks;
791             }
792         }
793     }
794     return needed;
795 }
796
797 /* -----------------------------------------------------------------------------
798    Debugging
799
800    memInventory() checks for memory leaks by counting up all the
801    blocks we know about and comparing that to the number of blocks
802    allegedly floating around in the system.
803    -------------------------------------------------------------------------- */
804
805 #ifdef DEBUG
806
807 void
808 memInventory(void)
809 {
810   nat g, s;
811   step *stp;
812   bdescr *bd;
813   lnat total_blocks = 0, free_blocks = 0;
814
815   /* count the blocks we current have */
816
817   for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
818     for (s = 0; s < generations[g].n_steps; s++) {
819       stp = &generations[g].steps[s];
820       total_blocks += stp->n_blocks;
821       if (RtsFlags.GcFlags.generations == 1) {
822         /* two-space collector has a to-space too :-) */
823         total_blocks += g0s0->n_to_blocks;
824       }
825       for (bd = stp->large_objects; bd; bd = bd->link) {
826         total_blocks += bd->blocks;
827         /* hack for megablock groups: they have an extra block or two in
828            the second and subsequent megablocks where the block
829            descriptors would normally go.
830         */
831         if (bd->blocks > BLOCKS_PER_MBLOCK) {
832           total_blocks -= (MBLOCK_SIZE / BLOCK_SIZE - BLOCKS_PER_MBLOCK)
833                           * (bd->blocks/(MBLOCK_SIZE/BLOCK_SIZE));
834         }
835       }
836     }
837   }
838
839   /* any blocks held by allocate() */
840   for (bd = small_alloc_list; bd; bd = bd->link) {
841     total_blocks += bd->blocks;
842   }
843   for (bd = large_alloc_list; bd; bd = bd->link) {
844     total_blocks += bd->blocks;
845   }
846
847 #ifdef PROFILING
848   if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_RETAINER) {
849     for (bd = firstStack; bd != NULL; bd = bd->link) 
850       total_blocks += bd->blocks;
851   }
852 #endif
853
854   // count the blocks allocated by the arena allocator
855   total_blocks += arenaBlocks();
856
857   /* count the blocks on the free list */
858   free_blocks = countFreeList();
859
860   if (total_blocks + free_blocks != mblocks_allocated *
861       BLOCKS_PER_MBLOCK) {
862     fprintf(stderr, "Blocks: %ld live + %ld free  = %ld total (%ld around)\n",
863             total_blocks, free_blocks, total_blocks + free_blocks,
864             mblocks_allocated * BLOCKS_PER_MBLOCK);
865   }
866
867   ASSERT(total_blocks + free_blocks == mblocks_allocated * BLOCKS_PER_MBLOCK);
868 }
869
870
871 nat
872 countBlocks(bdescr *bd)
873 {
874     nat n;
875     for (n=0; bd != NULL; bd=bd->link) {
876         n += bd->blocks;
877     }
878     return n;
879 }
880
881 /* Full heap sanity check. */
882 void
883 checkSanity( void )
884 {
885     nat g, s;
886
887     if (RtsFlags.GcFlags.generations == 1) {
888         checkHeap(g0s0->to_blocks);
889         checkChain(g0s0->large_objects);
890     } else {
891         
892         for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
893             for (s = 0; s < generations[g].n_steps; s++) {
894                 ASSERT(countBlocks(generations[g].steps[s].blocks)
895                        == generations[g].steps[s].n_blocks);
896                 ASSERT(countBlocks(generations[g].steps[s].large_objects)
897                        == generations[g].steps[s].n_large_blocks);
898                 if (g == 0 && s == 0) { continue; }
899                 checkHeap(generations[g].steps[s].blocks);
900                 checkChain(generations[g].steps[s].large_objects);
901                 if (g > 0) {
902                     checkMutableList(generations[g].mut_list, g);
903                     checkMutOnceList(generations[g].mut_once_list, g);
904                 }
905             }
906         }
907         checkFreeListSanity();
908     }
909 }
910
911 // handy function for use in gdb, because Bdescr() is inlined.
912 extern bdescr *_bdescr( StgPtr p );
913
914 bdescr *
915 _bdescr( StgPtr p )
916 {
917     return Bdescr(p);
918 }
919
920 #endif