when a memory leak is detected, report which blocks are unreachable
[ghc-hetmet.git] / rts / sm / MBlock.c
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team 1998-2008
4  *
5  * MegaBlock Allocator Interface.  This file contains all the dirty
6  * architecture-dependent hackery required to get a chunk of aligned
7  * memory from the operating system.
8  *
9  * ---------------------------------------------------------------------------*/
10
11 #include "PosixSource.h"
12
13 #include "Rts.h"
14 #include "RtsUtils.h"
15 #include "MBlock.h"
16 #include "BlockAlloc.h"
17 #include "Trace.h"
18 #include "OSMem.h"
19
20 lnat mblocks_allocated = 0;
21
22 void
23 initMBlocks(void)
24 {
25     osMemInit();
26 }
27
28 /* -----------------------------------------------------------------------------
29    The MBlock Map: provides our implementation of HEAP_ALLOCED()
30    -------------------------------------------------------------------------- */
31
32 #if SIZEOF_VOID_P == 4
33 StgWord8 mblock_map[MBLOCK_MAP_SIZE]; // initially all zeros
34 #elif SIZEOF_VOID_P == 8
35 static MBlockMap dummy_mblock_map;
36 MBlockMap *mblock_cache = &dummy_mblock_map;
37 nat mblock_map_count = 0;
38 MBlockMap **mblock_maps = NULL;
39
40 static MBlockMap *
41 findMBlockMap(void *p)
42 {
43     nat i;
44     StgWord32 hi = (StgWord32) (((StgWord)p) >> 32);
45     for( i = 0; i < mblock_map_count; i++ )
46     {
47         if(mblock_maps[i]->addrHigh32 == hi)
48         {
49             return mblock_maps[i];
50         }
51     }
52     return NULL;
53 }
54
55 StgBool
56 slowIsHeapAlloced(void *p)
57 {
58     MBlockMap *map = findMBlockMap(p);
59     if(map)
60     {
61         mblock_cache = map;
62         return map->mblocks[MBLOCK_MAP_ENTRY(p)];
63     }
64     else
65         return 0;
66 }
67 #endif
68
69 static void
70 markHeapAlloced(void *p)
71 {
72 #if SIZEOF_VOID_P == 4
73     mblock_map[MBLOCK_MAP_ENTRY(p)] = 1;
74 #elif SIZEOF_VOID_P == 8
75     MBlockMap *map = findMBlockMap(p);
76     if(map == NULL)
77     {
78         mblock_map_count++;
79         mblock_maps = realloc(mblock_maps,
80                               sizeof(MBlockMap*) * mblock_map_count);
81         map = mblock_maps[mblock_map_count-1] = calloc(1,sizeof(MBlockMap));
82         map->addrHigh32 = (StgWord32) (((StgWord)p) >> 32);
83     }
84     map->mblocks[MBLOCK_MAP_ENTRY(p)] = 1;
85     mblock_cache = map;
86 #endif
87 }
88
89 /* ----------------------------------------------------------------------------
90    Debugging code for traversing the allocated MBlocks
91    
92    This is used for searching for lost blocks when a memory leak is
93    detected; see Blocks.c:findUnmarkedBlock().
94    ------------------------------------------------------------------------ */
95
96 #ifdef DEBUG
97
98 #if SIZEOF_VOID_P == 4
99
100 STATIC_INLINE
101 void * mapEntryToMBlock(nat i)
102 {
103     return (void *)((StgWord)i << MBLOCK_SHIFT);
104 }
105
106 void * getFirstMBlock(void)
107 {
108     nat i;
109
110     for (i = 0; i < MBLOCK_MAP_SIZE; i++) {
111         if (mblock_map[i]) return mapEntryToMBlock(i);
112     }
113     return NULL;
114 }
115
116 void * getNextMBlock(void *mblock)
117 {
118     nat i;
119
120     for (i = MBLOCK_MAP_ENTRY(mblock) + 1; i < MBLOCK_MAP_SIZE; i++) {
121         if (mblock_map[i]) return mapEntryToMBlock(i);
122     }
123     return NULL;
124 }
125
126 #elif SIZEOF_VOID_P == 8
127
128 STATIC_INLINE
129 void * mapEntryToMBlock(MBlockMap *map, nat i)
130 {
131     return (void *)(((StgWord)map->addrHigh32) << 32) + 
132         ((StgWord)i << MBLOCK_SHIFT);
133 }
134
135 void * getFirstMBlock(void)
136 {
137     MBlockMap *map;
138     nat i, j;
139
140     for (j = 0; j < mblock_map_count; j++)  {
141         map = mblock_maps[j];
142         for (i = 0; i < MBLOCK_MAP_SIZE; i++) {
143             if (map->mblocks[i]) return mapEntryToMBlock(map,i);
144         }
145     }
146     return NULL;
147 }
148
149 void * getNextMBlock(void *mblock)
150 {
151     MBlockMap *map;
152     nat i, j;
153
154     for (j = 0; j < mblock_map_count; j++)  {
155         map = mblock_maps[j];
156         if (map->addrHigh32 == (StgWord)mblock >> 32) break;
157     }
158     if (j == mblock_map_count) return NULL;
159
160     for (; j < mblock_map_count; j++) {
161         map = mblock_maps[j];
162         if (map->addrHigh32 == (StgWord)mblock >> 32) {
163             i = MBLOCK_MAP_ENTRY(mblock) + 1;
164         } else {
165             i = 0;
166         }
167         for (; i < MBLOCK_MAP_SIZE; i++) {
168             if (map->mblocks[i]) return mapEntryToMBlock(map,i);
169         }
170     }
171     return NULL;
172 }
173
174 #endif // SIZEOF_VOID_P
175
176 #endif // DEBUG
177
178 /* -----------------------------------------------------------------------------
179    Allocate new mblock(s)
180    -------------------------------------------------------------------------- */
181
182 void *
183 getMBlock(void)
184 {
185   return getMBlocks(1);
186 }
187
188 // The external interface: allocate 'n' mblocks, and return the
189 // address.
190
191 void *
192 getMBlocks(nat n)
193 {
194     nat i;
195     void *ret;
196
197     ret = osGetMBlocks(n);
198
199     debugTrace(DEBUG_gc, "allocated %d megablock(s) at %p",n,ret);
200     
201     // fill in the table
202     for (i = 0; i < n; i++) {
203         markHeapAlloced( ret + i * MBLOCK_SIZE );
204     }
205     
206     mblocks_allocated += n;
207
208     return ret;
209 }
210
211 void
212 freeAllMBlocks(void)
213 {
214     osFreeAllMBlocks();
215 }