Eterm erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed) { ErtsLiteralArea *la; ErtsMessage *msgp; struct erl_off_heap_header* oh; char *literals; Uint lit_bsize; ErlHeapFragment *hfrag; la = ERTS_COPY_LITERAL_AREA(); if (!la) return am_ok; oh = la->off_heap; literals = (char *) &la->start[0]; lit_bsize = (char *) la->end - literals; /* * If a literal is in the message queue we make an explicit copy of * it and attach it to the heap fragment. Each message needs to be * self contained, we cannot save the literal in the old_heap or * any other heap than the message it self. */ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ); ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ); for (msgp = c_p->msg.first; msgp; msgp = msgp->next) { ErlHeapFragment *hf; Uint lit_sz = 0; *redsp += 1; if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) hfrag = &msgp->hfrag; else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag) hfrag = msgp->data.heap_frag; else continue; /* Content on heap or in external term format... */ for (hf = hfrag; hf; hf = hf->next) { lit_sz += hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size], literals, lit_bsize); *redsp += 1; } *redsp += lit_sz / 16; /* Better value needed... */ if (lit_sz > 0) { ErlHeapFragment *bp = new_message_buffer(lit_sz); Eterm *hp = bp->mem; for (hf = hfrag; hf; hf = hf->next) { hfrag_literal_copy(&hp, &bp->off_heap, &hf->mem[0], &hf->mem[hf->used_size], literals, lit_bsize); hfrag = hf; } /* link new hfrag last */ ASSERT(hfrag->next == NULL); hfrag->next = bp; bp->next = NULL; } } if (gc_allowed) { /* * Current implementation first tests without * allowing GC, and then restarts the operation * allowing GC if it is needed. It is therfore * very likely that we will need the GC (although * this is not completely certain). We go for * the GC directly instead of scanning everything * one more time... */ goto literal_gc; } *redsp += 2; if (any_heap_ref_ptrs(&c_p->fvalue, &c_p->fvalue+1, literals, lit_bsize)) { c_p->freason = EXC_NULL; c_p->fvalue = NIL; c_p->ftrace = NIL; } if (any_heap_ref_ptrs(c_p->stop, c_p->hend, literals, lit_bsize)) goto literal_gc; *redsp += 1; #ifdef HIPE if (nstack_any_heap_ref_ptrs(c_p, literals, lit_bsize)) goto literal_gc; *redsp += 1; #endif if (any_heap_refs(c_p->heap, c_p->htop, literals, lit_bsize)) goto literal_gc; *redsp += 1; if (any_heap_refs(c_p->old_heap, c_p->old_htop, literals, lit_bsize)) goto literal_gc; /* Check dictionary */ *redsp += 1; if (c_p->dictionary) { Eterm* start = ERTS_PD_START(c_p->dictionary); Eterm* end = start + ERTS_PD_SIZE(c_p->dictionary); if (any_heap_ref_ptrs(start, end, literals, lit_bsize)) goto literal_gc; } /* Check heap fragments */ for (hfrag = c_p->mbuf; hfrag; hfrag = hfrag->next) { Eterm *hp, *hp_end; *redsp += 1; hp = &hfrag->mem[0]; hp_end = &hfrag->mem[hfrag->used_size]; if (any_heap_refs(hp, hp_end, literals, lit_bsize)) goto literal_gc; } /* * Message buffer fragments (matched messages) * - off heap lists should already have been moved into * process off heap structure. * - Check for literals */ for (msgp = c_p->msg_frag; msgp; msgp = msgp->next) { hfrag = erts_message_to_heap_frag(msgp); for (; hfrag; hfrag = hfrag->next) { Eterm *hp, *hp_end; *redsp += 1; hp = &hfrag->mem[0]; hp_end = &hfrag->mem[hfrag->used_size]; if (any_heap_refs(hp, hp_end, literals, lit_bsize)) goto literal_gc; } } return am_ok; literal_gc: if (!gc_allowed) return am_need_gc; if (c_p->flags & F_DISABLE_GC) return THE_NON_VALUE; FLAGS(c_p) |= F_NEED_FULLSWEEP; *redsp += erts_garbage_collect_nobump(c_p, 0, c_p->arg_reg, c_p->arity, fcalls); erts_garbage_collect_literals(c_p, (Eterm *) literals, lit_bsize, oh); *redsp += lit_bsize / 64; /* Need, better value... */ return am_ok; }
static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls) { BeamInstr* start; char* literals; Uint lit_bsize; char* mod_start; Uint mod_size; Eterm* sp; int done_gc = 0; int need_gc = 0; ErtsMessage *msgp; ErlHeapFragment *hfrag; #define ERTS_ORDINARY_GC__ (1 << 0) #define ERTS_LITERAL_GC__ (1 << 1) /* * Pick up limits for the module. */ start = (BeamInstr*) modp->old.code_hdr; mod_start = (char *) start; mod_size = modp->old.code_length; /* * Check if current instruction or continuation pointer points into module. */ if (ErtsInArea(rp->i, mod_start, mod_size) || ErtsInArea(rp->cp, mod_start, mod_size)) { return am_true; } /* * Check all continuation pointers stored on the stack. */ for (sp = rp->stop; sp < STACK_START(rp); sp++) { if (is_CP(*sp) && ErtsInArea(cp_val(*sp), mod_start, mod_size)) { return am_true; } } /* * Check all continuation pointers stored in stackdump * and clear exception stackdump if there is a pointer * to the module. */ if (rp->ftrace != NIL) { struct StackTrace *s; ASSERT(is_list(rp->ftrace)); s = (struct StackTrace *) big_val(CDR(list_val(rp->ftrace))); if ((s->pc && ErtsInArea(s->pc, mod_start, mod_size)) || (s->current && ErtsInArea(s->current, mod_start, mod_size))) { rp->freason = EXC_NULL; rp->fvalue = NIL; rp->ftrace = NIL; } else { int i; for (i = 0; i < s->depth; i++) { if (ErtsInArea(s->trace[i], mod_start, mod_size)) { rp->freason = EXC_NULL; rp->fvalue = NIL; rp->ftrace = NIL; break; } } } } if (rp->flags & F_DISABLE_GC) { /* * Cannot proceed. Process has disabled gc in order to * safely leave inconsistent data on the heap and/or * off heap lists. Need to wait for gc to be enabled * again. */ return THE_NON_VALUE; } /* * Message queue can contains funs, and may contain * literals. If we got references to this module from the message * queue. * * If a literal is in the message queue we maka an explicit copy of * and attach it to the heap fragment. Each message needs to be * self contained, we cannot save the literal in the old_heap or * any other heap than the message it self. */ erts_smp_proc_lock(rp, ERTS_PROC_LOCK_MSGQ); ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ); literals = (char*) modp->old.code_hdr->literals_start; lit_bsize = (char*) modp->old.code_hdr->literals_end - literals; for (msgp = rp->msg.first; msgp; msgp = msgp->next) { if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) hfrag = &msgp->hfrag; else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag) hfrag = msgp->data.heap_frag; else continue; { ErlHeapFragment *hf; Uint lit_sz = 0; for (hf=hfrag; hf; hf = hf->next) { if (check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)) return am_true; lit_sz += hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size], literals, lit_bsize); } if (lit_sz > 0) { ErlHeapFragment *bp = new_message_buffer(lit_sz); Eterm *hp = bp->mem; for (hf=hfrag; hf; hf = hf->next) { hfrag_literal_copy(&hp, &bp->off_heap, &hf->mem[0], &hf->mem[hf->used_size], literals, lit_bsize); hfrag=hf; } /* link new hfrag last */ ASSERT(hfrag->next == NULL); hfrag->next = bp; bp->next = NULL; } } } while (1) { /* Check heap, stack etc... */ if (check_mod_funs(rp, &rp->off_heap, mod_start, mod_size)) goto try_gc; if (!(flags & ERTS_CPC_COPY_LITERALS)) { /* Process ok. May contain old literals but we will be called * again before module is purged. */ return am_false; } if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, literals, lit_bsize)) { rp->freason = EXC_NULL; rp->fvalue = NIL; rp->ftrace = NIL; } if (any_heap_ref_ptrs(rp->stop, rp->hend, literals, lit_bsize)) goto try_literal_gc; #ifdef HIPE if (nstack_any_heap_ref_ptrs(rp, literals, lit_bsize)) goto try_literal_gc; #endif if (any_heap_refs(rp->heap, rp->htop, literals, lit_bsize)) goto try_literal_gc; if (any_heap_refs(rp->old_heap, rp->old_htop, literals, lit_bsize)) goto try_literal_gc; /* Check dictionary */ if (rp->dictionary) { Eterm* start = ERTS_PD_START(rp->dictionary); Eterm* end = start + ERTS_PD_SIZE(rp->dictionary); if (any_heap_ref_ptrs(start, end, literals, lit_bsize)) goto try_literal_gc; } /* Check heap fragments */ for (hfrag = rp->mbuf; hfrag; hfrag = hfrag->next) { Eterm *hp, *hp_end; /* Off heap lists should already have been moved into process */ ASSERT(!check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)); hp = &hfrag->mem[0]; hp_end = &hfrag->mem[hfrag->used_size]; if (any_heap_refs(hp, hp_end, literals, lit_bsize)) goto try_literal_gc; } /* * Message buffer fragments (matched messages) * - off heap lists should already have been moved into * process off heap structure. * - Check for literals */ for (msgp = rp->msg_frag; msgp; msgp = msgp->next) { hfrag = erts_message_to_heap_frag(msgp); for (; hfrag; hfrag = hfrag->next) { Eterm *hp, *hp_end; ASSERT(!check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size)); hp = &hfrag->mem[0]; hp_end = &hfrag->mem[hfrag->used_size]; if (any_heap_refs(hp, hp_end, literals, lit_bsize)) goto try_literal_gc; } } return am_false; try_literal_gc: need_gc |= ERTS_LITERAL_GC__; try_gc: need_gc |= ERTS_ORDINARY_GC__; if ((done_gc & need_gc) == need_gc) return am_true; if (!(flags & ERTS_CPC_ALLOW_GC)) return am_aborted; need_gc &= ~done_gc; /* * Try to get rid of literals by by garbage collecting. * Clear both fvalue and ftrace. */ rp->freason = EXC_NULL; rp->fvalue = NIL; rp->ftrace = NIL; if (need_gc & ERTS_ORDINARY_GC__) { FLAGS(rp) |= F_NEED_FULLSWEEP; *redsp += erts_garbage_collect_nobump(rp, 0, rp->arg_reg, rp->arity, fcalls); done_gc |= ERTS_ORDINARY_GC__; } if (need_gc & ERTS_LITERAL_GC__) { struct erl_off_heap_header* oh; oh = modp->old.code_hdr->literals_off_heap; *redsp += lit_bsize / 64; /* Need, better value... */ erts_garbage_collect_literals(rp, (Eterm*)literals, lit_bsize, oh); done_gc |= ERTS_LITERAL_GC__; } need_gc = 0; } #undef ERTS_ORDINARY_GC__ #undef ERTS_LITERAL_GC__ }