/* ----------------------------------------------------------------------------- * * (c) The University of Glasgow 2006-2017 * * Introspection into GHC's heap representation * * ---------------------------------------------------------------------------*/ #include "Rts.h" #include "RtsAPI.h" #include "RtsUtils.h" #include "Capability.h" #include "Printer.h" StgWord heap_view_closureSize(StgClosure *closure) { ASSERT(LOOKS_LIKE_CLOSURE_PTR(closure)); return closure_sizeW(closure); } static void heap_view_closure_ptrs_in_large_bitmap(StgClosure *ptrs[], StgWord *nptrs , StgClosure **p, StgLargeBitmap *large_bitmap , uint32_t size ) { uint32_t i, j, b; StgWord bitmap; b = 0; for (i = 0; i < size; b++) { bitmap = large_bitmap->bitmap[b]; j = stg_min(size-i, BITS_IN(W_)); i += j; for (; j > 0; j--, p++) { if ((bitmap & 1) == 0) { ptrs[(*nptrs)++] = *p; } bitmap = bitmap >> 1; } } } void heap_view_closure_ptrs_in_pap_payload(StgClosure *ptrs[], StgWord *nptrs , StgClosure *fun, StgClosure **payload, StgWord size) { StgWord bitmap; const StgFunInfoTable *fun_info; fun_info = get_fun_itbl(UNTAG_CLOSURE(fun)); // ASSERT(fun_info->i.type != PAP); StgClosure **p = payload; switch (fun_info->f.fun_type) { case ARG_GEN: bitmap = BITMAP_BITS(fun_info->f.b.bitmap); goto small_bitmap; case ARG_GEN_BIG: heap_view_closure_ptrs_in_large_bitmap(ptrs, nptrs, payload, GET_FUN_LARGE_BITMAP(fun_info), size); break; case ARG_BCO: heap_view_closure_ptrs_in_large_bitmap(ptrs, nptrs, payload, BCO_BITMAP(fun), size); break; default: bitmap = BITMAP_BITS(stg_arg_bitmaps[fun_info->f.fun_type]); small_bitmap: while (size > 0) { if ((bitmap & 1) == 0) { ptrs[(*nptrs)++] = *p; } bitmap = bitmap >> 1; p++; size--; } break; } } // See Heap.h StgWord collect_pointers(StgClosure *closure, StgClosure *ptrs[]) { StgClosure **end; const StgInfoTable *info = get_itbl(closure); StgWord nptrs = 0; StgWord i; switch (info->type) { case INVALID_OBJECT: barf("Invalid Object"); break; // No pointers case ARR_WORDS: case STACK: break; // Default layout case CONSTR_1_0: case CONSTR_0_1: case CONSTR_2_0: case CONSTR_1_1: case CONSTR_0_2: case CONSTR: case CONSTR_NOCAF: case PRIM: case FUN: case FUN_1_0: case FUN_0_1: case FUN_1_1: case FUN_2_0: case FUN_0_2: case FUN_STATIC: end = closure->payload + info->layout.payload.ptrs; for (StgClosure **ptr = closure->payload; ptr < end; ptr++) { ptrs[nptrs++] = *ptr; } break; case THUNK: case THUNK_1_0: case THUNK_0_1: case THUNK_1_1: case THUNK_2_0: case THUNK_0_2: case THUNK_STATIC: end = ((StgThunk *)closure)->payload + info->layout.payload.ptrs; for (StgClosure **ptr = ((StgThunk *)closure)->payload; ptr < end; ptr++) { ptrs[nptrs++] = *ptr; } break; case THUNK_SELECTOR: ptrs[nptrs++] = ((StgSelector *)closure)->selectee; break; case AP: ptrs[nptrs++] = ((StgAP *)closure)->fun; heap_view_closure_ptrs_in_pap_payload(ptrs, &nptrs, ((StgAP *)closure)->fun, ((StgAP *)closure)->payload, ((StgAP *)closure)->n_args); break; case PAP: ptrs[nptrs++] = ((StgPAP *)closure)->fun; heap_view_closure_ptrs_in_pap_payload(ptrs, &nptrs, ((StgPAP *)closure)->fun, ((StgPAP *)closure)->payload, ((StgPAP *)closure)->n_args); break; case AP_STACK: ptrs[nptrs++] = ((StgAP_STACK *)closure)->fun; /* The payload is a stack, which consists of a mixture of pointers and non-pointers. We can't simply pretend it's all pointers, because that will cause crashes in the GC later. We could traverse the stack and extract pointers and non-pointers, but that would be complicated, so let's just ignore the payload for now. See #15375. */ break; case BCO: ptrs[nptrs++] = (StgClosure *)((StgBCO *)closure)->instrs; ptrs[nptrs++] = (StgClosure *)((StgBCO *)closure)->literals; ptrs[nptrs++] = (StgClosure *)((StgBCO *)closure)->ptrs; break; case IND: case IND_STATIC: case BLACKHOLE: ptrs[nptrs++] = (StgClosure *)(((StgInd *)closure)->indirectee); break; case MUT_ARR_PTRS_CLEAN: case MUT_ARR_PTRS_DIRTY: case MUT_ARR_PTRS_FROZEN_CLEAN: case MUT_ARR_PTRS_FROZEN_DIRTY: for (i = 0; i < ((StgMutArrPtrs *)closure)->ptrs; ++i) { ptrs[nptrs++] = ((StgMutArrPtrs *)closure)->payload[i]; } break; case SMALL_MUT_ARR_PTRS_CLEAN: case SMALL_MUT_ARR_PTRS_DIRTY: case SMALL_MUT_ARR_PTRS_FROZEN_CLEAN: case SMALL_MUT_ARR_PTRS_FROZEN_DIRTY: for (i = 0; i < ((StgSmallMutArrPtrs *)closure)->ptrs; ++i) { ptrs[nptrs++] = ((StgSmallMutArrPtrs *)closure)->payload[i]; } break; case MUT_VAR_CLEAN: case MUT_VAR_DIRTY: ptrs[nptrs++] = ((StgMutVar *)closure)->var; break; case MVAR_DIRTY: case MVAR_CLEAN: ptrs[nptrs++] = (StgClosure *)((StgMVar *)closure)->head; ptrs[nptrs++] = (StgClosure *)((StgMVar *)closure)->tail; ptrs[nptrs++] = ((StgMVar *)closure)->value; break; case TSO: ASSERT((StgClosure *)((StgTSO *)closure)->_link != NULL); ptrs[nptrs++] = (StgClosure *)((StgTSO *)closure)->_link; ASSERT((StgClosure *)((StgTSO *)closure)->global_link != NULL); ptrs[nptrs++] = (StgClosure *)((StgTSO *)closure)->global_link; ASSERT((StgClosure *)((StgTSO *)closure)->stackobj != NULL); ptrs[nptrs++] = (StgClosure *)((StgTSO *)closure)->stackobj; ASSERT((StgClosure *)((StgTSO *)closure)->trec != NULL); ptrs[nptrs++] = (StgClosure *)((StgTSO *)closure)->trec; ASSERT((StgClosure *)((StgTSO *)closure)->blocked_exceptions != NULL); ptrs[nptrs++] = (StgClosure *)((StgTSO *)closure)->blocked_exceptions; ASSERT((StgClosure *)((StgTSO *)closure)->bq != NULL); ptrs[nptrs++] = (StgClosure *)((StgTSO *)closure)->bq; break; case WEAK: { StgWeak *w = (StgWeak *)closure; ptrs[nptrs++] = (StgClosure *) w->cfinalizers; ptrs[nptrs++] = (StgClosure *) w->key; ptrs[nptrs++] = (StgClosure *) w->value; ptrs[nptrs++] = (StgClosure *) w->finalizer; // link may be NULL which is not a valid GC pointer if (w->link) { ptrs[nptrs++] = (StgClosure *) w->link; } break; } default: fprintf(stderr,"closurePtrs: Cannot handle type %s yet\n", closure_type_names[info->type]); break; } return nptrs; } StgMutArrPtrs *heap_view_closurePtrs(Capability *cap, StgClosure *closure) { ASSERT(LOOKS_LIKE_CLOSURE_PTR(closure)); StgWord size = heap_view_closureSize(closure); // First collect all pointers here, with the comfortable memory bound // of the whole closure. Afterwards we know how many pointers are in // the closure and then we can allocate space on the heap and copy them // there. Note that we cannot allocate this on the C stack as the closure // may be, e.g., a large array. StgClosure **ptrs = (StgClosure **) stgMallocBytes(sizeof(StgClosure *) * size, "heap_view_closurePtrs"); StgWord nptrs = collect_pointers(closure, ptrs); size = nptrs + mutArrPtrsCardTableSize(nptrs); StgMutArrPtrs *arr = (StgMutArrPtrs *)allocate(cap, sizeofW(StgMutArrPtrs) + size); TICK_ALLOC_PRIM(sizeofW(StgMutArrPtrs), nptrs, 0); SET_HDR(arr, &stg_MUT_ARR_PTRS_FROZEN_CLEAN_info, cap->r.rCCCS); arr->ptrs = nptrs; arr->size = size; for (StgWord i = 0; ipayload[i] = ptrs[i]; } free(ptrs); return arr; }