/* * %CopyrightBegin% * * Copyright Ericsson AB 2016-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * %CopyrightEnd% */ #ifndef ERL_NFUNC_SCHED_H__ #define ERL_NFUNC_SCHED_H__ #include "erl_process.h" #include "bif.h" #include "error.h" typedef struct { int applying; Export* ep; BeamInstr *cp; Uint32 flags; Uint32 flags_meta; BeamInstr* I; ErtsTracer meta_tracer; } NifExportTrace; /* * NIF exports need a few more items than the Export struct provides, * including the erl_module_nif* and a NIF function pointer, so the * NifExport below adds those. The Export member must be first in the * struct. A number of values are stored for error handling purposes * only. * * 'argc' is >= 0 when NifExport is in use, and < 0 when not. */ typedef struct { Export exp; struct erl_module_nif* m; /* NIF module, or NULL if BIF */ void *func; /* Indirect NIF or BIF to execute (may be unused) */ ErtsCodeMFA *current;/* Current as set when originally called */ NifExportTrace *trace; /* --- The following is only used on error --- */ BeamInstr *pc; /* Program counter */ BeamInstr *cp; /* Continuation pointer */ ErtsCodeMFA *mfa; /* MFA of original call */ int argc; /* Number of arguments in original call */ int argv_size; /* Allocated size of argv */ Eterm argv[1]; /* Saved arguments from the original call */ } NifExport; NifExport *erts_new_proc_nif_export(Process *c_p, int argc); void erts_nif_export_save_trace(Process *c_p, NifExport *nep, int applying, Export* ep, BeamInstr *cp, Uint32 flags, Uint32 flags_meta, BeamInstr* I, ErtsTracer meta_tracer); void erts_nif_export_restore_trace(Process *c_p, Eterm result, NifExport *nep); void erts_destroy_nif_export(Process *p); NifExport *erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc, ErtsCodeMFA *mfa, BeamInstr *pc, BeamInstr instr, void *dfunc, void *ifunc, Eterm mod, Eterm func, int argc, const Eterm *argv); void erts_nif_export_cleanup_nif_mod(NifExport *ep); /* erl_nif.c */ ERTS_GLB_INLINE NifExport *erts_get_proc_nif_export(Process *c_p, int extra); ERTS_GLB_INLINE int erts_setup_nif_export_rootset(Process* proc, Eterm** objv, Uint* nobj); ERTS_GLB_INLINE int erts_check_nif_export_in_area(Process *p, char *start, Uint size); ERTS_GLB_INLINE void erts_nif_export_restore(Process *c_p, NifExport *ep, Eterm result); ERTS_GLB_INLINE void erts_nif_export_restore_error(Process* c_p, BeamInstr **pc, Eterm *reg, ErtsCodeMFA **nif_mfa); ERTS_GLB_INLINE int erts_nif_export_check_save_trace(Process *c_p, Eterm result, int applying, Export* ep, BeamInstr *cp, Uint32 flags, Uint32 flags_meta, BeamInstr* I, ErtsTracer meta_tracer); ERTS_GLB_INLINE Process *erts_proc_shadow2real(Process *c_p); #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE NifExport * erts_get_proc_nif_export(Process *c_p, int argc) { NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p); if (!nep || (nep->argc < 0 && nep->argv_size < argc)) return erts_new_proc_nif_export(c_p, argc); return nep; } /* * If a process has saved arguments, they need to be part of the GC * rootset. The function below is called from setup_rootset() in * erl_gc.c. Any exception term saved in the NifExport is also made * part of the GC rootset here; it always resides in rootset[0]. */ ERTS_GLB_INLINE int erts_setup_nif_export_rootset(Process* proc, Eterm** objv, Uint* nobj) { NifExport* ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); if (!ep || ep->argc <= 0) return 0; *objv = ep->argv; *nobj = ep->argc; return 1; } /* * Check if nif export points into code area... */ ERTS_GLB_INLINE int erts_check_nif_export_in_area(Process *p, char *start, Uint size) { NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(p); if (!nep || nep->argc < 0) return 0; if (ErtsInArea(nep->pc, start, size)) return 1; if (ErtsInArea(nep->cp, start, size)) return 1; if (ErtsInArea(nep->mfa, start, size)) return 1; if (ErtsInArea(nep->current, start, size)) return 1; return 0; } ERTS_GLB_INLINE void erts_nif_export_restore(Process *c_p, NifExport *ep, Eterm result) { ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_get_scheduler_data())); ERTS_LC_ASSERT(!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)); ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN); c_p->current = ep->current; ep->argc = -1; /* Unused nif-export marker... */ if (ep->trace) erts_nif_export_restore_trace(c_p, result, ep); } ERTS_GLB_INLINE void erts_nif_export_restore_error(Process* c_p, BeamInstr **pc, Eterm *reg, ErtsCodeMFA **nif_mfa) { NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p); int ix; ASSERT(nep); *pc = nep->pc; c_p->cp = nep->cp; *nif_mfa = nep->mfa; for (ix = 0; ix < nep->argc; ix++) reg[ix] = nep->argv[ix]; erts_nif_export_restore(c_p, nep, THE_NON_VALUE); } ERTS_GLB_INLINE int erts_nif_export_check_save_trace(Process *c_p, Eterm result, int applying, Export* ep, BeamInstr *cp, Uint32 flags, Uint32 flags_meta, BeamInstr* I, ErtsTracer meta_tracer) { if (is_non_value(result) && c_p->freason == TRAP) { NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p); if (nep && nep->argc >= 0) { erts_nif_export_save_trace(c_p, nep, applying, ep, cp, flags, flags_meta, I, meta_tracer); return 1; } } return 0; } ERTS_GLB_INLINE Process * erts_proc_shadow2real(Process *c_p) { if (c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC) { Process *real_c_p = c_p->next; ASSERT(ERTS_SCHEDULER_IS_DIRTY(erts_get_scheduler_data())); ASSERT(real_c_p->common.id == c_p->common.id); return real_c_p; } ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_get_scheduler_data())); return c_p; } #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ #endif /* ERL_NFUNC_SCHED_H__ */ #if defined(ERTS_WANT_NFUNC_SCHED_INTERNALS__) && !defined(ERTS_NFUNC_SCHED_INTERNALS__) #define ERTS_NFUNC_SCHED_INTERNALS__ #define ERTS_I_BEAM_OP_TO_NIF_EXPORT(I) \ (ASSERT(BeamIsOpCode(*(I), op_apply_bif) || \ BeamIsOpCode(*(I), op_call_nif)), \ ((NifExport *) (((char *) (I)) - offsetof(NifExport, exp.beam[0])))) #include "erl_message.h" #include ERTS_GLB_INLINE void erts_flush_dirty_shadow_proc(Process *sproc); ERTS_GLB_INLINE void erts_cache_dirty_shadow_proc(Process *sproc); ERTS_GLB_INLINE Process *erts_make_dirty_shadow_proc(ErtsSchedulerData *esdp, Process *c_p); #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE void erts_flush_dirty_shadow_proc(Process *sproc) { Process *c_p = sproc->next; ASSERT(sproc->common.id == c_p->common.id); ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN); ASSERT(c_p->stop == sproc->stop); ASSERT(c_p->hend == sproc->hend); ASSERT(c_p->heap == sproc->heap); ASSERT(c_p->abandoned_heap == sproc->abandoned_heap); ASSERT(c_p->heap_sz == sproc->heap_sz); ASSERT(c_p->high_water == sproc->high_water); ASSERT(c_p->old_heap == sproc->old_heap); ASSERT(c_p->old_htop == sproc->old_htop); ASSERT(c_p->old_hend == sproc->old_hend); ASSERT(c_p->htop <= sproc->htop && sproc->htop <= c_p->stop); c_p->htop = sproc->htop; if (!c_p->mbuf) c_p->mbuf = sproc->mbuf; else if (sproc->mbuf) { ErlHeapFragment *bp; for (bp = sproc->mbuf; bp->next; bp = bp->next) ASSERT(!bp->off_heap.first); bp->next = c_p->mbuf; c_p->mbuf = sproc->mbuf; } c_p->mbuf_sz += sproc->mbuf_sz; if (!c_p->off_heap.first) c_p->off_heap.first = sproc->off_heap.first; else if (sproc->off_heap.first) { struct erl_off_heap_header *ohhp; for (ohhp = sproc->off_heap.first; ohhp->next; ohhp = ohhp->next) ; ohhp->next = c_p->off_heap.first; c_p->off_heap.first = sproc->off_heap.first; } c_p->off_heap.overhead += sproc->off_heap.overhead; } ERTS_GLB_INLINE void erts_cache_dirty_shadow_proc(Process *sproc) { Process *c_p = sproc->next; ASSERT(c_p); ASSERT(sproc->common.id == c_p->common.id); ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN); sproc->htop = c_p->htop; sproc->stop = c_p->stop; sproc->hend = c_p->hend; sproc->heap = c_p->heap; sproc->abandoned_heap = c_p->abandoned_heap; sproc->heap_sz = c_p->heap_sz; sproc->high_water = c_p->high_water; sproc->old_hend = c_p->old_hend; sproc->old_htop = c_p->old_htop; sproc->old_heap = c_p->old_heap; sproc->mbuf = NULL; sproc->mbuf_sz = 0; ERTS_INIT_OFF_HEAP(&sproc->off_heap); } ERTS_GLB_INLINE Process * erts_make_dirty_shadow_proc(ErtsSchedulerData *esdp, Process *c_p) { Process *sproc; ASSERT(ERTS_SCHEDULER_IS_DIRTY(esdp)); sproc = esdp->dirty_shadow_process; ASSERT(sproc); ASSERT(sproc->static_flags & ERTS_STC_FLG_SHADOW_PROC); ASSERT(erts_atomic32_read_nob(&sproc->state) == (ERTS_PSFLG_ACTIVE | ERTS_PSFLG_DIRTY_RUNNING | ERTS_PSFLG_PROXY)); sproc->next = c_p; sproc->common.id = c_p->common.id; erts_cache_dirty_shadow_proc(sproc); return sproc; } #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ #endif /* defined(ERTS_WANT_NFUNC_SCHED_INTERNALS__) && !defined(ERTS_NFUNC_SCHED_INTERNALS__) */