[project @ 1999-03-03 19:07:39 by sof]
[ghc-hetmet.git] / ghc / rts / Sanity.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Sanity.c,v 1.11 1999/03/03 19:07:39 sof Exp $
3  *
4  * (c) The GHC Team, 1998-1999
5  *
6  * Sanity checking code for the heap and stack.
7  *
8  * Used when debugging: check that the stack looks reasonable.
9  *
10  *    - All things that are supposed to be pointers look like pointers.
11  *
12  *    - Objects in text space are marked as static closures, those
13  *      in the heap are dynamic.
14  *
15  * ---------------------------------------------------------------------------*/
16
17 #include "Rts.h"
18
19 #ifdef DEBUG
20
21 #include "RtsFlags.h"
22 #include "RtsUtils.h"
23 #include "BlockAlloc.h"
24 #include "Sanity.h"
25
26 #define LOOKS_LIKE_PTR(r) (IS_DATA_PTR(r) || ((HEAP_ALLOCED(r) && Bdescr((P_)r)->free != (void *)-1)))
27
28 /* -----------------------------------------------------------------------------
29    Check stack sanity
30    -------------------------------------------------------------------------- */
31
32 StgOffset checkStackClosure( StgClosure* c );
33
34 StgOffset checkStackObject( StgPtr sp );
35
36 void      checkStackChunk( StgPtr sp, StgPtr stack_end );
37
38 static StgOffset checkSmallBitmap(  StgPtr payload, StgWord32 bitmap );
39
40 static StgOffset checkLargeBitmap( StgPtr payload, 
41                                    StgLargeBitmap* large_bitmap );
42
43 void checkClosureShallow( StgClosure* p );
44
45 static StgOffset 
46 checkSmallBitmap( StgPtr payload, StgWord32 bitmap )
47 {
48     StgOffset i;
49
50     i = 0;
51     for(; bitmap != 0; ++i, bitmap >>= 1 ) {
52         if ((bitmap & 1) == 0) {
53             checkClosure(stgCast(StgClosure*,payload[i]));
54         }
55     }
56     return i;
57 }
58
59
60 static StgOffset 
61 checkLargeBitmap( StgPtr payload, StgLargeBitmap* large_bitmap )
62 {
63     StgWord32 bmp;
64     StgOffset i;
65
66     i = 0;
67     for (bmp=0; bmp<large_bitmap->size; bmp++) {
68         StgWord32 bitmap = large_bitmap->bitmap[bmp];
69         for(; bitmap != 0; ++i, bitmap >>= 1 ) {
70             if ((bitmap & 1) == 0) {
71                 checkClosure(stgCast(StgClosure*,payload[i]));
72             }
73         }
74     }
75     return i;
76 }
77
78 StgOffset 
79 checkStackClosure( StgClosure* c )
80 {    
81     const StgInfoTable* info = get_itbl(c);
82
83     /* All activation records have 'bitmap' style layout info. */
84     switch (info->type) {
85     case RET_DYN: /* Dynamic bitmap: the mask is stored on the stack */
86         {
87             StgRetDyn* r = stgCast(StgRetDyn*,c);
88             return sizeofW(StgRetDyn) + 
89                    checkSmallBitmap(r->payload,r->liveness);
90         }
91     case RET_BCO: /* small bitmap (<= 32 entries) */
92     case RET_SMALL:
93     case RET_VEC_SMALL:
94     case UPDATE_FRAME:
95     case CATCH_FRAME:
96     case STOP_FRAME:
97     case SEQ_FRAME:
98             return sizeofW(StgClosure) + 
99                    checkSmallBitmap((StgPtr)c->payload,info->layout.bitmap);
100     case RET_BIG: /* large bitmap (> 32 entries) */
101     case RET_VEC_BIG:
102             return sizeofW(StgClosure) + 
103                    checkLargeBitmap((StgPtr)c->payload,
104                                     info->layout.large_bitmap);
105     case FUN:
106     case FUN_STATIC: /* probably a slow-entry point return address: */
107             return 1;
108     default:
109             /* if none of the above, maybe it's a closure which looks a
110              * little like an infotable
111              */
112             checkClosureShallow(*stgCast(StgClosure**,c));
113             return 1;
114             /* barf("checkStackClosure: weird activation record found on stack (%p).",c); */
115     }
116 }
117
118 /*
119  * check that it looks like a valid closure - without checking its payload
120  * used to avoid recursion between checking PAPs and checking stack
121  * chunks.
122  */
123  
124 void 
125 checkClosureShallow( StgClosure* p )
126 {
127     ASSERT(LOOKS_LIKE_GHC_INFO(p->header.info));
128
129     /* Is it a static closure (i.e. in the data segment)? */
130     if (LOOKS_LIKE_STATIC(p)) {
131         ASSERT(closure_STATIC(p));
132     } else {
133         ASSERT(!closure_STATIC(p));
134         ASSERT(LOOKS_LIKE_PTR(p));
135     }
136 }
137
138 /* check an individual stack object */
139 StgOffset 
140 checkStackObject( StgPtr sp )
141 {
142     if (IS_ARG_TAG(*sp)) {
143         /* Tagged words might be "stubbed" pointers, so there's no
144          * point checking to see whether they look like pointers or
145          * not (some of them will).
146          */
147         return ARG_SIZE(*sp) + 1;
148     } else if (LOOKS_LIKE_GHC_INFO(*stgCast(StgPtr*,sp))) {
149         return checkStackClosure(stgCast(StgClosure*,sp));
150     } else { /* must be an untagged closure pointer in the stack */
151         checkClosureShallow(*stgCast(StgClosure**,sp));
152         return 1;
153     }
154 }
155
156 /* check sections of stack between update frames */
157 void 
158 checkStackChunk( StgPtr sp, StgPtr stack_end )
159 {
160     StgPtr p;
161
162     p = sp;
163     while (p < stack_end) {
164         p += checkStackObject( p );
165     }
166     ASSERT( p == stack_end );
167 }
168
169 StgOffset 
170 checkClosure( StgClosure* p )
171 {
172     const StgInfoTable *info;
173
174 #ifndef INTERPRETER    
175     ASSERT(LOOKS_LIKE_GHC_INFO(p->header.info));
176 #endif
177
178     /* Is it a static closure (i.e. in the data segment)? */
179     if (LOOKS_LIKE_STATIC(p)) {
180         ASSERT(closure_STATIC(p));
181     } else {
182         ASSERT(!closure_STATIC(p));
183         ASSERT(LOOKS_LIKE_PTR(p));
184     }
185
186     info = get_itbl(p);
187     switch (info->type) {
188     case BCO:
189         {
190             StgBCO* bco = stgCast(StgBCO*,p);
191             nat i;
192             for(i=0; i < bco->n_ptrs; ++i) {
193                 ASSERT(LOOKS_LIKE_PTR(bcoConstPtr(bco,i)));
194             }
195             return bco_sizeW(bco);
196         }
197
198     case MVAR:
199       { 
200         StgMVar *mvar = (StgMVar *)p;
201         ASSERT(LOOKS_LIKE_PTR(mvar->head));
202         ASSERT(LOOKS_LIKE_PTR(mvar->tail));
203         ASSERT(LOOKS_LIKE_PTR(mvar->value));
204         return sizeofW(StgMVar);
205       }
206
207     case THUNK:
208     case THUNK_1_0:
209     case THUNK_0_1:
210     case THUNK_1_1:
211     case THUNK_0_2:
212     case THUNK_2_0:
213       {
214         nat i;
215         for (i = 0; i < info->layout.payload.ptrs; i++) {
216           ASSERT(LOOKS_LIKE_PTR(payloadPtr(p,i)));
217         }
218         return stg_max(sizeW_fromITBL(info), sizeofW(StgHeader) + MIN_UPD_SIZE);
219       }
220
221     case FUN:
222     case FUN_1_0:
223     case FUN_0_1:
224     case FUN_1_1:
225     case FUN_0_2:
226     case FUN_2_0:
227     case CONSTR:
228     case CONSTR_1_0:
229     case CONSTR_0_1:
230     case CONSTR_1_1:
231     case CONSTR_0_2:
232     case CONSTR_2_0:
233     case IND_PERM:
234     case IND_OLDGEN:
235     case IND_OLDGEN_PERM:
236     case CAF_UNENTERED:
237     case CAF_ENTERED:
238     case CAF_BLACKHOLE:
239     case BLACKHOLE:
240     case BLACKHOLE_BQ:
241     case FOREIGN:
242     case STABLE_NAME:
243     case MUT_VAR:
244     case CONSTR_INTLIKE:
245     case CONSTR_CHARLIKE:
246     case CONSTR_STATIC:
247     case CONSTR_NOCAF_STATIC:
248     case THUNK_STATIC:
249     case FUN_STATIC:
250     case IND_STATIC:
251         {
252             nat i;
253             for (i = 0; i < info->layout.payload.ptrs; i++) {
254                 ASSERT(LOOKS_LIKE_PTR(payloadPtr(p,i)));
255             }
256             return sizeW_fromITBL(info);
257         }
258
259     case WEAK:
260       /* deal with these specially - the info table isn't
261        * representative of the actual layout.
262        */
263       { StgWeak *w = (StgWeak *)p;
264         ASSERT(LOOKS_LIKE_PTR(w->key));
265         ASSERT(LOOKS_LIKE_PTR(w->value));
266         ASSERT(LOOKS_LIKE_PTR(w->finalizer));
267         if (w->link) {
268           ASSERT(LOOKS_LIKE_PTR(w->link));
269         }
270         return sizeW_fromITBL(info);
271       }
272
273     case THUNK_SELECTOR:
274             ASSERT(LOOKS_LIKE_PTR(stgCast(StgSelector*,p)->selectee));
275             return sizeofW(StgHeader) + MIN_UPD_SIZE;
276
277     case IND:
278         { 
279             /* we don't expect to see any of these after GC
280              * but they might appear during execution
281              */
282             P_ q;
283             StgInd *ind = stgCast(StgInd*,p);
284             ASSERT(LOOKS_LIKE_PTR(ind->indirectee));
285             q = (P_)p + sizeofW(StgInd);
286             while (!*q) { q++; }; /* skip padding words (see GC.c: evacuate())*/
287             return q - (P_)p;
288         }
289
290     case RET_BCO:
291     case RET_SMALL:
292     case RET_VEC_SMALL:
293     case RET_BIG:
294     case RET_VEC_BIG:
295     case RET_DYN:
296     case UPDATE_FRAME:
297     case STOP_FRAME:
298     case CATCH_FRAME:
299     case SEQ_FRAME:
300             barf("checkClosure: stack frame");
301
302     case AP_UPD: /* we can treat this as being the same as a PAP */
303     case PAP:
304         { 
305             StgPAP *pap = stgCast(StgPAP*,p);
306             ASSERT(LOOKS_LIKE_PTR(pap->fun));
307             checkStackChunk((StgPtr)pap->payload, 
308                             (StgPtr)pap->payload + pap->n_args
309                             );
310             return pap_sizeW(pap);
311         }
312
313     case ARR_WORDS:
314             return arr_words_sizeW(stgCast(StgArrWords*,p));
315
316     case MUT_ARR_PTRS:
317     case MUT_ARR_PTRS_FROZEN:
318         {
319             StgMutArrPtrs* a = stgCast(StgMutArrPtrs*,p);
320             nat i;
321             for (i = 0; i < a->ptrs; i++) {
322                 ASSERT(LOOKS_LIKE_PTR(a->payload[i]));
323             }
324             return mut_arr_ptrs_sizeW(a);
325         }
326
327     case TSO:
328         checkTSO((StgTSO *)p);
329         return tso_sizeW((StgTSO *)p);
330
331     case BLOCKED_FETCH:
332     case FETCH_ME:
333     case EVACUATED:
334             barf("checkClosure: unimplemented/strange closure type");
335     default:
336             barf("checkClosure");
337     }
338 #undef LOOKS_LIKE_PTR
339 }
340
341 /* -----------------------------------------------------------------------------
342    Check Heap Sanity
343
344    After garbage collection, the live heap is in a state where we can
345    run through and check that all the pointers point to the right
346    place.  This function starts at a given position and sanity-checks
347    all the objects in the remainder of the chain.
348    -------------------------------------------------------------------------- */
349
350 extern void 
351 checkHeap(bdescr *bd, StgPtr start)
352 {
353     StgPtr p;
354
355     if (start == NULL) {
356       p = bd->start;
357     } else {
358       p = start;
359     }
360
361     while (bd != NULL) {
362       while (p < bd->free) {
363         nat size = checkClosure(stgCast(StgClosure*,p));
364         /* This is the smallest size of closure that can live in the heap. */
365         ASSERT( size >= MIN_NONUPD_SIZE + sizeofW(StgHeader) );
366         p += size;
367
368         /* skip over slop */
369         while (p < bd->free &&
370                (*p == 0 || !LOOKS_LIKE_GHC_INFO((void*)*p))) { p++; } 
371       }
372       bd = bd->link;
373       if (bd != NULL) {
374         p = bd->start;
375       }
376     }
377 }
378
379 extern void
380 checkChain(bdescr *bd)
381 {
382   while (bd != NULL) {
383     checkClosure((StgClosure *)bd->start);
384     bd = bd->link;
385   }
386 }
387
388 /* check stack - making sure that update frames are linked correctly */
389 void 
390 checkStack(StgPtr sp, StgPtr stack_end, StgUpdateFrame* su )
391 {
392     /* check everything down to the first update frame */
393     checkStackChunk( sp, stgCast(StgPtr,su) );
394     while ( stgCast(StgPtr,su) < stack_end) {
395         sp = stgCast(StgPtr,su);
396         switch (get_itbl(su)->type) {
397         case UPDATE_FRAME:
398                 su = su->link;
399                 break;
400         case SEQ_FRAME:
401                 su = stgCast(StgSeqFrame*,su)->link;
402                 break;
403         case CATCH_FRAME:
404                 su = stgCast(StgCatchFrame*,su)->link;
405                 break;
406         case STOP_FRAME:
407                 /* not quite: ASSERT(stgCast(StgPtr,su) == stack_end); */
408                 return;
409         default:
410                 barf("checkStack: weird record found on update frame list.");
411         }
412         checkStackChunk( sp, stgCast(StgPtr,su) );
413     }
414     ASSERT(stgCast(StgPtr,su) == stack_end);
415 }
416
417 extern void
418 checkTSO(StgTSO *tso)
419 {
420     StgPtr sp = tso->sp;
421     StgPtr stack = tso->stack;
422     StgUpdateFrame* su = tso->su;
423     StgOffset stack_size = tso->stack_size;
424     StgPtr stack_end = stack + stack_size;
425
426     if (tso->whatNext == ThreadComplete ||  tso->whatNext == ThreadKilled) {
427       /* The garbage collector doesn't bother following any pointers
428        * from dead threads, so don't check sanity here.  
429        */
430       return;
431     }
432
433     ASSERT(stack <= sp && sp < stack_end);
434     ASSERT(sp <= stgCast(StgPtr,su));
435
436     checkStack(sp, stack_end, su);
437 }
438
439 /* -----------------------------------------------------------------------------
440    Check Blackhole Sanity
441
442    Test whether an object is already on the update list.
443    It isn't necessarily an rts error if it is - it might be a programming
444    error.
445
446    Future versions might be able to test for a blackhole without traversing
447    the update frame list.
448
449    -------------------------------------------------------------------------- */
450 rtsBool isBlackhole( StgTSO* tso, StgClosure* p )
451 {
452   StgUpdateFrame* su = tso->su;
453   do {
454     switch (get_itbl(su)->type) {
455     case UPDATE_FRAME:
456       if (su->updatee == p) {
457         return rtsTrue;
458       } else {
459         su = su->link;
460       }
461       break;
462     case SEQ_FRAME:
463       su = stgCast(StgSeqFrame*,su)->link;
464       break;
465     case CATCH_FRAME:
466       su = stgCast(StgCatchFrame*,su)->link;
467       break;
468     case STOP_FRAME:
469       return rtsFalse;
470     default:
471       barf("isBlackhole: weird record found on update frame list.");
472     }
473   } while (1);
474 }
475
476 #endif /* DEBUG */