ba9220ac42638e60ccf180ecdb0e5565ef0cb7c9
[ghc-hetmet.git] / rts / sm / BlockAlloc.c
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team 1998-2008
4  * 
5  * The block allocator and free list manager.
6  *
7  * This is the architecture independent part of the block allocator.
8  * It requires only the following support from the operating system: 
9  *
10  *    void *getMBlock(nat n);
11  *
12  * returns the address of an n*MBLOCK_SIZE region of memory, aligned on
13  * an MBLOCK_SIZE boundary.  There are no other restrictions on the
14  * addresses of memory returned by getMBlock().
15  *
16  * ---------------------------------------------------------------------------*/
17
18 #include "PosixSource.h"
19 #include "Rts.h"
20
21 #include "Storage.h"
22 #include "RtsUtils.h"
23 #include "BlockAlloc.h"
24
25 #include <string.h>
26
27 static void  initMBlock(void *mblock);
28
29 /* -----------------------------------------------------------------------------
30
31   Implementation notes
32   ~~~~~~~~~~~~~~~~~~~~
33
34   Terminology:
35     - bdescr = block descriptor
36     - bgroup = block group (1 or more adjacent blocks)
37     - mblock = mega block
38     - mgroup = mega group (1 or more adjacent mblocks)
39
40    Invariants on block descriptors
41    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42    bd->start always points to the start of the block.
43
44    bd->free is either:
45       - zero for a non-group-head; bd->link points to the head
46       - (-1) for the head of a free block group
47       - or it points within the block
48
49    bd->blocks is either:
50       - zero for a non-group-head; bd->link points to the head
51       - number of blocks in this group otherwise
52
53    bd->link either points to a block descriptor or is NULL
54
55    The following fields are not used by the allocator:
56      bd->flags
57      bd->gen_no
58      bd->gen
59      bd->dest
60
61   Exceptions: we don't maintain invariants for all the blocks within a
62   group on the free list, because it is expensive to modify every
63   bdescr in a group when coalescing.  Just the head and last bdescrs
64   will be correct for a group on the free list.
65
66
67   Free lists
68   ~~~~~~~~~~
69
70   Preliminaries:
71     - most allocations are for a small number of blocks
72     - sometimes the OS gives us new memory backwards in the address
73       space, sometimes forwards, so we should not be biased towards
74       any particular layout in the address space
75     - We want to avoid fragmentation
76     - We want allocation and freeing to be O(1) or close.
77
78   Coalescing trick: when a bgroup is freed (freeGroup()), we can check
79   whether it can be coalesced with other free bgroups by checking the
80   bdescrs for the blocks on either side of it.  This means that we can
81   coalesce in O(1) time.  Every free bgroup must have its head and tail
82   bdescrs initialised, the rest don't matter.
83
84   We keep the free list in buckets, using a heap-sort strategy.
85   Bucket N contains blocks with sizes 2^N - 2^(N+1)-1.  The list of
86   blocks in each bucket is doubly-linked, so that if a block is
87   coalesced we can easily remove it from its current free list.
88
89   To allocate a new block of size S, grab a block from bucket
90   log2ceiling(S) (i.e. log2() rounded up), in which all blocks are at
91   least as big as S, and split it if necessary.  If there are no
92   blocks in that bucket, look at bigger buckets until a block is found
93   Allocation is therefore O(logN) time.
94
95   To free a block:
96     - coalesce it with neighbours.
97     - remove coalesced neighbour(s) from free list(s)
98     - add the new (coalesced) block to the front of the appropriate
99       bucket, given by log2(S) where S is the size of the block.
100
101   Free is O(1).
102
103   We cannot play this coalescing trick with mblocks, because there is
104   no requirement that the bdescrs in the second and subsequent mblock
105   of an mgroup are initialised (the mgroup might be filled with a
106   large array, overwriting the bdescrs for example).
107
108   So there is a separate free list for megablocks, sorted in *address*
109   order, so that we can coalesce.  Allocation in this list is best-fit
110   by traversing the whole list: we don't expect this list to be long,
111   and allocation/freeing of large blocks is rare; avoiding
112   fragmentation is more important than performance here.
113
114   freeGroup() might end up moving a block from free_list to
115   free_mblock_list, if after coalescing we end up with a full mblock.
116
117   checkFreeListSanity() checks all the invariants on the free lists.
118
119   --------------------------------------------------------------------------- */
120
121 /* ---------------------------------------------------------------------------
122    WATCH OUT FOR OVERFLOW
123
124    Be very careful with integer overflow here.  If you have an
125    expression like (n_blocks * BLOCK_SIZE), and n_blocks is an int or
126    a nat, then it will very likely overflow on a 64-bit platform.
127    Always cast to StgWord (or W_ for short) first: ((W_)n_blocks * BLOCK_SIZE).
128
129   --------------------------------------------------------------------------- */
130
131 #define MAX_FREE_LIST 9
132
133 // In THREADED_RTS mode, the free list is protected by sm_mutex.
134
135 static bdescr *free_list[MAX_FREE_LIST];
136 static bdescr *free_mblock_list;
137
138 // free_list[i] contains blocks that are at least size 2^i, and at
139 // most size 2^(i+1) - 1.  
140 // 
141 // To find the free list in which to place a block, use log_2(size).
142 // To find a free block of the right size, use log_2_ceil(size).
143
144 lnat n_alloc_blocks;   // currently allocated blocks
145 lnat hw_alloc_blocks;  // high-water allocated blocks
146
147 /* -----------------------------------------------------------------------------
148    Initialisation
149    -------------------------------------------------------------------------- */
150
151 void initBlockAllocator(void)
152 {
153     nat i;
154     for (i=0; i < MAX_FREE_LIST; i++) {
155         free_list[i] = NULL;
156     }
157     free_mblock_list = NULL;
158     n_alloc_blocks = 0;
159     hw_alloc_blocks = 0;
160 }
161
162 /* -----------------------------------------------------------------------------
163    Allocation
164    -------------------------------------------------------------------------- */
165
166 STATIC_INLINE void
167 initGroup(bdescr *head)
168 {
169   bdescr *bd;
170   nat i, n;
171
172   n = head->blocks;
173   head->free   = head->start;
174   head->link   = NULL;
175   for (i=1, bd = head+1; i < n; i++, bd++) {
176       bd->free = 0;
177       bd->blocks = 0;
178       bd->link = head;
179   }
180 }
181
182 // There are quicker non-loopy ways to do log_2, but we expect n to be
183 // usually small, and MAX_FREE_LIST is also small, so the loop version
184 // might well be the best choice here.
185 STATIC_INLINE nat
186 log_2_ceil(nat n)
187 {
188     nat i, x;
189     x = 1;
190     for (i=0; i < MAX_FREE_LIST; i++) {
191         if (x >= n) return i;
192         x = x << 1;
193     }
194     return MAX_FREE_LIST;
195 }
196
197 STATIC_INLINE nat
198 log_2(nat n)
199 {
200     nat i, x;
201     x = n;
202     for (i=0; i < MAX_FREE_LIST; i++) {
203         x = x >> 1;
204         if (x == 0) return i;
205     }
206     return MAX_FREE_LIST;
207 }
208
209 STATIC_INLINE void
210 free_list_insert (bdescr *bd)
211 {
212     nat ln;
213
214     ASSERT(bd->blocks < BLOCKS_PER_MBLOCK);
215     ln = log_2(bd->blocks);
216     
217     dbl_link_onto(bd, &free_list[ln]);
218 }
219
220
221 STATIC_INLINE bdescr *
222 tail_of (bdescr *bd)
223 {
224     return bd + bd->blocks - 1;
225 }
226
227 // After splitting a group, the last block of each group must have a
228 // tail that points to the head block, to keep our invariants for
229 // coalescing. 
230 STATIC_INLINE void
231 setup_tail (bdescr *bd)
232 {
233     bdescr *tail;
234     tail = tail_of(bd);
235     if (tail != bd) {
236         tail->blocks = 0;
237         tail->free = 0;
238         tail->link = bd;
239     }
240 }
241
242
243 // Take a free block group bd, and split off a group of size n from
244 // it.  Adjust the free list as necessary, and return the new group.
245 static bdescr *
246 split_free_block (bdescr *bd, nat n, nat ln)
247 {
248     bdescr *fg; // free group
249
250     ASSERT(bd->blocks > n);
251     dbl_link_remove(bd, &free_list[ln]);
252     fg = bd + bd->blocks - n; // take n blocks off the end
253     fg->blocks = n;
254     bd->blocks -= n;
255     setup_tail(bd);
256     ln = log_2(bd->blocks);
257     dbl_link_onto(bd, &free_list[ln]);
258     return fg;
259 }
260
261 static bdescr *
262 alloc_mega_group (nat mblocks)
263 {
264     bdescr *best, *bd, *prev;
265     nat n;
266
267     n = MBLOCK_GROUP_BLOCKS(mblocks);
268
269     best = NULL;
270     prev = NULL;
271     for (bd = free_mblock_list; bd != NULL; prev = bd, bd = bd->link)
272     {
273         if (bd->blocks == n) 
274         {
275             if (prev) {
276                 prev->link = bd->link;
277             } else {
278                 free_mblock_list = bd->link;
279             }
280             initGroup(bd);
281             return bd;
282         }
283         else if (bd->blocks > n)
284         {
285             if (!best || bd->blocks < best->blocks)
286             {
287                 best = bd;
288             }
289         }
290     }
291
292     if (best)
293     {
294         // we take our chunk off the end here.
295         StgWord best_mblocks  = BLOCKS_TO_MBLOCKS(best->blocks);
296         bd = FIRST_BDESCR((StgWord8*)MBLOCK_ROUND_DOWN(best) + 
297                           (best_mblocks-mblocks)*MBLOCK_SIZE);
298
299         best->blocks = MBLOCK_GROUP_BLOCKS(best_mblocks - mblocks);
300         initMBlock(MBLOCK_ROUND_DOWN(bd));
301     }
302     else
303     {
304         void *mblock = getMBlocks(mblocks);
305         initMBlock(mblock);             // only need to init the 1st one
306         bd = FIRST_BDESCR(mblock);
307     }
308     bd->blocks = MBLOCK_GROUP_BLOCKS(mblocks);
309     return bd;
310 }
311
312 bdescr *
313 allocGroup (nat n)
314 {
315     bdescr *bd, *rem;
316     nat ln;
317
318     if (n == 0) barf("allocGroup: requested zero blocks");
319     
320     if (n >= BLOCKS_PER_MBLOCK)
321     {
322         nat mblocks;
323
324         mblocks = BLOCKS_TO_MBLOCKS(n);
325
326         // n_alloc_blocks doesn't count the extra blocks we get in a
327         // megablock group.
328         n_alloc_blocks += mblocks * BLOCKS_PER_MBLOCK;
329         if (n_alloc_blocks > hw_alloc_blocks) hw_alloc_blocks = n_alloc_blocks;
330
331         bd = alloc_mega_group(mblocks);
332         // only the bdescrs of the first MB are required to be initialised
333         initGroup(bd);
334
335         IF_DEBUG(sanity, checkFreeListSanity());
336         return bd;
337     }
338     
339     n_alloc_blocks += n;
340     if (n_alloc_blocks > hw_alloc_blocks) hw_alloc_blocks = n_alloc_blocks;
341
342     ln = log_2_ceil(n);
343
344     while (ln < MAX_FREE_LIST && free_list[ln] == NULL) {
345         ln++;
346     }
347
348     if (ln == MAX_FREE_LIST) {
349 #if 0
350         if (((W_)mblocks_allocated * MBLOCK_SIZE_W - (W_)n_alloc_blocks * BLOCK_SIZE_W) > (1024*1024)/sizeof(W_)) {
351             debugBelch("Fragmentation, wanted %d blocks:", n);
352             RtsFlags.DebugFlags.block_alloc = 1;
353             checkFreeListSanity();
354         }
355 #endif
356
357         bd = alloc_mega_group(1);
358         bd->blocks = n;
359         initGroup(bd);                   // we know the group will fit
360         rem = bd + n;
361         rem->blocks = BLOCKS_PER_MBLOCK-n;
362         initGroup(rem); // init the slop
363         n_alloc_blocks += rem->blocks;
364         freeGroup(rem);                  // add the slop on to the free list
365         IF_DEBUG(sanity, checkFreeListSanity());
366         return bd;
367     }
368
369     bd = free_list[ln];
370
371     if (bd->blocks == n)                // exactly the right size!
372     {
373         dbl_link_remove(bd, &free_list[ln]);
374     }
375     else if (bd->blocks >  n)            // block too big...
376     {                              
377         bd = split_free_block(bd, n, ln);
378     }
379     else
380     {
381         barf("allocGroup: free list corrupted");
382     }
383     initGroup(bd);              // initialise it
384     IF_DEBUG(sanity, checkFreeListSanity());
385     ASSERT(bd->blocks == n);
386     return bd;
387 }
388
389 bdescr *
390 allocGroup_lock(nat n)
391 {
392     bdescr *bd;
393     ACQUIRE_SM_LOCK;
394     bd = allocGroup(n);
395     RELEASE_SM_LOCK;
396     return bd;
397 }
398
399 bdescr *
400 allocBlock(void)
401 {
402     return allocGroup(1);
403 }
404
405 bdescr *
406 allocBlock_lock(void)
407 {
408     bdescr *bd;
409     ACQUIRE_SM_LOCK;
410     bd = allocBlock();
411     RELEASE_SM_LOCK;
412     return bd;
413 }
414
415 /* -----------------------------------------------------------------------------
416    De-Allocation
417    -------------------------------------------------------------------------- */
418
419 STATIC_INLINE bdescr *
420 coalesce_mblocks (bdescr *p)
421 {
422     bdescr *q;
423
424     q = p->link;
425     if (q != NULL && 
426         MBLOCK_ROUND_DOWN(q) == 
427         (StgWord8*)MBLOCK_ROUND_DOWN(p) + 
428         BLOCKS_TO_MBLOCKS(p->blocks) * MBLOCK_SIZE) {
429         // can coalesce
430         p->blocks  = MBLOCK_GROUP_BLOCKS(BLOCKS_TO_MBLOCKS(p->blocks) +
431                                          BLOCKS_TO_MBLOCKS(q->blocks));
432         p->link = q->link;
433         return p;
434     }
435     return q;
436 }
437
438 static void
439 free_mega_group (bdescr *mg)
440 {
441     bdescr *bd, *prev;
442
443     // Find the right place in the free list.  free_mblock_list is
444     // sorted by *address*, not by size as the free_list is.
445     prev = NULL;
446     bd = free_mblock_list;
447     while (bd && bd->start < mg->start) {
448         prev = bd;
449         bd = bd->link;
450     }
451
452     // coalesce backwards
453     if (prev)
454     {
455         mg->link = prev->link;
456         prev->link = mg;
457         mg = coalesce_mblocks(prev);
458     }
459     else
460     {
461         mg->link = free_mblock_list;
462         free_mblock_list = mg;
463     }
464     // coalesce forwards
465     coalesce_mblocks(mg);
466
467     IF_DEBUG(sanity, checkFreeListSanity());
468 }    
469
470
471 void
472 freeGroup(bdescr *p)
473 {
474   nat ln;
475
476   // Todo: not true in multithreaded GC
477   // ASSERT_SM_LOCK();
478
479   ASSERT(p->free != (P_)-1);
480
481   p->free = (void *)-1;  /* indicates that this block is free */
482   p->gen = NULL;
483   p->gen_no = 0;
484   /* fill the block group with garbage if sanity checking is on */
485   IF_DEBUG(sanity,memset(p->start, 0xaa, (W_)p->blocks * BLOCK_SIZE));
486
487   if (p->blocks == 0) barf("freeGroup: block size is zero");
488
489   if (p->blocks >= BLOCKS_PER_MBLOCK)
490   {
491       nat mblocks;
492
493       mblocks = BLOCKS_TO_MBLOCKS(p->blocks);
494       // If this is an mgroup, make sure it has the right number of blocks
495       ASSERT(p->blocks == MBLOCK_GROUP_BLOCKS(mblocks));
496
497       n_alloc_blocks -= mblocks * BLOCKS_PER_MBLOCK;
498
499       free_mega_group(p);
500       return;
501   }
502
503   ASSERT(n_alloc_blocks >= p->blocks);
504   n_alloc_blocks -= p->blocks;
505
506   // coalesce forwards
507   {
508       bdescr *next;
509       next = p + p->blocks;
510       if (next <= LAST_BDESCR(MBLOCK_ROUND_DOWN(p)) && next->free == (P_)-1)
511       {
512           p->blocks += next->blocks;
513           ln = log_2(next->blocks);
514           dbl_link_remove(next, &free_list[ln]);
515           if (p->blocks == BLOCKS_PER_MBLOCK)
516           {
517               free_mega_group(p);
518               return;
519           }
520           setup_tail(p);
521       }
522   }
523
524   // coalesce backwards
525   if (p != FIRST_BDESCR(MBLOCK_ROUND_DOWN(p)))
526   {
527       bdescr *prev;
528       prev = p - 1;
529       if (prev->blocks == 0) prev = prev->link; // find the head
530
531       if (prev->free == (P_)-1)
532       {
533           ln = log_2(prev->blocks);
534           dbl_link_remove(prev, &free_list[ln]);
535           prev->blocks += p->blocks;
536           if (prev->blocks >= BLOCKS_PER_MBLOCK)
537           {
538               free_mega_group(prev);
539               return;
540           }
541           p = prev;
542       }
543   }
544       
545   setup_tail(p);
546   free_list_insert(p);
547
548   IF_DEBUG(sanity, checkFreeListSanity());
549 }
550
551 void
552 freeGroup_lock(bdescr *p)
553 {
554     ACQUIRE_SM_LOCK;
555     freeGroup(p);
556     RELEASE_SM_LOCK;
557 }
558
559 void
560 freeChain(bdescr *bd)
561 {
562   bdescr *next_bd;
563   while (bd != NULL) {
564     next_bd = bd->link;
565     freeGroup(bd);
566     bd = next_bd;
567   }
568 }
569
570 void
571 freeChain_lock(bdescr *bd)
572 {
573     ACQUIRE_SM_LOCK;
574     freeChain(bd);
575     RELEASE_SM_LOCK;
576 }
577
578 // splitBlockGroup(bd,B) splits bd in two.  Afterward, bd will have B
579 // blocks, and a new block descriptor pointing to the remainder is
580 // returned.
581 bdescr *
582 splitBlockGroup (bdescr *bd, nat blocks)
583 {
584     bdescr *new_bd;
585
586     if (bd->blocks <= blocks) {
587         barf("splitLargeBlock: too small");
588     }
589
590     if (bd->blocks > BLOCKS_PER_MBLOCK) {
591         nat low_mblocks, high_mblocks;
592         void *new_mblock;
593         if ((blocks - BLOCKS_PER_MBLOCK) % (MBLOCK_SIZE / BLOCK_SIZE) != 0) {
594             barf("splitLargeBlock: not a multiple of a megablock");
595         }
596         low_mblocks = 1 + (blocks - BLOCKS_PER_MBLOCK) / (MBLOCK_SIZE / BLOCK_SIZE);
597         high_mblocks = (bd->blocks - blocks) / (MBLOCK_SIZE / BLOCK_SIZE);
598
599         new_mblock = (void *) ((P_)MBLOCK_ROUND_DOWN(bd) + (W_)low_mblocks * MBLOCK_SIZE_W);
600         initMBlock(new_mblock);
601         new_bd = FIRST_BDESCR(new_mblock);
602         new_bd->blocks = MBLOCK_GROUP_BLOCKS(high_mblocks);
603
604         ASSERT(blocks + new_bd->blocks == 
605                bd->blocks + BLOCKS_PER_MBLOCK - MBLOCK_SIZE/BLOCK_SIZE);
606     }
607     else
608     {
609         // NB. we're not updating all the bdescrs in the split groups to
610         // point to the new heads, so this can only be used for large
611         // objects which do not start in the non-head block.
612         new_bd = bd + blocks;
613         new_bd->blocks = bd->blocks - blocks;
614     }
615     bd->blocks = blocks;
616
617     return new_bd;
618 }
619
620 static void
621 initMBlock(void *mblock)
622 {
623     bdescr *bd;
624     StgWord8 *block;
625
626     /* the first few Bdescr's in a block are unused, so we don't want to
627      * put them all on the free list.
628      */
629     block = FIRST_BLOCK(mblock);
630     bd    = FIRST_BDESCR(mblock);
631     
632     /* Initialise the start field of each block descriptor
633      */
634     for (; block <= (StgWord8*)LAST_BLOCK(mblock); bd += 1, 
635              block += BLOCK_SIZE) {
636         bd->start = (void*)block;
637     }
638 }
639
640 /* -----------------------------------------------------------------------------
641    Stats / metrics
642    -------------------------------------------------------------------------- */
643
644 nat
645 countBlocks(bdescr *bd)
646 {
647     nat n;
648     for (n=0; bd != NULL; bd=bd->link) {
649         n += bd->blocks;
650     }
651     return n;
652 }
653
654 // (*1) Just like countBlocks, except that we adjust the count for a
655 // megablock group so that it doesn't include the extra few blocks
656 // that would be taken up by block descriptors in the second and
657 // subsequent megablock.  This is so we can tally the count with the
658 // number of blocks allocated in the system, for memInventory().
659 nat
660 countAllocdBlocks(bdescr *bd)
661 {
662     nat n;
663     for (n=0; bd != NULL; bd=bd->link) {
664         n += bd->blocks;
665         // hack for megablock groups: see (*1) above
666         if (bd->blocks > BLOCKS_PER_MBLOCK) {
667             n -= (MBLOCK_SIZE / BLOCK_SIZE - BLOCKS_PER_MBLOCK)
668                 * (bd->blocks/(MBLOCK_SIZE/BLOCK_SIZE));
669         }
670     }
671     return n;
672 }
673
674 /* -----------------------------------------------------------------------------
675    Debugging
676    -------------------------------------------------------------------------- */
677
678 #ifdef DEBUG
679 static void
680 check_tail (bdescr *bd)
681 {
682     bdescr *tail = tail_of(bd);
683
684     if (tail != bd)
685     {
686         ASSERT(tail->blocks == 0);
687         ASSERT(tail->free == 0);
688         ASSERT(tail->link == bd);
689     }
690 }
691
692 void
693 checkFreeListSanity(void)
694 {
695     bdescr *bd, *prev;
696     nat ln, min;
697
698
699     min = 1;
700     for (ln = 0; ln < MAX_FREE_LIST; ln++) {
701         IF_DEBUG(block_alloc, debugBelch("free block list [%d]:\n", ln));
702
703         prev = NULL;
704         for (bd = free_list[ln]; bd != NULL; prev = bd, bd = bd->link)
705         {
706             IF_DEBUG(block_alloc,
707                      debugBelch("group at %p, length %ld blocks\n", 
708                                 bd->start, (long)bd->blocks));
709             ASSERT(bd->free == (P_)-1);
710             ASSERT(bd->blocks > 0 && bd->blocks < BLOCKS_PER_MBLOCK);
711             ASSERT(bd->blocks >= min && bd->blocks <= (min*2 - 1));
712             ASSERT(bd->link != bd); // catch easy loops
713
714             check_tail(bd);
715
716             if (prev)
717                 ASSERT(bd->u.back == prev);
718             else 
719                 ASSERT(bd->u.back == NULL);
720
721             {
722                 bdescr *next;
723                 next = bd + bd->blocks;
724                 if (next <= LAST_BDESCR(MBLOCK_ROUND_DOWN(bd)))
725                 {
726                     ASSERT(next->free != (P_)-1);
727                 }
728             }
729         }
730         min = min << 1;
731     }
732
733     prev = NULL;
734     for (bd = free_mblock_list; bd != NULL; prev = bd, bd = bd->link)
735     {
736         IF_DEBUG(block_alloc,
737                  debugBelch("mega group at %p, length %ld blocks\n", 
738                             bd->start, (long)bd->blocks));
739
740         ASSERT(bd->link != bd); // catch easy loops
741
742         if (bd->link != NULL)
743         {
744             // make sure the list is sorted
745             ASSERT(bd->start < bd->link->start);
746         }
747
748         ASSERT(bd->blocks >= BLOCKS_PER_MBLOCK);
749         ASSERT(MBLOCK_GROUP_BLOCKS(BLOCKS_TO_MBLOCKS(bd->blocks))
750                == bd->blocks);
751
752         // make sure we're fully coalesced
753         if (bd->link != NULL)
754         {
755             ASSERT (MBLOCK_ROUND_DOWN(bd->link) != 
756                     (StgWord8*)MBLOCK_ROUND_DOWN(bd) + 
757                     BLOCKS_TO_MBLOCKS(bd->blocks) * MBLOCK_SIZE);
758         }
759     }
760 }
761
762 nat /* BLOCKS */
763 countFreeList(void)
764 {
765   bdescr *bd;
766   lnat total_blocks = 0;
767   nat ln;
768
769   for (ln=0; ln < MAX_FREE_LIST; ln++) {
770       for (bd = free_list[ln]; bd != NULL; bd = bd->link) {
771           total_blocks += bd->blocks;
772       }
773   }
774   for (bd = free_mblock_list; bd != NULL; bd = bd->link) {
775       total_blocks += BLOCKS_PER_MBLOCK * BLOCKS_TO_MBLOCKS(bd->blocks);
776       // The caller of this function, memInventory(), expects to match
777       // the total number of blocks in the system against mblocks *
778       // BLOCKS_PER_MBLOCK, so we must subtract the space for the
779       // block descriptors from *every* mblock.
780   }
781   return total_blocks;
782 }
783
784 void
785 markBlocks (bdescr *bd)
786 {
787     for (; bd != NULL; bd = bd->link) {
788         bd->flags |= BF_KNOWN;
789     }
790 }
791
792 void
793 reportUnmarkedBlocks (void)
794 {
795     void *mblock;
796     bdescr *bd;
797
798     debugBelch("Unreachable blocks:\n");
799     for (mblock = getFirstMBlock(); mblock != NULL;
800          mblock = getNextMBlock(mblock)) {
801         for (bd = FIRST_BDESCR(mblock); bd <= LAST_BDESCR(mblock); ) {
802             if (!(bd->flags & BF_KNOWN) && bd->free != (P_)-1) {
803                 debugBelch("  %p\n",bd);
804             }
805             if (bd->blocks >= BLOCKS_PER_MBLOCK) {
806                 mblock = (StgWord8*)mblock +
807                     (BLOCKS_TO_MBLOCKS(bd->blocks) - 1) * MBLOCK_SIZE;
808                 break;
809             } else {
810                 bd += bd->blocks;
811             }
812         }
813     }
814 }
815
816 #endif