diff options
Diffstat (limited to 'erts/emulator')
143 files changed, 6831 insertions, 12332 deletions
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 2efbe2d57e..7e966c81bb 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -136,13 +136,6 @@ ENABLE_ALLOC_TYPE_VARS += smp nofrag M4FLAGS += -DERTS_SMP=1 else -ifeq ($(FLAVOR),hybrid) -FLAVOR_MARKER=.hybrid -FLAVOR_FLAGS=-DHYBRID -ENABLE_ALLOC_TYPE_VARS += hybrid -else - - # If flavor isn't one of the above, it *is* plain flavor... override FLAVOR=plain FLAVOR_MARKER= @@ -151,7 +144,6 @@ ENABLE_ALLOC_TYPE_VARS += nofrag M4FLAGS += endif -endif TF_MARKER=$(TYPEMARKER)$(FLAVOR_MARKER) @@ -340,15 +332,11 @@ LIBS += $(THR_LIBS) ifneq ($(findstring erts_internal_r, $(THR_LIBS)),erts_internal_r) -ifeq ($(findstring vxworks,$(TARGET)),vxworks) -ERTS_INTERNAL_LIB=erts_internal -else ifneq ($(strip $(THR_LIB_NAME)),) ERTS_INTERNAL_LIB=erts_internal_r else ERTS_INTERNAL_LIB=erts_internal endif -endif LIBS += -l$(ERTS_INTERNAL_LIB)$(TYPEMARKER) @@ -444,18 +432,18 @@ release_spec: @echo $(VOID_EMULATOR)' - omitted target release_spec (install)' else release_spec: all - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/man - $(INSTALL_DIR) $(RELSYSDIR)/doc - $(INSTALL_DIR) $(RELSYSDIR)/bin - $(INSTALL_DIR) $(RELEASE_PATH)/usr/include - $(INSTALL_DATA) $(RELEASE_INCLUDES) $(RELEASE_PATH)/usr/include - $(INSTALL_DATA) $(RELEASE_INCLUDES) $(RELSYSDIR)/include - $(INSTALL_PROGRAM) $(BINDIR)/$(EMULATOR_EXECUTABLE) $(RELSYSDIR)/bin + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/man" + $(INSTALL_DIR) "$(RELSYSDIR)/doc" + $(INSTALL_DIR) "$(RELSYSDIR)/bin" + $(INSTALL_DIR) "$(RELEASE_PATH)/usr/include" + $(INSTALL_DATA) $(RELEASE_INCLUDES) "$(RELEASE_PATH)/usr/include" + $(INSTALL_DATA) $(RELEASE_INCLUDES) "$(RELSYSDIR)/include" + $(INSTALL_PROGRAM) $(BINDIR)/$(EMULATOR_EXECUTABLE) "$(RELSYSDIR)/bin" ifeq ($(ERLANG_OSTYPE), unix) - $(INSTALL_PROGRAM) $(BINDIR)/$(CS_EXECUTABLE) $(RELSYSDIR)/bin + $(INSTALL_PROGRAM) $(BINDIR)/$(CS_EXECUTABLE) "$(RELSYSDIR)/bin" endif endif @@ -471,8 +459,6 @@ _create_dirs := $(shell mkdir -p $(CREATE_DIRS)) GENERATE = HIPE_ASM = -ifeq ($(findstring vxworks,$(TARGET)),vxworks) -else ifdef HIPE_ENABLED HIPE_ASM += $(TTF_DIR)/hipe_x86_asm.h \ $(TTF_DIR)/hipe_amd64_asm.h \ @@ -484,7 +470,6 @@ GENERATE += $(HIPE_ASM) \ $(TTF_DIR)/hipe_literals.h \ $(BINDIR)/hipe_mkliterals$(TF_MARKER) endif -endif ifdef DTRACE_ENABLED # global.h causes problems by including dtrace-wrapper.h which includes @@ -624,11 +609,6 @@ ifdef PERFCTR_PATH INCLUDES += -I$(PERFCTR_PATH)/usr.lib -I$(PERFCTR_PATH)/linux/include endif -# Need to include etc dir on VxWorks -ifeq ($(findstring vxworks,$(TARGET)),vxworks) -INCLUDES += -I$(ERL_TOP)/erts/etc/vxworks -endif - ifeq ($(TARGET),win32) $(OBJDIR)/dll_sys.o: sys/$(ERLANG_OSTYPE)/sys.c $(CC) $(CFLAGS) -DERL_RUN_SHARED_LIB=1 $(INCLUDES) -c $< -o $@ @@ -671,12 +651,6 @@ $(OBJDIR)/%.o: drivers/common/%.c $(OBJDIR)/%.o: drivers/$(ERLANG_OSTYPE)/%.c $(CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLANG_OSTYPE) -I../etc/$(ERLANG_OSTYPE) -c $< -o $@ -# VxWorks uses unix drivers too... -ifeq ($(findstring vxworks,$(TARGET)),vxworks) -$(OBJDIR)/%.o: drivers/unix/%.c - $(CC) $(CFLAGS) $(INCLUDES) -Idrivers/common -c $< -o $@ -endif - # ---------------------------------------------------------------------- # Specials # @@ -717,7 +691,9 @@ EMU_OBJS = \ $(OBJDIR)/beam_emu.o $(OBJDIR)/beam_opcodes.o \ $(OBJDIR)/beam_load.o $(OBJDIR)/beam_bif_load.o \ $(OBJDIR)/beam_debug.o $(OBJDIR)/beam_bp.o \ - $(OBJDIR)/beam_catches.o + $(OBJDIR)/beam_catches.o \ + $(OBJDIR)/code_ix.o \ + $(OBJDIR)/beam_ranges.o RUN_OBJS = \ $(OBJDIR)/erl_pbifs.o $(OBJDIR)/benchmark.o \ @@ -748,7 +724,7 @@ RUN_OBJS = \ $(OBJDIR)/register.o $(OBJDIR)/break.o \ $(OBJDIR)/erl_async.o $(OBJDIR)/erl_lock_check.o \ $(OBJDIR)/erl_gc.o $(OBJDIR)/erl_lock_count.o \ - $(OBJDIR)/erl_nmgc.o $(OBJDIR)/erl_posix_str.o \ + $(OBJDIR)/erl_posix_str.o \ $(OBJDIR)/erl_bits.o $(OBJDIR)/erl_math.o \ $(OBJDIR)/erl_fun.o $(OBJDIR)/erl_bif_port.o \ $(OBJDIR)/erl_term.o $(OBJDIR)/erl_node_tables.o \ @@ -787,12 +763,8 @@ OS_OBJS = \ $(OBJDIR)/gzio.o \ $(OBJDIR)/elib_memmove.o -ifeq ($(findstring vxworks,$(TARGET)),vxworks) - OS_OBJS += $(OBJDIR)/int64.o -else OS_OBJS += $(OBJDIR)/sys_float.o \ $(OBJDIR)/sys_time.o -endif DRV_OBJS = \ $(OBJDIR)/efile_drv.o \ $(OBJDIR)/inet_drv.o \ @@ -800,9 +772,7 @@ DRV_OBJS = \ $(OBJDIR)/ram_file_drv.o endif -ifneq ($(findstring vxworks,$(TARGET)),vxworks) DRV_OBJS += $(OBJDIR)/ttsl_drv.o -endif ifeq ($(ERTS_ENABLE_KERNEL_POLL),yes) OS_OBJS += $(OBJDIR)/erl_poll.kp.o \ @@ -932,33 +902,13 @@ $(TTF_DIR)/hipe_arm_bifs.S: hipe/hipe_arm_bifs.m4 hipe/hipe_arm_asm.m4 \ $(OBJDIR)/hipe_arm_bifs.o: $(TTF_DIR)/hipe_arm_bifs.S \ $(TTF_DIR)/hipe_literals.h -# end of HiPE section -######################################## +# Use -fomit-frame-pointer to work around gcc (v4.5.2) bug causing +# "error: r7 cannot be used in asm here" for DEBUG build. +$(OBJDIR)/hipe_arm.o: hipe/hipe_arm.c + $(CC) $(subst O2,O3, $(CFLAGS)) -fomit-frame-pointer $(INCLUDES) -c $< -o $@ -ifeq ($(findstring vxworks,$(TARGET)),vxworks) -######################################## -# Extract what we need from libgcc.a +# end of HiPE section ######################################## -GCCLIBFLAGS=@GCCLIBFLAGS@ -STRIP=@STRIP@ -SYMPREFIX=@SYMPREFIX@ - -NEEDFUNCTIONS=__divdi3 __moddi3 __udivdi3 -KEEPSYMS=$(NEEDFUNCTIONS:%=-K $(SYMPREFIX)%) - -$(OBJDIR)/int64.o: $(TARGET)/int64.c - $(CC) -o $(OBJDIR)/int64tmp.o -c $(TARGET)/int64.c - $(LD) -o $(OBJDIR)/int64.o $(OBJDIR)/int64tmp.o $(LDFLAGS) $(GCCLIBFLAGS) - $(STRIP) $(KEEPSYMS) $(OBJDIR)/int64.o - -$(TARGET)/int64.c: - echo 'void dummy(void); void dummy(void) {' > $(TARGET)/int64.c - for x in $(NEEDFUNCTIONS); do echo 'extern void '$$x'();' \ - >> $(TARGET)/int64.c; done - for x in $(NEEDFUNCTIONS); do echo $$x'();' >> $(TARGET)/int64.c; done - echo '}' >> $(TARGET)/int64.c - -endif # ---------------------------------------------------------------------- # The emulator itself diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 02735d4b68..106fad030b 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -244,7 +244,6 @@ atom gather_sched_wall_time_result atom getting_linked atom getting_unlinked atom global -atom global_heaps_size atom Gt='>' atom grun atom group_leader @@ -259,7 +258,6 @@ atom hide atom high atom hipe_architecture atom http httph https http_response http_request http_header http_eoh http_error http_bin httph_bin -atom hybrid atom id atom if_clause atom imports @@ -391,6 +389,7 @@ atom opt atom or atom ordered_set atom orelse +atom os_pid atom os_type atom os_version atom ose_bg_proc diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 78a9d76a20..94f8edf165 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -37,25 +37,83 @@ static void set_default_trace_pattern(Eterm module); static Eterm check_process_code(Process* rp, Module* modp); -static void delete_code(Process *c_p, ErtsProcLocks c_p_locks, Module* modp); -static void delete_export_references(Eterm module); -static int purge_module(int module); +static void delete_code(Module* modp); static void decrement_refc(BeamInstr* code); static int is_native(BeamInstr* code); static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size); static int any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size); -static void remove_from_address_table(BeamInstr* code); -Eterm -load_module_2(BIF_ALIST_2) + + +BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1) +{ + Module* modp; + Eterm res; + ErtsCodeIndex code_ix; + + if (is_not_atom(BIF_ARG_1)) { + BIF_ERROR(BIF_P, BADARG); + } + code_ix = erts_active_code_ix(); + if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) { + return am_undefined; + } + erts_rlock_old_code(code_ix); + res = ((modp->curr.code && is_native(modp->curr.code)) || + (modp->old.code != 0 && is_native(modp->old.code))) ? + am_true : am_false; + erts_runlock_old_code(code_ix); + return res; +} + +BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3) { - Eterm reason; - Eterm* hp; - int sz; - byte* code; + Module* modp; Eterm res; + + if (!erts_try_seize_code_write_permission(BIF_P)) { + ERTS_BIF_YIELD3(bif_export[BIF_code_make_stub_module_3], + BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); + } + + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + + modp = erts_get_module(BIF_ARG_1, erts_active_code_ix()); + + if (modp && modp->curr.num_breakpoints > 0) { + ASSERT(modp->curr.code != NULL); + erts_clear_module_break(modp); + ASSERT(modp->curr.num_breakpoints == 0); + } + + erts_start_staging_code_ix(); + + res = erts_make_stub_module(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); + + if (res == BIF_ARG_1) { + erts_end_staging_code_ix(); + erts_commit_staging_code_ix(); + } + else { + erts_abort_staging_code_ix(); + } + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_release_code_write_permission(); + return res; +} + +BIF_RETTYPE +prepare_loading_2(BIF_ALIST_2) +{ byte* temp_alloc = NULL; - struct LoaderState* stp; + byte* code; + Uint sz; + Binary* magic; + Eterm reason; + Eterm* hp; + Eterm res; if (is_not_atom(BIF_ARG_1)) { error: @@ -65,108 +123,277 @@ load_module_2(BIF_ALIST_2) if ((code = erts_get_aligned_binary_bytes(BIF_ARG_2, &temp_alloc)) == NULL) { goto error; } - hp = HAlloc(BIF_P, 3); - /* - * Read the BEAM file and prepare the module for loading. - */ - stp = erts_alloc_loader_state(); + magic = erts_alloc_loader_state(); sz = binary_size(BIF_ARG_2); - reason = erts_prepare_loading(stp, BIF_P, BIF_P->group_leader, + reason = erts_prepare_loading(magic, BIF_P, BIF_P->group_leader, &BIF_ARG_1, code, sz); erts_free_aligned_binary_bytes(temp_alloc); if (reason != NIL) { + hp = HAlloc(BIF_P, 3); res = TUPLE2(hp, am_error, reason); BIF_RET(res); } + hp = HAlloc(BIF_P, PROC_BIN_SIZE); + res = erts_mk_magic_binary_term(&hp, &MSO(BIF_P), magic); + erts_refc_dec(&magic->refc, 1); + BIF_RET(res); +} - /* - * Stop all other processes and finish the loading of the module. - */ - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); +struct m { + Binary* code; + Eterm module; + Module* modp; + Uint exception; +}; - reason = erts_finish_loading(stp, BIF_P, 0, &BIF_ARG_1); - if (reason != NIL) { - res = TUPLE2(hp, am_error, reason); - } else { - set_default_trace_pattern(BIF_ARG_1); - res = TUPLE2(hp, am_module, BIF_ARG_1); - } +static Eterm staging_epilogue(Process* c_p, int, Eterm res, int, struct m*, int); - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_RET(res); +static Eterm +exception_list(Process* p, Eterm tag, struct m* mp, Sint exceptions) +{ + Eterm* hp = HAlloc(p, 3 + 2*exceptions); + Eterm res = NIL; + + mp += exceptions - 1; + while (exceptions > 0) { + if (mp->exception) { + res = CONS(hp, mp->module, res); + hp += 2; + exceptions--; + } + mp--; + } + return TUPLE2(hp, tag, res); } -BIF_RETTYPE purge_module_1(BIF_ALIST_1) + +BIF_RETTYPE +finish_loading_1(BIF_ALIST_1) { - int purge_res; + int i; + int n; + struct m* p = NULL; + Uint exceptions; + Eterm res; + int is_blocking = 0; + int do_commit = 0; - if (is_not_atom(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); + if (!erts_try_seize_code_write_permission(BIF_P)) { + ERTS_BIF_YIELD1(bif_export[BIF_finish_loading_1], BIF_P, BIF_ARG_1); } - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); + /* + * Validate the argument before we start loading; it must be a + * proper list where each element is a magic binary containing + * prepared (not previously loaded) code. + * + * First count the number of elements and allocate an array + * to keep the elements in. + */ - erts_export_consolidate(); - purge_res = purge_module(atom_val(BIF_ARG_1)); + n = list_length(BIF_ARG_1); + if (n == -1) { + ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG); + goto done; + } + p = erts_alloc(ERTS_ALC_T_LOADER_TMP, n*sizeof(struct m)); - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + /* + * We now know that the argument is a proper list. Validate + * and collect the binaries into the array. + */ - if (purge_res < 0) { - BIF_ERROR(BIF_P, BADARG); + for (i = 0; i < n; i++) { + Eterm* cons = list_val(BIF_ARG_1); + Eterm term = CAR(cons); + ProcBin* pb; + + if (!ERTS_TERM_IS_MAGIC_BINARY(term)) { + ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG); + goto done; + } + pb = (ProcBin*) binary_val(term); + p[i].code = pb->val; + p[i].module = erts_module_for_prepared_code(p[i].code); + if (p[i].module == NIL) { + ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG); + goto done; + } + BIF_ARG_1 = CDR(cons); } - BIF_RET(am_true); -} -BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1) -{ - Module* modp; + /* + * Since we cannot handle atomic loading of a group of modules + * if one or more of them uses on_load, we will only allow one + * element in the list. This limitation is intended to be + * lifted in the future. + */ - if (is_not_atom(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); + if (n > 1) { + ERTS_BIF_PREP_ERROR(res, BIF_P, SYSTEM_LIMIT); + goto done; } - if ((modp = erts_get_module(BIF_ARG_1)) == NULL) { - return am_undefined; + + /* + * All types are correct. There cannot be a BADARG from now on. + * Before we can start loading, we must check whether any of + * the modules already has old code. To avoid a race, we must + * not allow other process to initiate a code loading operation + * from now on. + */ + + res = am_ok; + erts_start_staging_code_ix(); + + for (i = 0; i < n; i++) { + p[i].modp = erts_put_module(p[i].module); + } + for (i = 0; i < n; i++) { + if (p[i].modp->curr.num_breakpoints > 0 || + p[i].modp->curr.num_traced_exports > 0 || + erts_is_default_trace_enabled()) { + /* tracing involved, fallback with thread blocking */ + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + is_blocking = 1; + break; + } } - return ((modp->code && is_native(modp->code)) || - (modp->old_code != 0 && is_native(modp->old_code))) ? - am_true : am_false; -} -BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3) -{ - Eterm res; + if (is_blocking) { + for (i = 0; i < n; i++) { + if (p[i].modp->curr.num_breakpoints) { + erts_clear_module_break(p[i].modp); + ASSERT(p[i].modp->curr.num_breakpoints == 0); + } + } + } - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); + exceptions = 0; + for (i = 0; i < n; i++) { + p[i].exception = 0; + if (p[i].modp->curr.code && p[i].modp->old.code) { + p[i].exception = 1; + exceptions++; + } + } - erts_export_consolidate(); - res = erts_make_stub_module(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); + if (exceptions) { + res = exception_list(BIF_P, am_not_purged, p, exceptions); + } else { + /* + * Now we can load all code. This can't fail. + */ - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - return res; + exceptions = 0; + for (i = 0; i < n; i++) { + Eterm mod; + Eterm retval; + + erts_refc_inc(&p[i].code->refc, 1); + retval = erts_finish_loading(p[i].code, BIF_P, 0, &mod); + ASSERT(retval == NIL || retval == am_on_load); + if (retval == am_on_load) { + p[i].exception = 1; + exceptions++; + } + } + + /* + * Check whether any module has an on_load_handler. + */ + + if (exceptions) { + res = exception_list(BIF_P, am_on_load, p, exceptions); + } + do_commit = 1; + } + +done: + return staging_epilogue(BIF_P, do_commit, res, is_blocking, p, n); +} + +static Eterm +staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking, + struct m* loaded, int nloaded) +{ +#ifdef ERTS_SMP + if (is_blocking || !commit) +#endif + { + if (commit) { + erts_end_staging_code_ix(); + erts_commit_staging_code_ix(); + if (loaded) { + int i; + for (i=0; i < nloaded; i++) { + set_default_trace_pattern(loaded[i].module); + } + } + } + else { + erts_abort_staging_code_ix(); + } + if (loaded) { + erts_free(ERTS_ALC_T_LOADER_TMP, loaded); + } + if (is_blocking) { + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); + } + erts_release_code_write_permission(); + return res; + } +#ifdef ERTS_SMP + else { + ErtsThrPrgrVal later; + ASSERT(is_value(res)); + + if (loaded) { + erts_free(ERTS_ALC_T_LOADER_TMP, loaded); + } + erts_end_staging_code_ix(); + /* + * Now we must wait for all schedulers to do a memory barrier before + * we can activate and let them access the new staged code. This allows + * schedulers to read active code_ix in a safe way while executing + * without any memory barriers at all. + */ + + later = erts_thr_progress_later(c_p->scheduler_data); + erts_thr_progress_wakeup(c_p->scheduler_data, later); + erts_notify_code_ix_activation(c_p, later); + erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); + /* + * handle_code_ix_activation() will do the rest "later" + * and resume this process to return 'res'. + */ + ERTS_BIF_YIELD_RETURN(c_p, res); + } +#endif } BIF_RETTYPE check_old_code_1(BIF_ALIST_1) { + ErtsCodeIndex code_ix; Module* modp; + Eterm res = am_false; if (is_not_atom(BIF_ARG_1)) { BIF_ERROR(BIF_P, BADARG); } - modp = erts_get_module(BIF_ARG_1); - if (modp == NULL) { /* Doesn't exist. */ - BIF_RET(am_false); - } else if (modp->old_code == NULL) { /* No old code. */ - BIF_RET(am_false); + code_ix = erts_active_code_ix(); + modp = erts_get_module(BIF_ARG_1, code_ix); + if (modp != NULL) { + erts_rlock_old_code(code_ix); + if (modp->old.code != NULL) { + res = am_true; + } + erts_runlock_old_code(code_ix); } - BIF_RET(am_true); + BIF_RET(res); } Eterm @@ -180,14 +407,20 @@ check_process_code_2(BIF_ALIST_2) } if (is_internal_pid(BIF_ARG_1)) { Eterm res; + ErtsCodeIndex code_ix; if (internal_pid_index(BIF_ARG_1) >= erts_max_processes) goto error; - modp = erts_get_module(BIF_ARG_2); + code_ix = erts_active_code_ix(); + modp = erts_get_module(BIF_ARG_2, code_ix); if (modp == NULL) { /* Doesn't exist. */ return am_false; - } else if (modp->old_code == NULL) { /* No old code. */ + } + erts_rlock_old_code(code_ix); + if (modp->old.code == NULL) { /* No old code. */ + erts_runlock_old_code(code_ix); return am_false; } + erts_runlock_old_code(code_ix); #ifdef ERTS_SMP rp = erts_pid2proc_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, @@ -202,7 +435,14 @@ check_process_code_2(BIF_ALIST_2) ERTS_BIF_YIELD2(bif_export[BIF_check_process_code_2], BIF_P, BIF_ARG_1, BIF_ARG_2); } - res = check_process_code(rp, modp); + erts_rlock_old_code(code_ix); + if (modp->old.code != NULL) { /* must check again */ + res = check_process_code(rp, modp); + } + else { + res = am_false; + } + erts_runlock_old_code(code_ix); #ifdef ERTS_SMP if (BIF_P != rp) { erts_resume(rp, ERTS_PROC_LOCK_MAIN); @@ -223,56 +463,71 @@ check_process_code_2(BIF_ALIST_2) BIF_RETTYPE delete_module_1(BIF_ALIST_1) { - int res; + ErtsCodeIndex code_ix; + Module* modp; + int is_blocking = 0; + int success = 0; + Eterm res = NIL; - if (is_not_atom(BIF_ARG_1)) - goto badarg; + if (is_not_atom(BIF_ARG_1)) { + BIF_ERROR(BIF_P, BADARG); + } - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); + if (!erts_try_seize_code_write_permission(BIF_P)) { + ERTS_BIF_YIELD1(bif_export[BIF_delete_module_1], BIF_P, BIF_ARG_1); + } { - Module *modp = erts_get_module(BIF_ARG_1); + erts_start_staging_code_ix(); + code_ix = erts_staging_code_ix(); + modp = erts_get_module(BIF_ARG_1, code_ix); if (!modp) { res = am_undefined; } - else if (modp->old_code != 0) { + else if (modp->old.code != 0) { erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Module %T must be purged before loading\n", BIF_ARG_1); erts_send_error_to_logger(BIF_P->group_leader, dsbufp); - res = am_badarg; + ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG); } else { - delete_export_references(BIF_ARG_1); - delete_code(BIF_P, 0, modp); + if (modp->curr.num_breakpoints > 0 || + modp->curr.num_traced_exports > 0) { + /* we have tracing, retry single threaded */ + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + is_blocking = 1; + if (modp->curr.num_breakpoints) { + erts_clear_module_break(modp); + ASSERT(modp->curr.num_breakpoints == 0); + } + } + delete_code(modp); res = am_true; + success = 1; } } - - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - - if (res == am_badarg) { - badarg: - BIF_ERROR(BIF_P, BADARG); - } - BIF_RET(res); + return staging_epilogue(BIF_P, success, res, is_blocking, NULL, 0); } BIF_RETTYPE module_loaded_1(BIF_ALIST_1) { Module* modp; + ErtsCodeIndex code_ix; + Eterm res = am_false; if (is_not_atom(BIF_ARG_1)) { BIF_ERROR(BIF_P, BADARG); } - if ((modp = erts_get_module(BIF_ARG_1)) == NULL || - modp->code == NULL || - modp->code[MI_ON_LOAD_FUNCTION_PTR] != 0) { - BIF_RET(am_false); + code_ix = erts_active_code_ix(); + if ((modp = erts_get_module(BIF_ARG_1, code_ix)) != NULL) { + if (modp->curr.code != NULL + && modp->curr.code[MI_ON_LOAD_FUNCTION_PTR] == 0) { + res = am_true; + } } - BIF_RET(am_true); + BIF_RET(res); } BIF_RETTYPE pre_loaded_0(BIF_ALIST_0) @@ -282,27 +537,28 @@ BIF_RETTYPE pre_loaded_0(BIF_ALIST_0) BIF_RETTYPE loaded_0(BIF_ALIST_0) { + ErtsCodeIndex code_ix = erts_active_code_ix(); + Module* modp; Eterm previous = NIL; Eterm* hp; int i; int j = 0; - - for (i = 0; i < module_code_size(); i++) { - if (module_code(i) != NULL && - ((module_code(i)->code_length != 0) || - (module_code(i)->old_code_length != 0))) { + + for (i = 0; i < module_code_size(code_ix); i++) { + if ((modp = module_code(i, code_ix)) != NULL && + ((modp->curr.code_length != 0) || + (modp->old.code_length != 0))) { j++; } } if (j > 0) { hp = HAlloc(BIF_P, j*2); - for (i = 0; i < module_code_size(); i++) { - if (module_code(i) != NULL && - ((module_code(i)->code_length != 0) || - (module_code(i)->old_code_length != 0))) { - previous = CONS(hp, make_atom(module_code(i)->module), - previous); + for (i = 0; i < module_code_size(code_ix); i++) { + if ((modp=module_code(i,code_ix)) != NULL && + ((modp->curr.code_length != 0) || + (modp->old.code_length != 0))) { + previous = CONS(hp, make_atom(modp->module), previous); hp += 2; } } @@ -312,54 +568,65 @@ BIF_RETTYPE loaded_0(BIF_ALIST_0) BIF_RETTYPE call_on_load_function_1(BIF_ALIST_1) { - Module* modp = erts_get_module(BIF_ARG_1); - Eterm on_load; + Module* modp = erts_get_module(BIF_ARG_1, erts_active_code_ix()); - if (!modp || modp->code == 0) { - error: - BIF_ERROR(BIF_P, BADARG); + if (modp && modp->curr.code) { + BIF_TRAP_CODE_PTR_0(BIF_P, modp->curr.code[MI_ON_LOAD_FUNCTION_PTR]); } - if ((on_load = modp->code[MI_ON_LOAD_FUNCTION_PTR]) == 0) { - goto error; + else { + BIF_ERROR(BIF_P, BADARG); } - BIF_TRAP_CODE_PTR_0(BIF_P, on_load); } BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) { - Module* modp = erts_get_module(BIF_ARG_1); + ErtsCodeIndex code_ix; + Module* modp; Eterm on_load; - if (!modp || modp->code == 0) { + if (!erts_try_seize_code_write_permission(BIF_P)) { + ERTS_BIF_YIELD2(bif_export[BIF_finish_after_on_load_2], + BIF_P, BIF_ARG_1, BIF_ARG_2); + } + + /* ToDo: Use code_ix staging instead of thread blocking */ + + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + + code_ix = erts_active_code_ix(); + modp = erts_get_module(BIF_ARG_1, code_ix); + + if (!modp || modp->curr.code == 0) { error: + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_release_code_write_permission(); BIF_ERROR(BIF_P, BADARG); } - if ((on_load = modp->code[MI_ON_LOAD_FUNCTION_PTR]) == 0) { + if ((on_load = modp->curr.code[MI_ON_LOAD_FUNCTION_PTR]) == 0) { goto error; } if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) { goto error; } - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - if (BIF_ARG_2 == am_true) { int i; /* * The on_load function succeded. Fix up export entries. */ - for (i = 0; i < export_list_size(); i++) { - Export *ep = export_list(i); + for (i = 0; i < export_list_size(code_ix); i++) { + Export *ep = export_list(i,code_ix); if (ep != NULL && ep->code[0] == BIF_ARG_1 && ep->code[4] != 0) { - ep->address = (void *) ep->code[4]; + ep->addressv[code_ix] = (void *) ep->code[4]; ep->code[4] = 0; } } - modp->code[MI_ON_LOAD_FUNCTION_PTR] = 0; + modp->curr.code[MI_ON_LOAD_FUNCTION_PTR] = 0; set_default_trace_pattern(BIF_ARG_1); } else if (BIF_ARG_2 == am_false) { BeamInstr* code; @@ -370,19 +637,21 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2) * This is an combination of delete and purge. We purge * the current code; the old code is not touched. */ - erts_total_code_size -= modp->code_length; - code = modp->code; - end = (BeamInstr *)((char *)code + modp->code_length); + erts_total_code_size -= modp->curr.code_length; + code = modp->curr.code; + end = (BeamInstr *)((char *)code + modp->curr.code_length); erts_cleanup_funs_on_purge(code, end); - beam_catches_delmod(modp->catches, code, modp->code_length); + beam_catches_delmod(modp->curr.catches, code, modp->curr.code_length, + erts_active_code_ix()); erts_free(ERTS_ALC_T_CODE, (void *) code); - modp->code = NULL; - modp->code_length = 0; - modp->catches = BEAM_CATCHES_NIL; - remove_from_address_table(code); + modp->curr.code = NULL; + modp->curr.code_length = 0; + modp->curr.catches = BEAM_CATCHES_NIL; + erts_remove_from_ranges(code); } erts_smp_thr_progress_unblock(); erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_release_code_write_permission(); BIF_RET(am_true); } @@ -403,11 +672,11 @@ set_default_trace_pattern(Eterm module) if (trace_pattern_is_on) { Eterm mfa[1]; mfa[0] = module; - (void) erts_set_trace_pattern(mfa, 1, + (void) erts_set_trace_pattern(0, mfa, 1, match_spec, meta_match_spec, 1, trace_pattern_flags, - meta_tracer_pid); + meta_tracer_pid, 1); } } @@ -419,20 +688,18 @@ check_process_code(Process* rp, Module* modp) Uint mod_size; BeamInstr* end; Eterm* sp; -#ifndef HYBRID /* FIND ME! */ struct erl_off_heap_header* oh; int done_gc = 0; -#endif #define INSIDE(a) (start <= (a) && (a) < end) /* * Pick up limits for the module. */ - start = modp->old_code; - end = (BeamInstr *)((char *)start + modp->old_code_length); + start = modp->old.code; + end = (BeamInstr *)((char *)start + modp->old.code_length); mod_start = (char *) start; - mod_size = modp->old_code_length; + mod_size = modp->old.code_length; /* * Check if current instruction or continuation pointer points into module. @@ -481,7 +748,6 @@ check_process_code(Process* rp, Module* modp) * See if there are funs that refer to the old version of the module. */ -#ifndef HYBRID /* FIND ME! */ rescan: for (oh = MSO(rp).first; oh; oh = oh->next) { if (thing_subtag(oh->thing_word) == FUN_SUBTAG) { @@ -507,7 +773,6 @@ check_process_code(Process* rp, Module* modp) } } } -#endif /* * See if there are constants inside the module referenced by the process. @@ -566,10 +831,10 @@ check_process_code(Process* rp, Module* modp) done_gc = 1; FLAGS(rp) |= F_NEED_FULLSWEEP; (void) erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); - literals = (Eterm *) modp->old_code[MI_LITERALS_START]; - lit_size = (Eterm *) modp->old_code[MI_LITERALS_END] - literals; + literals = (Eterm *) modp->old.code[MI_LITERALS_START]; + lit_size = (Eterm *) modp->old.code[MI_LITERALS_END] - literals; oh = (struct erl_off_heap_header *) - modp->old_code[MI_LITERALS_OFF_HEAP]; + modp->old.code[MI_LITERALS_OFF_HEAP]; erts_garbage_collect_literals(rp, literals, lit_size, oh); } } @@ -628,53 +893,82 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) #undef in_area - -static int -purge_module(int module) +BIF_RETTYPE purge_module_1(BIF_ALIST_1) { + ErtsCodeIndex code_ix; BeamInstr* code; BeamInstr* end; Module* modp; + int is_blocking = 0; + Eterm ret; - /* - * Correct module? - */ - - if ((modp = erts_get_module(make_atom(module))) == NULL) { - return -2; + if (is_not_atom(BIF_ARG_1)) { + BIF_ERROR(BIF_P, BADARG); } - /* - * Any code to purge? - */ - if (modp->old_code == 0) { - return -1; + if (!erts_try_seize_code_write_permission(BIF_P)) { + ERTS_BIF_YIELD1(bif_export[BIF_purge_module_1], BIF_P, BIF_ARG_1); } + code_ix = erts_active_code_ix(); + /* - * Unload any NIF library + * Correct module? */ - if (modp->old_nif != NULL) { - erts_unload_nif(modp->old_nif); - modp->old_nif = NULL; + + if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) { + ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); } + else { + erts_rwlock_old_code(code_ix); - /* - * Remove the old code. - */ - ASSERT(erts_total_code_size >= modp->old_code_length); - erts_total_code_size -= modp->old_code_length; - code = modp->old_code; - end = (BeamInstr *)((char *)code + modp->old_code_length); - erts_cleanup_funs_on_purge(code, end); - beam_catches_delmod(modp->old_catches, code, modp->old_code_length); - decrement_refc(code); - erts_free(ERTS_ALC_T_CODE, (void *) code); - modp->old_code = NULL; - modp->old_code_length = 0; - modp->old_catches = BEAM_CATCHES_NIL; - remove_from_address_table(code); - return 0; + /* + * Any code to purge? + */ + if (modp->old.code == 0) { + ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); + } + else { + /* + * Unload any NIF library + */ + if (modp->old.nif != NULL) { + /* ToDo: Do unload nif without blocking */ + erts_rwunlock_old_code(code_ix); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + is_blocking = 1; + erts_rwlock_old_code(code_ix); + erts_unload_nif(modp->old.nif); + modp->old.nif = NULL; + } + + /* + * Remove the old code. + */ + ASSERT(erts_total_code_size >= modp->old.code_length); + erts_total_code_size -= modp->old.code_length; + code = modp->old.code; + end = (BeamInstr *)((char *)code + modp->old.code_length); + erts_cleanup_funs_on_purge(code, end); + beam_catches_delmod(modp->old.catches, code, modp->old.code_length, + code_ix); + decrement_refc(code); + erts_free(ERTS_ALC_T_CODE, (void *) code); + modp->old.code = NULL; + modp->old.code_length = 0; + modp->old.catches = BEAM_CATCHES_NIL; + erts_remove_from_ranges(code); + ERTS_BIF_PREP_RET(ret, am_true); + } + erts_rwunlock_old_code(code_ix); + } + if (is_blocking) { + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + } + erts_release_code_write_permission(); + return ret; } static void @@ -694,92 +988,48 @@ decrement_refc(BeamInstr* code) } } -static void -remove_from_address_table(BeamInstr* code) -{ - int i; - - for (i = 0; i < num_loaded_modules; i++) { - if (modules[i].start == code) { - num_loaded_modules--; - while (i < num_loaded_modules) { - modules[i] = modules[i+1]; - i++; - } - mid_module = &modules[num_loaded_modules/2]; - return; - } - } - ASSERT(0); /* Not found? */ -} - - /* - * Move code from current to old. + * Move code from current to old and null all export entries for the module */ -static void -delete_code(Process *c_p, ErtsProcLocks c_p_locks, Module* modp) -{ -#ifdef ERTS_ENABLE_LOCK_CHECK -#ifdef ERTS_SMP - if (c_p && c_p_locks) - erts_proc_lc_chk_only_proc_main(c_p); - else -#endif - erts_lc_check_exact(NULL, 0); -#endif - - /* - * Clear breakpoints if any - */ - if (modp->code != NULL && modp->code[MI_NUM_BREAKPOINTS] > 0) { - if (c_p && c_p_locks) - erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - erts_clear_module_break(modp); - modp->code[MI_NUM_BREAKPOINTS] = 0; - erts_smp_thr_progress_unblock(); - if (c_p && c_p_locks) - erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); - } - modp->old_code = modp->code; - modp->old_code_length = modp->code_length; - modp->old_catches = modp->catches; - modp->old_nif = modp->nif; - modp->code = NULL; - modp->code_length = 0; - modp->catches = BEAM_CATCHES_NIL; - modp->nif = NULL; -} - - -/* null all references on the export table for the module called with the - atom index below */ - static void -delete_export_references(Eterm module) +delete_code(Module* modp) { + ErtsCodeIndex code_ix = erts_staging_code_ix(); + Eterm module = make_atom(modp->module); int i; - ASSERT(is_atom(module)); - - for (i = 0; i < export_list_size(); i++) { - Export *ep = export_list(i); + for (i = 0; i < export_list_size(code_ix); i++) { + Export *ep = export_list(i, code_ix); if (ep != NULL && (ep->code[0] == module)) { - if (ep->address == ep->code+3 && - (ep->code[3] == (BeamInstr) em_apply_bif)) { - continue; + if (ep->addressv[code_ix] == ep->code+3) { + if (ep->code[3] == (BeamInstr) em_apply_bif) { + continue; + } + else if (ep->code[3] == + (BeamInstr) BeamOp(op_i_generic_breakpoint)) { + ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); + ASSERT(modp->curr.num_traced_exports > 0); + erts_clear_export_break(modp, ep->code+3); + } + else ASSERT(ep->code[3] == (BeamInstr) em_call_error_handler + || !erts_initialized); } - ep->address = ep->code+3; + ep->addressv[code_ix] = ep->code+3; ep->code[3] = (BeamInstr) em_call_error_handler; ep->code[4] = 0; - MatchSetUnref(ep->match_prog_set); - ep->match_prog_set = NULL; } } + + ASSERT(modp->curr.num_breakpoints == 0); + ASSERT(modp->curr.num_traced_exports == 0); + modp->old = modp->curr; + modp->curr.code = NULL; + modp->curr.code_length = 0; + modp->curr.catches = BEAM_CATCHES_NIL; + modp->curr.nif = NULL; } - + Eterm beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks, Eterm module) @@ -791,11 +1041,10 @@ beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks, Eterm module) * if not, delete old code; error if old code already exists. */ - if (modp->code != NULL && modp->old_code != NULL) { + if (modp->curr.code != NULL && modp->old.code != NULL) { return am_not_purged; - } else if (modp->old_code == NULL) { /* Make the current version old. */ - delete_code(c_p, c_p_locks, modp); - delete_export_references(module); + } else if (modp->old.code == NULL) { /* Make the current version old. */ + delete_code(modp); } return NIL; } diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index a6e00f87e0..50d18b0347 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -44,67 +44,34 @@ #define ReAlloc(P, SIZ) erts_realloc(ERTS_ALC_T_BPD, (P), (SZ)) #define Free(P) erts_free(ERTS_ALC_T_BPD, (P)) -/* -** Doubly linked ring macros -*/ - -#define BpInit(a,i) \ -do { \ - (a)->orig_instr = (i); \ - (a)->next = (a); \ - (a)->prev = (a); \ -} while (0) - -#define BpSpliceNext(a,b) \ -do { \ - register BpData *c = (a), *d = (b), *e; \ - e = c->next->prev; \ - c->next->prev = d->next->prev; \ - d->next->prev = e; \ - e = c->next; \ - c->next = d->next; \ - d->next = e; \ -} while (0) - -#define BpSplicePrev(a,b) \ -do { \ - register BpData *c = (a), *d = (b), *e; \ - e = c->prev->next; \ - c->prev->next = d->prev->next; \ - d->prev->next = e; \ - e = c->prev; \ - c->prev = d->prev; \ - d->prev = e; \ -} while (0) - -#ifdef DEBUG -# define BpSingleton(a) ((a)->next == (a) && (a)->prev == (a)) +#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) +# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) \ + if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN) +# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \ + if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN) #else -# define BpSingleton(a) ((a)->next == (a)) +# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) +# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) #endif -#define BpInitAndSpliceNext(a,i,b) \ -do { \ - (a)->orig_instr = (i); \ - (a)->prev = (b); \ - (b)->next->prev = (a); \ - (a)->next = (b)->next; \ - (b)->next = (a); \ -} while (0) +#define ERTS_BPF_LOCAL_TRACE 0x01 +#define ERTS_BPF_META_TRACE 0x02 +#define ERTS_BPF_COUNT 0x04 +#define ERTS_BPF_COUNT_ACTIVE 0x08 +#define ERTS_BPF_DEBUG 0x10 +#define ERTS_BPF_TIME_TRACE 0x20 +#define ERTS_BPF_TIME_TRACE_ACTIVE 0x40 +#define ERTS_BPF_GLOBAL_TRACE 0x80 -#define BpInitAndSplicePrev(a,i,b) \ -do { \ - (a)->orig_instr = (i); \ - (a)->next = (b); \ - (b)->prev->next = (a); \ - (a)->prev = (b)->prev; \ - (b)->prev = (a); \ -} while (0) +#define ERTS_BPF_ALL 0xFF +extern Eterm beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */ +extern Eterm beam_return_trace[1]; /* OpCode(i_return_trace) */ +extern Eterm beam_exception_trace[1]; /* OpCode(i_exception_trace) */ +extern Eterm beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */ -#define BREAK_IS_BIF (1) -#define BREAK_IS_ERL (0) - +erts_smp_atomic32_t erts_active_bp_index; +erts_smp_atomic32_t erts_staging_bp_index; /* ************************************************************************* ** Local prototypes @@ -113,26 +80,30 @@ do { \ /* ** Helpers */ - -static int set_break(Eterm mfa[3], int specified, - Binary *match_spec, BeamInstr break_op, - enum erts_break_op count_op, Eterm tracer_pid); -static int set_module_break(Module *modp, Eterm mfa[3], int specified, - Binary *match_spec, BeamInstr break_op, - enum erts_break_op count_op, Eterm tracer_pid); -static int set_function_break(Module *modp, BeamInstr *pc, int bif, - Binary *match_spec, BeamInstr break_op, - enum erts_break_op count_op, Eterm tracer_pid); - -static int clear_break(Eterm mfa[3], int specified, - BeamInstr break_op); -static int clear_module_break(Module *modp, Eterm mfa[3], int specified, - BeamInstr break_op); -static int clear_function_break(Module *modp, BeamInstr *pc, int bif, - BeamInstr break_op); - -static BpData *is_break(BeamInstr *pc, BeamInstr break_op); -static BpData *get_break(Process *p, BeamInstr *pc, BeamInstr break_op); +static Eterm do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, + int local, Binary* ms, Eterm tracer_pid); +static void set_break(BpFunctions* f, Binary *match_spec, Uint break_flags, + enum erts_break_op count_op, Eterm tracer_pid); +static void set_function_break(BeamInstr *pc, + Binary *match_spec, + Uint break_flags, + enum erts_break_op count_op, + Eterm tracer_pid); + +static void clear_break(BpFunctions* f, Uint break_flags); +static int clear_function_break(BeamInstr *pc, Uint break_flags); + +static BpDataTime* get_time_break(BeamInstr *pc); +static GenericBpData* check_break(BeamInstr *pc, Uint break_flags); +static void bp_time_diff(bp_data_time_item_t *item, + process_breakpoint_time_t *pbt, + Uint ms, Uint s, Uint us); + +static void bp_meta_unref(BpMetaPid* bmp); +static void bp_count_unref(BpCount* bcp); +static void bp_time_unref(BpDataTime* bdt); +static void consolidate_bp_data(Module* modp, BeamInstr* pc, int local); +static void uninstall_breakpoint(BeamInstr* pc); /* bp_hash */ #define BP_TIME_ADD(pi0, pi1) \ @@ -152,240 +123,996 @@ static ERTS_INLINE bp_data_time_item_t * bp_hash_get(bp_time_hash_t *hash, bp_da static ERTS_INLINE bp_data_time_item_t * bp_hash_put(bp_time_hash_t *hash, bp_data_time_item_t *sitem); static void bp_hash_delete(bp_time_hash_t *hash); - /* ************************************************************************* ** External interfaces */ -erts_smp_spinlock_t erts_bp_lock; - void erts_bp_init(void) { - erts_smp_spinlock_init(&erts_bp_lock, "breakpoints"); + erts_smp_atomic32_init_nob(&erts_active_bp_index, 0); + erts_smp_atomic32_init_nob(&erts_staging_bp_index, 1); } -int -erts_set_trace_break(Eterm mfa[3], int specified, Binary *match_spec, - Eterm tracer_pid) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return set_break(mfa, specified, match_spec, - (BeamInstr) BeamOp(op_i_trace_breakpoint), 0, tracer_pid); + +void +erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified) +{ + ErtsCodeIndex code_ix = erts_active_code_ix(); + Uint max_funcs = 0; + int current; + int max_modules = module_code_size(code_ix); + int num_modules = 0; + Module* modp; + Module** module; + Uint i; + + module = (Module **) Alloc(max_modules*sizeof(Module *)); + num_modules = 0; + for (current = 0; current < max_modules; current++) { + modp = module_code(current, code_ix); + if (modp->curr.code) { + max_funcs += modp->curr.code[MI_NUM_FUNCTIONS]; + module[num_modules++] = modp; + } + } + + f->matching = (BpFunction *) Alloc(max_funcs*sizeof(BpFunction)); + i = 0; + for (current = 0; current < num_modules; current++) { + BeamInstr** code_base = (BeamInstr **) module[current]->curr.code; + BeamInstr* code; + Uint num_functions = (Uint) code_base[MI_NUM_FUNCTIONS]; + Uint fi; + + if (specified > 0) { + if (mfa[0] != make_atom(module[current]->module)) { + /* Wrong module name */ + continue; + } + } + + for (fi = 0; fi < num_functions; fi++) { + Eterm* pc; + int wi; + + code = code_base[MI_FUNCTIONS+fi]; + ASSERT(code[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); + pc = code+5; + if (erts_is_native_break(pc)) { + continue; + } + if (is_nil(code[3])) { /* Ignore BIF stub */ + continue; + } + for (wi = 0; + wi < specified && (Eterm) code[2+wi] == mfa[wi]; + wi++) { + /* Empty loop body */ + } + if (wi == specified) { + /* Store match */ + f->matching[i].pc = pc; + f->matching[i].mod = module[current]; + i++; + } + } + } + f->matched = i; + Free(module); } -int -erts_set_mtrace_break(Eterm mfa[3], int specified, Binary *match_spec, - Eterm tracer_pid) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return set_break(mfa, specified, match_spec, - (BeamInstr) BeamOp(op_i_mtrace_breakpoint), 0, tracer_pid); +void +erts_bp_match_export(BpFunctions* f, Eterm mfa[3], int specified) +{ + ErtsCodeIndex code_ix = erts_active_code_ix(); + int i; + int num_exps = export_list_size(code_ix); + int ne; + + f->matching = (BpFunction *) Alloc(num_exps*sizeof(BpFunction)); + ne = 0; + for (i = 0; i < num_exps; i++) { + Export* ep = export_list(i, code_ix); + BeamInstr* pc; + int j; + + for (j = 0; j < specified && mfa[j] == ep->code[j]; j++) { + /* Empty loop body */ + } + if (j < specified) { + continue; + } + pc = ep->code+3; + if (ep->addressv[code_ix] == pc) { + if ((*pc == (BeamInstr) em_apply_bif || + *pc == (BeamInstr) em_call_error_handler)) { + continue; + } + ASSERT(*pc == (BeamInstr) BeamOp(op_i_generic_breakpoint)); + } else if (erts_is_native_break(ep->addressv[code_ix])) { + continue; + } + + f->matching[ne].pc = pc; + f->matching[ne].mod = erts_get_module(ep->code[0], code_ix); + ne++; + + } + f->matched = ne; } -/* set breakpoint data for on exported bif entry */ +void +erts_bp_free_matched_functions(BpFunctions* f) +{ + Free(f->matching); +} void -erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, Eterm tracer_pid) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - set_function_break(NULL, pc, BREAK_IS_BIF, match_spec, (BeamInstr) BeamOp(op_i_mtrace_breakpoint), 0, tracer_pid); +erts_consolidate_bp_data(BpFunctions* f, int local) +{ + BpFunction* fs = f->matching; + Uint i; + Uint n = f->matched; + + ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked()); + + for (i = 0; i < n; i++) { + consolidate_bp_data(fs[i].mod, fs[i].pc, local); + } } -void erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op count_op) { - set_function_break(NULL, pc, BREAK_IS_BIF, NULL, (BeamInstr) BeamOp(op_i_time_breakpoint), count_op, NIL); +void +erts_consolidate_bif_bp_data(void) +{ + int i; + + ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked()); + for (i = 0; i < BIF_SIZE; i++) { + Export *ep = bif_export[i]; + consolidate_bp_data(0, ep->code+3, 0); + } } -void erts_clear_time_trace_bif(BeamInstr *pc) { - clear_function_break(NULL, pc, BREAK_IS_BIF, (BeamInstr) BeamOp(op_i_time_breakpoint)); +static void +consolidate_bp_data(Module* modp, BeamInstr* pc, int local) +{ + GenericBp* g = (GenericBp *) pc[-4]; + GenericBpData* src; + GenericBpData* dst; + Uint flags; + + if (g == 0) { + return; + } + + src = &g->data[erts_active_bp_ix()]; + dst = &g->data[erts_staging_bp_ix()]; + + /* + * The contents of the staging area may be out of date. + * Decrement all reference pointers. + */ + + flags = dst->flags; + if (flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) { + MatchSetUnref(dst->local_ms); + } + if (flags & ERTS_BPF_META_TRACE) { + bp_meta_unref(dst->meta_pid); + MatchSetUnref(dst->meta_ms); + } + if (flags & ERTS_BPF_COUNT) { + bp_count_unref(dst->count); + } + if (flags & ERTS_BPF_TIME_TRACE) { + bp_time_unref(dst->time); + } + + /* + * If all flags are zero, deallocate all breakpoint data. + */ + + flags = dst->flags = src->flags; + if (flags == 0) { + if (modp) { + if (local) { + modp->curr.num_breakpoints--; + } else { + modp->curr.num_traced_exports--; + } + ASSERT(modp->curr.num_breakpoints >= 0); + ASSERT(modp->curr.num_traced_exports >= 0); + ASSERT(*pc != (BeamInstr) BeamOp(op_i_generic_breakpoint)); + } + pc[-4] = 0; + Free(g); + return; + } + + /* + * Copy the active data to the staging area (making it ready + * for the next time it will be used). + */ + + if (flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) { + dst->local_ms = src->local_ms; + MatchSetRef(dst->local_ms); + } + if (flags & ERTS_BPF_META_TRACE) { + dst->meta_pid = src->meta_pid; + erts_refc_inc(&dst->meta_pid->refc, 1); + dst->meta_ms = src->meta_ms; + MatchSetRef(dst->meta_ms); + } + if (flags & ERTS_BPF_COUNT) { + dst->count = src->count; + erts_refc_inc(&dst->count->refc, 1); + } + if (flags & ERTS_BPF_TIME_TRACE) { + dst->time = src->time; + erts_refc_inc(&dst->time->refc, 1); + ASSERT(dst->time->hash); + } } -int -erts_set_debug_break(Eterm mfa[3], int specified) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return set_break(mfa, specified, NULL, - (BeamInstr) BeamOp(op_i_debug_breakpoint), 0, NIL); +void +erts_commit_staged_bp(void) +{ + ErtsBpIndex staging = erts_staging_bp_ix(); + ErtsBpIndex active = erts_active_bp_ix(); + + erts_smp_atomic32_set_nob(&erts_active_bp_index, staging); + erts_smp_atomic32_set_nob(&erts_staging_bp_index, active); } -int -erts_set_count_break(Eterm mfa[3], int specified, enum erts_break_op count_op) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return set_break(mfa, specified, NULL, - (BeamInstr) BeamOp(op_i_count_breakpoint), count_op, NIL); +void +erts_install_breakpoints(BpFunctions* f) +{ + Uint i; + Uint n = f->matched; + BeamInstr br = (BeamInstr) BeamOp(op_i_generic_breakpoint); + + for (i = 0; i < n; i++) { + BeamInstr* pc = f->matching[i].pc; + GenericBp* g = (GenericBp *) pc[-4]; + if (*pc != br && g) { + Module* modp = f->matching[i].mod; + + /* + * The breakpoint must be disabled in the active data + * (it will enabled later by switching bp indices), + * and enabled in the staging data. + */ + ASSERT(g->data[erts_active_bp_ix()].flags == 0); + ASSERT(g->data[erts_staging_bp_ix()].flags != 0); + + /* + * The following write is not protected by any lock. We + * assume that the hardware guarantees that a write of an + * aligned word-size (or half-word) writes is atomic + * (i.e. that other processes executing this code will not + * see a half pointer). + */ + *pc = br; + modp->curr.num_breakpoints++; + } + } } -int -erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op count_op) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return set_break(mfa, specified, NULL, - (BeamInstr) BeamOp(op_i_time_breakpoint), count_op, NIL); +void +erts_uninstall_breakpoints(BpFunctions* f) +{ + Uint i; + Uint n = f->matched; + + for (i = 0; i < n; i++) { + BeamInstr* pc = f->matching[i].pc; + uninstall_breakpoint(pc); + } } -int -erts_clear_trace_break(Eterm mfa[3], int specified) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return clear_break(mfa, specified, - (BeamInstr) BeamOp(op_i_trace_breakpoint)); +static void +uninstall_breakpoint(BeamInstr* pc) +{ + if (*pc == (BeamInstr) BeamOp(op_i_generic_breakpoint)) { + GenericBp* g = (GenericBp *) pc[-4]; + if (g->data[erts_active_bp_ix()].flags == 0) { + /* + * The following write is not protected by any lock. We + * assume that the hardware guarantees that a write of an + * aligned word-size (or half-word) writes is atomic + * (i.e. that other processes executing this code will not + * see a half pointer). + */ + *pc = g->orig_instr; + } + } } -int -erts_clear_mtrace_break(Eterm mfa[3], int specified) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return clear_break(mfa, specified, - (BeamInstr) BeamOp(op_i_mtrace_breakpoint)); +void +erts_set_trace_break(BpFunctions* f, Binary *match_spec) +{ + set_break(f, match_spec, ERTS_BPF_LOCAL_TRACE, 0, am_true); } void -erts_clear_mtrace_bif(BeamInstr *pc) { - clear_function_break(NULL, pc, BREAK_IS_BIF, (BeamInstr) BeamOp(op_i_mtrace_breakpoint)); +erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, Eterm tracer_pid) +{ + set_break(f, match_spec, ERTS_BPF_META_TRACE, 0, tracer_pid); } -int -erts_clear_debug_break(Eterm mfa[3], int specified) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return clear_break(mfa, specified, - (BeamInstr) BeamOp(op_i_debug_breakpoint)); +void +erts_set_call_trace_bif(BeamInstr *pc, Binary *match_spec, int local) +{ + Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE; + + set_function_break(pc, match_spec, flags, 0, NIL); } -int -erts_clear_count_break(Eterm mfa[3], int specified) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return clear_break(mfa, specified, - (BeamInstr) BeamOp(op_i_count_breakpoint)); +void +erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, Eterm tracer_pid) +{ + set_function_break(pc, match_spec, ERTS_BPF_META_TRACE, 0, tracer_pid); } -int -erts_clear_time_break(Eterm mfa[3], int specified) { - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return clear_break(mfa, specified, - (BeamInstr) BeamOp(op_i_time_breakpoint)); +void +erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op count_op) +{ + set_function_break(pc, NULL, + ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE, + count_op, NIL); } -int -erts_clear_break(Eterm mfa[3], int specified) { +void +erts_clear_time_trace_bif(BeamInstr *pc) { + clear_function_break(pc, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE); +} + +void +erts_set_debug_break(BpFunctions* f) { + set_break(f, NULL, ERTS_BPF_DEBUG, 0, NIL); +} + +void +erts_set_count_break(BpFunctions* f, enum erts_break_op count_op) +{ + set_break(f, 0, ERTS_BPF_COUNT|ERTS_BPF_COUNT_ACTIVE, + count_op, NIL); +} + +void +erts_set_time_break(BpFunctions* f, enum erts_break_op count_op) +{ + set_break(f, 0, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE, + count_op, NIL); +} + +void +erts_clear_trace_break(BpFunctions* f) +{ + clear_break(f, ERTS_BPF_LOCAL_TRACE); +} + +void +erts_clear_call_trace_bif(BeamInstr *pc, int local) +{ + GenericBp* g = (GenericBp *) pc[-4]; + + if (g) { + Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE; + if (g->data[erts_staging_bp_ix()].flags & flags) { + clear_function_break(pc, flags); + } + } +} + +void +erts_clear_mtrace_break(BpFunctions* f) +{ + clear_break(f, ERTS_BPF_META_TRACE); +} + +void +erts_clear_mtrace_bif(BeamInstr *pc) +{ + clear_function_break(pc, ERTS_BPF_META_TRACE); +} + +void +erts_clear_debug_break(BpFunctions* f) +{ ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - return clear_break(mfa, specified, 0); + clear_break(f, ERTS_BPF_DEBUG); +} + +void +erts_clear_count_break(BpFunctions* f) +{ + clear_break(f, ERTS_BPF_COUNT|ERTS_BPF_COUNT_ACTIVE); +} + +void +erts_clear_time_break(BpFunctions* f) +{ + clear_break(f, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE); +} + +void +erts_clear_all_breaks(BpFunctions* f) +{ + clear_break(f, ERTS_BPF_ALL); } int erts_clear_module_break(Module *modp) { + BeamInstr** code_base; + Uint n; + Uint i; + ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); ASSERT(modp); - return clear_module_break(modp, NULL, 0, 0); + code_base = (BeamInstr **) modp->curr.code; + if (code_base == NULL) { + return 0; + } + n = (Uint) code_base[MI_NUM_FUNCTIONS]; + for (i = 0; i < n; ++i) { + BeamInstr* pc; + + pc = code_base[MI_FUNCTIONS+i] + 5; + if (erts_is_native_break(pc)) { + continue; + } + clear_function_break(pc, ERTS_BPF_ALL); + } + + erts_commit_staged_bp(); + + for (i = 0; i < n; ++i) { + BeamInstr* pc; + + pc = code_base[MI_FUNCTIONS+i] + 5; + if (erts_is_native_break(pc)) { + continue; + } + uninstall_breakpoint(pc); + consolidate_bp_data(modp, pc, 1); + ASSERT(pc[-4] == 0); + } + return n; } -int -erts_clear_function_break(Module *modp, BeamInstr *pc) { +void +erts_clear_export_break(Module* modp, BeamInstr* pc) +{ ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - ASSERT(modp); - return clear_function_break(modp, pc, BREAK_IS_ERL, 0); + + clear_function_break(pc, ERTS_BPF_ALL); + erts_commit_staged_bp(); + *pc = (BeamInstr) 0; + consolidate_bp_data(modp, pc, 0); + ASSERT(pc[-4] == 0); } +BeamInstr +erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg) +{ + GenericBp* g; + GenericBpData* bp; + Uint bp_flags; + ErtsBpIndex ix = erts_active_bp_ix(); + + g = (GenericBp *) I[-4]; + bp = &g->data[ix]; + bp_flags = bp->flags; + ASSERT((bp_flags & ~ERTS_BPF_ALL) == 0); + if (bp_flags & (ERTS_BPF_LOCAL_TRACE| + ERTS_BPF_GLOBAL_TRACE| + ERTS_BPF_TIME_TRACE_ACTIVE) && + !IS_TRACED_FL(c_p, F_TRACE_CALLS)) { + bp_flags &= ~(ERTS_BPF_LOCAL_TRACE| + ERTS_BPF_GLOBAL_TRACE| + ERTS_BPF_TIME_TRACE| + ERTS_BPF_TIME_TRACE_ACTIVE); + if (bp_flags == 0) { /* Quick exit */ + return g->orig_instr; + } + } + + if (bp_flags & ERTS_BPF_LOCAL_TRACE) { + ASSERT((bp_flags & ERTS_BPF_GLOBAL_TRACE) == 0); + (void) do_call_trace(c_p, I, reg, 1, bp->local_ms, am_true); + } else if (bp_flags & ERTS_BPF_GLOBAL_TRACE) { + (void) do_call_trace(c_p, I, reg, 0, bp->local_ms, am_true); + } + + if (bp_flags & ERTS_BPF_META_TRACE) { + Eterm old_pid; + Eterm new_pid; + + old_pid = (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid); + new_pid = do_call_trace(c_p, I, reg, 1, bp->meta_ms, old_pid); + if (new_pid != old_pid) { + erts_smp_atomic_set_nob(&bp->meta_pid->pid, new_pid); + } + } + + if (bp_flags & ERTS_BPF_COUNT_ACTIVE) { + erts_smp_atomic_inc_nob(&bp->count->acount); + } + + if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) { + Eterm w; + erts_trace_time_call(c_p, I, bp->time); + w = (BeamInstr) *c_p->cp; + if (! (w == (BeamInstr) BeamOp(op_i_return_time_trace) || + w == (BeamInstr) BeamOp(op_return_trace) || + w == (BeamInstr) BeamOp(op_i_return_to_trace)) ) { + Eterm* E = c_p->stop; + ASSERT(c_p->htop <= E && E <= c_p->hend); + if (E - 2 < c_p->htop) { + (void) erts_garbage_collect(c_p, 2, reg, I[-1]); + ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); + } + E = c_p->stop; + ASSERT(c_p->htop <= E && E <= c_p->hend); + + E -= 2; + E[0] = make_cp(I); + E[1] = make_cp(c_p->cp); /* original return address */ + c_p->cp = beam_return_time_trace; + c_p->stop = E; + } + } + + if (bp_flags & ERTS_BPF_DEBUG) { + return (BeamInstr) BeamOp(op_i_debug_breakpoint); + } else { + return g->orig_instr; + } +} /* - * SMP NOTE: Process p may have become exiting on return! + * Entry point called by the trace wrap functions in erl_bif_wrap.c + * + * The trace wrap functions are themselves called through the export + * entries instead of the original BIF functions. */ -BeamInstr -erts_trace_break(Process *p, BeamInstr *pc, Eterm *args, - Uint32 *ret_flags, Eterm *tracer_pid) { - Eterm tpid1, tpid2; - BpData **bds = (BpData **) (pc)[-4]; - BpDataTrace *bdt = NULL; +Eterm +erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) +{ + Eterm result; + Eterm (*func)(Process*, Eterm*, BeamInstr*); + Export* ep = bif_export[bif_index]; + Uint32 flags = 0, flags_meta = 0; + Eterm meta_tracer_pid = NIL; + int applying = (I == &(ep->code[3])); /* Yup, the apply code for a bif + * is actually in the + * export entry */ + BeamInstr *cp = p->cp; + GenericBp* g; + GenericBpData* bp; + Uint bp_flags = 0; + + ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); + + g = (GenericBp *) ep->fake_op_func_info_for_hipe[1]; + if (g) { + bp = &g->data[erts_active_bp_ix()]; + bp_flags = bp->flags; + } - ASSERT(bds); - ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); - bdt = (BpDataTrace *) bds[bp_sched2ix_proc(p)]; - ASSERT(bdt); - bdt = (BpDataTrace *) bdt->next; - ASSERT(bdt); - ASSERT(ret_flags); - ASSERT(tracer_pid); - - ErtsSmpBPLock(bdt); - tpid1 = tpid2 = bdt->tracer_pid; - ErtsSmpBPUnlock(bdt); - - *ret_flags = erts_call_trace(p, pc-3/*mfa*/, bdt->match_spec, args, - 1, &tpid2); - *tracer_pid = tpid2; - if (tpid1 != tpid2) { - ErtsSmpBPLock(bdt); - bdt->tracer_pid = tpid2; - ErtsSmpBPUnlock(bdt); - } - bds[bp_sched2ix_proc(p)] = (BpData *) bdt; - return bdt->orig_instr; + /* + * Make continuation pointer OK, it is not during direct BIF calls, + * but it is correct during apply of bif. + */ + if (!applying) { + p->cp = I; + } + if (bp_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE) && + IS_TRACED_FL(p, F_TRACE_CALLS)) { + int local = !!(bp_flags & ERTS_BPF_LOCAL_TRACE); + flags = erts_call_trace(p, ep->code, bp->local_ms, args, + local, &p->tracer_proc); + } + if (bp_flags & ERTS_BPF_META_TRACE) { + Eterm tpid1, tpid2; + + tpid1 = tpid2 = + (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid); + flags_meta = erts_call_trace(p, ep->code, bp->meta_ms, args, + 0, &tpid2); + meta_tracer_pid = tpid2; + if (tpid1 != tpid2) { + erts_smp_atomic_set_nob(&bp->meta_pid->pid, tpid2); + } + } + if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE && + IS_TRACED_FL(p, F_TRACE_CALLS)) { + BeamInstr *pc = (BeamInstr *)ep->code+3; + erts_trace_time_call(p, pc, bp->time); + } + + /* Restore original continuation pointer (if changed). */ + p->cp = cp; + + func = bif_table[bif_index].f; + + result = func(p, args, I); + + if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) { + BeamInstr i_return_trace = beam_return_trace[0]; + BeamInstr i_return_to_trace = beam_return_to_trace[0]; + BeamInstr i_return_time_trace = beam_return_time_trace[0]; + Eterm *cpp; + /* Maybe advance cp to skip trace stack frames */ + for (cpp = p->stop; ; cp = cp_val(*cpp++)) { + if (*cp == i_return_trace) { + /* Skip stack frame variables */ + while (is_not_CP(*cpp)) cpp++; + cpp += 2; /* Skip return_trace parameters */ + } else if (*cp == i_return_time_trace) { + /* Skip stack frame variables */ + while (is_not_CP(*cpp)) cpp++; + cpp += 1; /* Skip return_time_trace parameters */ + } else if (*cp == i_return_to_trace) { + /* A return_to trace message is going to be generated + * by normal means, so we do not have to. + */ + cp = NULL; + break; + } else break; + } + } + + /* Try to get these in the order + * they usually appear in normal code... */ + if (is_non_value(result)) { + Uint reason = p->freason; + if (reason != TRAP) { + Eterm class; + Eterm value = p->fvalue; + DeclareTmpHeapNoproc(nocatch,3); + UseTmpHeapNoproc(3); + /* Expand error value like in handle_error() */ + if (reason & EXF_ARGLIST) { + Eterm *tp; + ASSERT(is_tuple(value)); + tp = tuple_val(value); + value = tp[1]; + } + if ((reason & EXF_THROWN) && (p->catches <= 0)) { + value = TUPLE2(nocatch, am_nocatch, value); + reason = EXC_ERROR; + } + /* Note: expand_error_value() could theoretically + * allocate on the heap, but not for any error + * returned by a BIF, and it would do no harm, + * just be annoying. + */ + value = expand_error_value(p, reason, value); + class = exception_tag[GET_EXC_CLASS(reason)]; + + if (flags_meta & MATCH_SET_EXCEPTION_TRACE) { + erts_trace_exception(p, ep->code, class, value, + &meta_tracer_pid); + } + if (flags & MATCH_SET_EXCEPTION_TRACE) { + erts_trace_exception(p, ep->code, class, value, + &p->tracer_proc); + } + if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) { + /* can only happen if(local)*/ + Eterm *ptr = p->stop; + ASSERT(is_CP(*ptr)); + ASSERT(ptr <= STACK_START(p)); + /* Search the nearest stack frame for a catch */ + while (++ptr < STACK_START(p)) { + if (is_CP(*ptr)) break; + if (is_catch(*ptr)) { + if (applying) { + /* Apply of BIF, cp is in calling function */ + if (cp) erts_trace_return_to(p, cp); + } else { + /* Direct bif call, I points into + * calling function */ + erts_trace_return_to(p, I); + } + } + } + } + UnUseTmpHeapNoproc(3); + if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) { + erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); + p->trace_flags |= F_EXCEPTION_TRACE; + erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR); + } + } + } else { + if (flags_meta & MATCH_SET_RX_TRACE) { + erts_trace_return(p, ep->code, result, &meta_tracer_pid); + } + /* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */ + if (flags & MATCH_SET_RX_TRACE) { + erts_trace_return(p, ep->code, result, &p->tracer_proc); + } + if (flags & MATCH_SET_RETURN_TO_TRACE) { + /* can only happen if(local)*/ + if (applying) { + /* Apply of BIF, cp is in calling function */ + if (cp) erts_trace_return_to(p, cp); + } else { + /* Direct bif call, I points into calling function */ + erts_trace_return_to(p, I); + } + } + } + ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); + return result; +} + +static Eterm +do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg, + int local, Binary* ms, Eterm tracer_pid) +{ + Eterm* cpp; + int return_to_trace = 0; + BeamInstr w; + BeamInstr *cp_save; + Uint32 flags; + Uint need = 0; + Eterm* E = c_p->stop; + + w = *c_p->cp; + if (w == (BeamInstr) BeamOp(op_return_trace)) { + cpp = &E[2]; + } else if (w == (BeamInstr) BeamOp(op_i_return_to_trace)) { + return_to_trace = 1; + cpp = &E[0]; + } else if (w == (BeamInstr) BeamOp(op_i_return_time_trace)) { + cpp = &E[0]; + } else { + cpp = NULL; + } + if (cpp) { + for (;;) { + BeamInstr w = *cp_val(*cpp); + if (w == (BeamInstr) BeamOp(op_return_trace)) { + cpp += 3; + } else if (w == (BeamInstr) BeamOp(op_i_return_to_trace)) { + return_to_trace = 1; + cpp += 1; + } else if (w == (BeamInstr) BeamOp(op_i_return_time_trace)) { + cpp += 2; + } else { + break; + } + } + cp_save = c_p->cp; + c_p->cp = (BeamInstr *) cp_val(*cpp); + ASSERT(is_CP(*cpp)); + } + ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + flags = erts_call_trace(c_p, I-3, ms, reg, local, &tracer_pid); + ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); + if (cpp) { + c_p->cp = cp_save; + } + + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) { + need += 1; + } + if (flags & MATCH_SET_RX_TRACE) { + need += 3; + } + if (need) { + ASSERT(c_p->htop <= E && E <= c_p->hend); + if (E - need < c_p->htop) { + (void) erts_garbage_collect(c_p, need, reg, I[-1]); + ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); + E = c_p->stop; + } + } + if (flags & MATCH_SET_RETURN_TO_TRACE && !return_to_trace) { + E -= 1; + ASSERT(c_p->htop <= E && E <= c_p->hend); + E[0] = make_cp(c_p->cp); + c_p->cp = (BeamInstr *) beam_return_to_trace; + } + if (flags & MATCH_SET_RX_TRACE) { + E -= 3; + ASSERT(c_p->htop <= E && E <= c_p->hend); + ASSERT(is_CP((Eterm) (UWord) (I - 3))); + ASSERT(am_true == tracer_pid || + is_internal_pid(tracer_pid) || is_internal_port(tracer_pid)); + E[2] = make_cp(c_p->cp); + E[1] = tracer_pid; + E[0] = make_cp(I - 3); /* We ARE at the beginning of an + instruction, + the funcinfo is above i. */ + c_p->cp = (flags & MATCH_SET_EXCEPTION_TRACE) ? + beam_exception_trace : beam_return_trace; + erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); + c_p->trace_flags |= F_EXCEPTION_TRACE; + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); + } + c_p->stop = E; + return tracer_pid; } +void +erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt) +{ + Uint ms,s,us; + process_breakpoint_time_t *pbt = NULL; + bp_data_time_item_t sitem, *item = NULL; + bp_time_hash_t *h = NULL; + BpDataTime *pbdt = NULL; + ASSERT(c_p); + ASSERT(erts_smp_atomic32_read_acqb(&c_p->state) & ERTS_PSFLG_RUNNING); -/* - * SMP NOTE: Process p may have become exiting on return! - */ -Uint32 -erts_bif_mtrace(Process *p, BeamInstr *pc, Eterm *args, int local, - Eterm *tracer_pid) { - BpData **bds = (BpData **) (pc)[-4]; - BpDataTrace *bdt = NULL; + /* get previous timestamp and breakpoint + * from the process psd */ + pbt = ERTS_PROC_GET_CALL_TIME(c_p); + get_sys_now(&ms, &s, &us); - ASSERT(tracer_pid); - if (bds) { - Eterm tpid1, tpid2; - Uint32 flags; - bdt = (BpDataTrace *)bds[bp_sched2ix_proc(p)]; + /* get pbt + * timestamp = t0 + * lookup bdt from code + * set ts0 to pbt + * add call count here? + */ + if (pbt == 0) { + /* First call of process to instrumented function */ + pbt = Alloc(sizeof(process_breakpoint_time_t)); + (void *) ERTS_PROC_SET_CALL_TIME(c_p, ERTS_PROC_LOCK_MAIN, pbt); + } else { + ASSERT(pbt->pc); + /* add time to previous code */ + bp_time_diff(&sitem, pbt, ms, s, us); + sitem.pid = c_p->id; + sitem.count = 0; - ErtsSmpBPLock(bdt); - tpid1 = tpid2 = bdt->tracer_pid; - ErtsSmpBPUnlock(bdt); + /* previous breakpoint */ + pbdt = get_time_break(pbt->pc); - flags = erts_call_trace(p, pc-3/*mfa*/, bdt->match_spec, args, - local, &tpid2); - *tracer_pid = tpid2; - if (tpid1 != tpid2) { - ErtsSmpBPLock(bdt); - bdt->tracer_pid = tpid2; - ErtsSmpBPUnlock(bdt); + /* if null then the breakpoint was removed */ + if (pbdt) { + h = &(pbdt->hash[bp_sched2ix_proc(c_p)]); + + ASSERT(h); + ASSERT(h->item); + + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } } - return flags; } - *tracer_pid = NIL; - return 0; + + /* Add count to this code */ + sitem.pid = c_p->id; + sitem.count = 1; + sitem.s_time = 0; + sitem.us_time = 0; + + /* this breakpoint */ + ASSERT(bdt); + h = &(bdt->hash[bp_sched2ix_proc(c_p)]); + + ASSERT(h); + ASSERT(h->item); + + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } + + pbt->pc = I; + pbt->ms = ms; + pbt->s = s; + pbt->us = us; } +void +erts_trace_time_return(Process *p, BeamInstr *pc) +{ + Uint ms,s,us; + process_breakpoint_time_t *pbt = NULL; + bp_data_time_item_t sitem, *item = NULL; + bp_time_hash_t *h = NULL; + BpDataTime *pbdt = NULL; + + ASSERT(p); + ASSERT(erts_smp_atomic32_read_acqb(&p->state) & ERTS_PSFLG_RUNNING); + /* get previous timestamp and breakpoint + * from the process psd */ + + pbt = ERTS_PROC_GET_CALL_TIME(p); + get_sys_now(&ms,&s,&us); + + /* get pbt + * lookup bdt from code + * timestamp = t1 + * get ts0 from pbt + * get item from bdt->hash[bp_hash(p->id)] + * ack diff (t1, t0) to item + */ + + if (pbt) { + /* might have been removed due to + * trace_pattern(false) + */ + ASSERT(pbt->pc); + + bp_time_diff(&sitem, pbt, ms, s, us); + sitem.pid = p->id; + sitem.count = 0; + + /* previous breakpoint */ + pbdt = get_time_break(pbt->pc); + + /* beware, the trace_pattern might have been removed */ + if (pbdt) { + h = &(pbdt->hash[bp_sched2ix_proc(p)]); + + ASSERT(h); + ASSERT(h->item); + + item = bp_hash_get(h, &sitem); + if (!item) { + item = bp_hash_put(h, &sitem); + } else { + BP_TIME_ADD(item, &sitem); + } + } + + pbt->pc = pc; + pbt->ms = ms; + pbt->s = s; + pbt->us = us; + } +} int -erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret) { - BpDataTrace *bdt = - (BpDataTrace *) is_break(pc, (BeamInstr) BeamOp(op_i_trace_breakpoint)); - - if (bdt) { +erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, int local) +{ + Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE; + GenericBpData* bp = check_break(pc, flags); + + if (bp) { if (match_spec_ret) { - *match_spec_ret = bdt->match_spec; + *match_spec_ret = bp->local_ms; } - if (tracer_pid_ret) { - ErtsSmpBPLock(bdt); - *tracer_pid_ret = bdt->tracer_pid; - ErtsSmpBPUnlock(bdt); - } - return !0; + return 1; } return 0; } int -erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret) { - BpDataTrace *bdt = - (BpDataTrace *) is_break(pc, (BeamInstr) BeamOp(op_i_mtrace_breakpoint)); +erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret, + Eterm *tracer_pid_ret) +{ + GenericBpData* bp = check_break(pc, ERTS_BPF_META_TRACE); - if (bdt) { + if (bp) { if (match_spec_ret) { - *match_spec_ret = bdt->match_spec; + *match_spec_ret = bp->meta_ms; } if (tracer_pid_ret) { - ErtsSmpBPLock(bdt); - *tracer_pid_ret = bdt->tracer_pid; - ErtsSmpBPUnlock(bdt); + *tracer_pid_ret = + (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid); } - return !0; + return 1; } return 0; } @@ -402,15 +1129,15 @@ erts_is_native_break(BeamInstr *pc) { } int -erts_is_count_break(BeamInstr *pc, Sint *count_ret) { - BpDataCount *bdc = - (BpDataCount *) is_break(pc, (BeamInstr) BeamOp(op_i_count_breakpoint)); +erts_is_count_break(BeamInstr *pc, Uint *count_ret) +{ + GenericBpData* bp = check_break(pc, ERTS_BPF_COUNT); - if (bdc) { + if (bp) { if (count_ret) { - *count_ret = (Sint) erts_smp_atomic_read_nob(&bdc->acount); + *count_ret = (Uint) erts_smp_atomic_read_nob(&bp->count->acount); } - return !0; + return 1; } return 0; } @@ -421,7 +1148,7 @@ int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) { Uint size; Eterm *hp, t; bp_data_time_item_t *item = NULL; - BpDataTime *bdt = (BpDataTime *) is_break(pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); + BpDataTime *bdt = get_time_break(pc); if (bdt) { if (retval) { @@ -464,7 +1191,7 @@ int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) { } bp_hash_delete(&hash); } - return !0; + return 1; } return 0; @@ -478,15 +1205,16 @@ erts_find_local_func(Eterm mfa[3]) { BeamInstr* code_ptr; Uint i,n; - if ((modp = erts_get_module(mfa[0])) == NULL) + if ((modp = erts_get_module(mfa[0], erts_active_code_ix())) == NULL) return NULL; - if ((code_base = (BeamInstr **) modp->code) == NULL) + if ((code_base = (BeamInstr **) modp->curr.code) == NULL) return NULL; n = (BeamInstr) code_base[MI_NUM_FUNCTIONS]; for (i = 0; i < n; ++i) { code_ptr = code_base[MI_FUNCTIONS+i]; ASSERT(((BeamInstr) BeamOp(op_i_func_info_IaaI)) == code_ptr[0]); - ASSERT(mfa[0] == ((Eterm) code_ptr[2])); + ASSERT(mfa[0] == ((Eterm) code_ptr[2]) || + is_nil((Eterm) code_ptr[2])); if (mfa[1] == ((Eterm) code_ptr[3]) && ((BeamInstr) mfa[2]) == code_ptr[4]) { return code_ptr + 5; @@ -654,7 +1382,7 @@ void erts_schedule_time_break(Process *p, Uint schedule) { * the previous breakpoint. */ - pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); + pbdt = get_time_break(pbt->pc); if (pbdt) { get_sys_now(&ms,&s,&us); bp_time_diff(&sitem, pbt, ms, s, us); @@ -692,669 +1420,259 @@ void erts_schedule_time_break(Process *p, Uint schedule) { } /* pbt */ } -/* call_time breakpoint - * Accumulated times are added to the previous bp, - * not the current one. The current one is saved - * for future reference. - * The previous breakpoint is stored in the process it self, the psd. - * We do not need to store in a stack frame. - * There is no need for locking, each thread has its own - * area in each bp to save data. - * Since we need to diffrentiate between processes for each bp, - * every bp has a hash (per thread) to process-bp statistics. - * - egil - */ - -void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type) { - Uint ms,s,us; - process_breakpoint_time_t *pbt = NULL; - bp_data_time_item_t sitem, *item = NULL; - bp_time_hash_t *h = NULL; - BpDataTime *pbdt = NULL; - - ASSERT(p); - ASSERT(ERTS_PSFLG_RUNNING & erts_smp_atomic32_read_acqb(&p->state)); - - /* get previous timestamp and breakpoint - * from the process psd */ - - pbt = ERTS_PROC_GET_CALL_TIME(p); - get_sys_now(&ms,&s,&us); - - switch(type) { - /* get pbt - * timestamp = t0 - * lookup bdt from code - * set ts0 to pbt - * add call count here? - */ - case ERTS_BP_CALL_TIME_CALL: - case ERTS_BP_CALL_TIME_TAIL_CALL: - - if (pbt) { - ASSERT(pbt->pc); - /* add time to previous code */ - bp_time_diff(&sitem, pbt, ms, s, us); - sitem.pid = p->id; - sitem.count = 0; - - /* previous breakpoint */ - pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); - - /* if null then the breakpoint was removed */ - if (pbdt) { - h = &(pbdt->hash[bp_sched2ix_proc(p)]); - - ASSERT(h); - ASSERT(h->item); - - item = bp_hash_get(h, &sitem); - if (!item) { - item = bp_hash_put(h, &sitem); - } else { - BP_TIME_ADD(item, &sitem); - } - } - - } else { - /* first call of process to instrumented function */ - pbt = Alloc(sizeof(process_breakpoint_time_t)); - (void *) ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCK_MAIN, pbt); - } - /* add count to this code */ - sitem.pid = p->id; - sitem.count = 1; - sitem.s_time = 0; - sitem.us_time = 0; - - /* this breakpoint */ - ASSERT(bdt); - h = &(bdt->hash[bp_sched2ix_proc(p)]); - - ASSERT(h); - ASSERT(h->item); - - item = bp_hash_get(h, &sitem); - if (!item) { - item = bp_hash_put(h, &sitem); - } else { - BP_TIME_ADD(item, &sitem); - } - - pbt->pc = pc; - pbt->ms = ms; - pbt->s = s; - pbt->us = us; - break; - - case ERTS_BP_CALL_TIME_RETURN: - /* get pbt - * lookup bdt from code - * timestamp = t1 - * get ts0 from pbt - * get item from bdt->hash[bp_hash(p->id)] - * ack diff (t1, t0) to item - */ - - if(pbt) { - /* might have been removed due to - * trace_pattern(false) - */ - ASSERT(pbt->pc); - - bp_time_diff(&sitem, pbt, ms, s, us); - sitem.pid = p->id; - sitem.count = 0; - - /* previous breakpoint */ - pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); - - /* beware, the trace_pattern might have been removed */ - if (pbdt) { - h = &(pbdt->hash[bp_sched2ix_proc(p)]); - - ASSERT(h); - ASSERT(h->item); - - item = bp_hash_get(h, &sitem); - if (!item) { - item = bp_hash_put(h, &sitem); - } else { - BP_TIME_ADD(item, &sitem); - } - } - - pbt->pc = pc; - pbt->ms = ms; - pbt->s = s; - pbt->us = us; - } - break; - default : - ASSERT(0); - /* will never happen */ - break; - } -} - - /* ************************************************************************* ** Local helpers */ -static int set_break(Eterm mfa[3], int specified, - Binary *match_spec, BeamInstr break_op, - enum erts_break_op count_op, Eterm tracer_pid) +static void +set_break(BpFunctions* f, Binary *match_spec, Uint break_flags, + enum erts_break_op count_op, Eterm tracer_pid) { - Module *modp; - int num_processed = 0; - if (!specified) { - /* Find and process all modules in the system... */ - int current; - int last = module_code_size(); - for (current = 0; current < last; current++) { - modp = module_code(current); - ASSERT(modp != NULL); - num_processed += - set_module_break(modp, mfa, specified, - match_spec, break_op, count_op, - tracer_pid); - } - } else { - /* Process a single module */ - if ((modp = erts_get_module(mfa[0])) != NULL) { - num_processed += - set_module_break(modp, mfa, specified, - match_spec, break_op, count_op, - tracer_pid); - } - } - return num_processed; -} - -static int set_module_break(Module *modp, Eterm mfa[3], int specified, - Binary *match_spec, BeamInstr break_op, - enum erts_break_op count_op, Eterm tracer_pid) { - BeamInstr** code_base; - BeamInstr* code_ptr; - int num_processed = 0; - Uint i,n; + Uint i; + Uint n; - ASSERT(break_op); - ASSERT(modp); - code_base = (BeamInstr **) modp->code; - if (code_base == NULL) { - return 0; + n = f->matched; + for (i = 0; i < n; i++) { + BeamInstr* pc = f->matching[i].pc; + set_function_break(pc, match_spec, break_flags, + count_op, tracer_pid); } - n = (BeamInstr) code_base[MI_NUM_FUNCTIONS]; - for (i = 0; i < n; ++i) { - code_ptr = code_base[MI_FUNCTIONS+i]; - ASSERT(code_ptr[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); - if ((specified < 2 || mfa[1] == ((Eterm) code_ptr[3])) && - (specified < 3 || ((int) mfa[2]) == ((int) code_ptr[4]))) { - BeamInstr *pc = code_ptr+5; - - num_processed += - set_function_break(modp, pc, BREAK_IS_ERL, match_spec, - break_op, count_op, tracer_pid); - } - } - return num_processed; } -static int set_function_break(Module *modp, BeamInstr *pc, int bif, - Binary *match_spec, BeamInstr break_op, - enum erts_break_op count_op, Eterm tracer_pid) { - - BeamInstr **code_base = NULL; - BpData *bd, **r, ***rs; - size_t size; - Uint ix = 0; - - if (bif == BREAK_IS_ERL) { - code_base = (BeamInstr **)modp->code; - ASSERT(code_base); - ASSERT(code_base <= (BeamInstr **)pc); - ASSERT((BeamInstr **)pc < code_base + (modp->code_length/sizeof(BeamInstr *))); - } else { - ASSERT(*pc == (BeamInstr) em_apply_bif); - ASSERT(modp == NULL); +static void +set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags, + enum erts_break_op count_op, Eterm tracer_pid) +{ + GenericBp* g; + GenericBpData* bp; + Uint common; + ErtsBpIndex ix = erts_staging_bp_ix(); + + ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked()); + g = (GenericBp *) pc[-4]; + if (g == 0) { + int i; + if (count_op == erts_break_reset || count_op == erts_break_stop) { + /* Do not insert a new breakpoint */ + return; + } + g = Alloc(sizeof(GenericBp)); + g->orig_instr = *pc; + for (i = 0; i < ERTS_NUM_BP_IX; i++) { + g->data[i].flags = 0; + } + pc[-4] = (BeamInstr) g; } + bp = &g->data[ix]; /* - * Currently no trace support for native code. + * If we are changing an existing breakpoint, clean up old data. */ - if (erts_is_native_break(pc)) { - return 0; - } - /* Do not allow two breakpoints of the same kind */ - if ( (bd = is_break(pc, break_op))) { - if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint) - || break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) { - - BpDataTrace *bdt = (BpDataTrace *) bd; - Binary *old_match_spec; - - /* Update match spec and tracer */ - MatchSetRef(match_spec); - ErtsSmpBPLock(bdt); - old_match_spec = bdt->match_spec; - bdt->match_spec = match_spec; - bdt->tracer_pid = tracer_pid; - ErtsSmpBPUnlock(bdt); - MatchSetUnref(old_match_spec); - } else { - BpDataCount *bdc = (BpDataCount *) bd; - erts_aint_t count = 0; - erts_aint_t res = 0; - - ASSERT(! match_spec); - ASSERT(is_nil(tracer_pid)); - - if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) { - if (count_op == erts_break_stop) { - count = erts_smp_atomic_read_nob(&bdc->acount); - if (count >= 0) { - while(1) { - res = erts_smp_atomic_cmpxchg_nob(&bdc->acount, -count - 1, count); - if ((res == count) || count < 0) break; - count = res; - } - } - } else { - /* Reset call counter */ - erts_smp_atomic_set_nob(&bdc->acount, 0); - } - - } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) { - BpDataTime *bdt = (BpDataTime *) bd; - Uint i = 0; - - ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); - - if (count_op == erts_break_stop) { - bdt->pause = 1; - } else { - bdt->pause = 0; - for (i = 0; i < bdt->n; i++) { - bp_hash_delete(&(bdt->hash[i])); - bp_hash_init(&(bdt->hash[i]), 32); - } - } - } else { - ASSERT (! count_op); - } - } - return 1; - } - if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint) || - break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) { - size = sizeof(BpDataTrace); - } else { - ASSERT(! match_spec); - ASSERT(is_nil(tracer_pid)); - if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) { - if (count_op == erts_break_reset || count_op == erts_break_stop) { - /* Do not insert a new breakpoint */ - return 1; - } - size = sizeof(BpDataCount); - } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) { - if (count_op == erts_break_reset || count_op == erts_break_stop) { - /* Do not insert a new breakpoint */ - return 1; - } - size = sizeof(BpDataTime); + common = break_flags & bp->flags; + if (common & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) { + MatchSetUnref(bp->local_ms); + } else if (common & ERTS_BPF_META_TRACE) { + MatchSetUnref(bp->meta_ms); + bp_meta_unref(bp->meta_pid); + } else if (common & ERTS_BPF_COUNT) { + if (count_op == erts_break_stop) { + bp->flags &= ~ERTS_BPF_COUNT_ACTIVE; } else { - ASSERT(! count_op); - ASSERT(break_op == (BeamInstr) BeamOp(op_i_debug_breakpoint)); - size = sizeof(BpDataDebug); + bp->flags |= ERTS_BPF_COUNT_ACTIVE; + erts_smp_atomic_set_nob(&bp->count->acount, 0); } - } - rs = (BpData ***) (pc-4); - if (! *rs) { - size_t ssize = sizeof(BeamInstr) * erts_no_schedulers; - *rs = (BpData **) Alloc(ssize); - sys_memzero(*rs, ssize); - } - - r = &((*rs)[0]); - - if (! *r) { - ASSERT(*pc != (BeamInstr) BeamOp(op_i_trace_breakpoint)); - ASSERT(*pc != (BeamInstr) BeamOp(op_i_mtrace_breakpoint)); - ASSERT(*pc != (BeamInstr) BeamOp(op_i_debug_breakpoint)); - ASSERT(*pc != (BeamInstr) BeamOp(op_i_count_breakpoint)); - ASSERT(*pc != (BeamInstr) BeamOp(op_i_time_breakpoint)); - /* First breakpoint; create singleton ring */ - bd = Alloc(size); - BpInit(bd, *pc); - *r = bd; - if (bif == BREAK_IS_ERL) { - *pc = break_op; - } - } else { - ASSERT(*pc == (BeamInstr) BeamOp(op_i_trace_breakpoint) || - *pc == (BeamInstr) BeamOp(op_i_mtrace_breakpoint) || - *pc == (BeamInstr) BeamOp(op_i_debug_breakpoint) || - *pc == (BeamInstr) BeamOp(op_i_time_breakpoint) || - *pc == (BeamInstr) BeamOp(op_i_count_breakpoint) || - *pc == (BeamInstr) em_apply_bif); - if (*pc == (BeamInstr) BeamOp(op_i_debug_breakpoint)) { - /* Debug bp must be last, so if it is also first; - * it must be singleton. */ - ASSERT(BpSingleton(*r)); - /* Insert new bp first in the ring, i.e second to last. */ - bd = Alloc(size); - BpInitAndSpliceNext(bd, *pc, *r); - if (bif == BREAK_IS_ERL) { - *pc = break_op; - } - } else if ((*r)->prev->orig_instr - == (BeamInstr) BeamOp(op_i_debug_breakpoint)) { - /* Debug bp last in the ring; insert new second to last. */ - bd = Alloc(size); - BpInitAndSplicePrev(bd, (*r)->prev->orig_instr, *r); - (*r)->prev->orig_instr = break_op; + ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0); + return; + } else if (common & ERTS_BPF_TIME_TRACE) { + BpDataTime* bdt = bp->time; + Uint i = 0; + + if (count_op == erts_break_stop) { + bp->flags &= ~ERTS_BPF_TIME_TRACE_ACTIVE; } else { - /* Just insert last in the ring */ - bd = Alloc(size); - BpInitAndSpliceNext(bd, (*r)->orig_instr, *r); - (*r)->orig_instr = break_op; - *r = bd; + bp->flags |= ERTS_BPF_TIME_TRACE_ACTIVE; + for (i = 0; i < bdt->n; i++) { + bp_hash_delete(&(bdt->hash[i])); + bp_hash_init(&(bdt->hash[i]), 32); + } } - } - for (ix = 1; ix < erts_no_schedulers; ++ix) { - (*rs)[ix] = (*rs)[0]; + ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0); + return; } - bd->this_instr = break_op; - /* Init the bp type specific data */ - if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint) || - break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) { - - BpDataTrace *bdt = (BpDataTrace *) bd; - - MatchSetRef(match_spec); - bdt->match_spec = match_spec; - bdt->tracer_pid = tracer_pid; - } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) { - BpDataTime *bdt = (BpDataTime *) bd; - Uint i = 0; - - bdt->pause = 0; - bdt->n = erts_no_schedulers; - bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n)); + /* + * Initialize the new breakpoint data. + */ + if (break_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) { + MatchSetRef(match_spec); + bp->local_ms = match_spec; + } else if (break_flags & ERTS_BPF_META_TRACE) { + BpMetaPid* bmp; + MatchSetRef(match_spec); + bp->meta_ms = match_spec; + bmp = Alloc(sizeof(BpMetaPid)); + erts_refc_init(&bmp->refc, 1); + erts_smp_atomic_init_nob(&bmp->pid, tracer_pid); + bp->meta_pid = bmp; + } else if (break_flags & ERTS_BPF_COUNT) { + BpCount* bcp; + + ASSERT((bp->flags & ERTS_BPF_COUNT) == 0); + bcp = Alloc(sizeof(BpCount)); + erts_refc_init(&bcp->refc, 1); + erts_smp_atomic_init_nob(&bcp->acount, 0); + bp->count = bcp; + } else if (break_flags & ERTS_BPF_TIME_TRACE) { + BpDataTime* bdt; + int i; + + ASSERT((bp->flags & ERTS_BPF_TIME_TRACE) == 0); + bdt = Alloc(sizeof(BpDataTime)); + erts_refc_init(&bdt->refc, 1); + bdt->n = erts_no_schedulers; + bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n)); for (i = 0; i < bdt->n; i++) { bp_hash_init(&(bdt->hash[i]), 32); } - } else if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) { - BpDataCount *bdc = (BpDataCount *) bd; - erts_smp_atomic_init_nob(&bdc->acount, 0); + bp->time = bdt; } - if (bif == BREAK_IS_ERL) { - ++(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]); + bp->flags |= break_flags; + ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0); +} + +static void +clear_break(BpFunctions* f, Uint break_flags) +{ + Uint i; + Uint n; + + n = f->matched; + for (i = 0; i < n; i++) { + BeamInstr* pc = f->matching[i].pc; + clear_function_break(pc, break_flags); } - return 1; } -static int clear_break(Eterm mfa[3], int specified, BeamInstr break_op) +static int +clear_function_break(BeamInstr *pc, Uint break_flags) { - int num_processed = 0; - Module *modp; + GenericBp* g; + GenericBpData* bp; + Uint common; + ErtsBpIndex ix = erts_staging_bp_ix(); - if (!specified) { - /* Iterate over all modules */ - int current; - int last = module_code_size(); + ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked()); - for (current = 0; current < last; current++) { - modp = module_code(current); - ASSERT(modp != NULL); - num_processed += clear_module_break(modp, mfa, specified, break_op); - } - } else { - /* Process a single module */ - if ((modp = erts_get_module(mfa[0])) != NULL) { - num_processed += - clear_module_break(modp, mfa, specified, break_op); - } + if ((g = (GenericBp *) pc[-4]) == 0) { + return 1; } - return num_processed; -} -static int clear_module_break(Module *m, Eterm mfa[3], int specified, - BeamInstr break_op) { - BeamInstr** code_base; - BeamInstr* code_ptr; - int num_processed = 0; - Uint i; - BeamInstr n; - - ASSERT(m); - code_base = (BeamInstr **) m->code; - if (code_base == NULL) { - return 0; + bp = &g->data[ix]; + ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0); + common = bp->flags & break_flags; + bp->flags &= ~break_flags; + if (common & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) { + MatchSetUnref(bp->local_ms); } - n = (BeamInstr) code_base[MI_NUM_FUNCTIONS]; - for (i = 0; i < n; ++i) { - code_ptr = code_base[MI_FUNCTIONS+i]; - if ((specified < 2 || mfa[1] == ((Eterm) code_ptr[3])) && - (specified < 3 || ((int) mfa[2]) == ((int) code_ptr[4]))) { - BeamInstr *pc = code_ptr + 5; - - num_processed += - clear_function_break(m, pc, BREAK_IS_ERL, break_op); - } + if (common & ERTS_BPF_META_TRACE) { + MatchSetUnref(bp->meta_ms); + } + if (common & ERTS_BPF_COUNT) { + ASSERT((bp->flags & ERTS_BPF_COUNT_ACTIVE) == 0); + bp_count_unref(bp->count); + } + if (common & ERTS_BPF_TIME_TRACE) { + ASSERT((bp->flags & ERTS_BPF_TIME_TRACE_ACTIVE) == 0); + bp_time_unref(bp->time); } - return num_processed; -} -static int clear_function_break(Module *m, BeamInstr *pc, int bif, BeamInstr break_op) { - BpData *bd; - Uint ix = 0; - BeamInstr **code_base = NULL; + ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0); + return 1; +} - if (bif == BREAK_IS_ERL) { - code_base = (BeamInstr **)m->code; - ASSERT(code_base); - ASSERT(code_base <= (BeamInstr **)pc); - ASSERT((BeamInstr **)pc < code_base + (m->code_length/sizeof(BeamInstr *))); - } else { - ASSERT(*pc == (BeamInstr) em_apply_bif); - ASSERT(m == NULL); +static void +bp_meta_unref(BpMetaPid* bmp) +{ + if (erts_refc_dectest(&bmp->refc, 0) <= 0) { + Free(bmp); } +} - /* - * Currently no trace support for native code. - */ - if (erts_is_native_break(pc)) { - return 0; +static void +bp_count_unref(BpCount* bcp) +{ + if (erts_refc_dectest(&bcp->refc, 0) <= 0) { + Free(bcp); } +} - while ( (bd = is_break(pc, break_op))) { - /* Remove all breakpoints of this type. - * There should be only one of each type, - * but break_op may be 0 which matches any type. +static void +bp_time_unref(BpDataTime* bdt) +{ + if (erts_refc_dectest(&bdt->refc, 0) <= 0) { + Uint i = 0; + Uint j = 0; + Process *h_p = NULL; + bp_data_time_item_t* item = NULL; + process_breakpoint_time_t* pbt = NULL; + + /* remove all psd associated with the hash + * and then delete the hash. + * ... sigh ... */ - BeamInstr op; - BpData ***rs = (BpData ***) (pc - 4); - BpData **r = NULL; - -#ifdef DEBUG - for (ix = 1; ix < erts_no_schedulers; ++ix) { - ASSERT((*rs)[ix] == (*rs)[0]); - } -#endif - - r = &((*rs)[0]); - - ASSERT(*r); - /* Find opcode for this breakpoint */ - if (break_op) { - op = break_op; - } else { - if (bd == (*r)->next) { - /* First breakpoint in ring */ - op = *pc; - } else { - op = bd->prev->orig_instr; - } - } - if (BpSingleton(bd)) { - ASSERT(*r == bd); - /* Only one breakpoint to remove */ - if (bif == BREAK_IS_ERL) { - *pc = bd->orig_instr; - } - Free(*rs); - *rs = NULL; - } else { - BpData *bd_prev = bd->prev; - - BpSpliceNext(bd, bd_prev); - ASSERT(BpSingleton(bd)); - if (bd == *r) { - /* We removed the last breakpoint in the ring */ - *r = bd_prev; - bd_prev->orig_instr = bd->orig_instr; - } else if (bd_prev == *r) { - /* We removed the first breakpoint in the ring */ - if (bif == BREAK_IS_ERL) { - *pc = bd->orig_instr; - } - } else { - bd_prev->orig_instr = bd->orig_instr; - } - } - if (op == (BeamInstr) BeamOp(op_i_trace_breakpoint) || - op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) { - - BpDataTrace *bdt = (BpDataTrace *) bd; - MatchSetUnref(bdt->match_spec); - } - if (op == (BeamInstr) BeamOp(op_i_time_breakpoint)) { - BpDataTime *bdt = (BpDataTime *) bd; - Uint i = 0; - Uint j = 0; - Process *h_p = NULL; - bp_data_time_item_t *item = NULL; - process_breakpoint_time_t *pbt = NULL; - - /* remove all psd associated with the hash - * and then delete the hash. - * ... sigh ... - */ - for( i = 0; i < bdt->n; ++i) { - if (bdt->hash[i].used) { - for (j = 0; j < bdt->hash[i].n; ++j) { - item = &(bdt->hash[i].item[j]); - if (item->pid != NIL) { - h_p = erts_proc_lookup(item->pid); - if (h_p) { - pbt = ERTS_PROC_SET_CALL_TIME(h_p, ERTS_PROC_LOCK_MAIN, NULL); - if (pbt) { - Free(pbt); - } + for (i = 0; i < bdt->n; ++i) { + if (bdt->hash[i].used) { + for (j = 0; j < bdt->hash[i].n; ++j) { + item = &(bdt->hash[i].item[j]); + if (item->pid != NIL) { + h_p = erts_pid2proc(NULL, 0, item->pid, + ERTS_PROC_LOCK_MAIN); + if (h_p) { + pbt = ERTS_PROC_SET_CALL_TIME(h_p, + ERTS_PROC_LOCK_MAIN, + NULL); + if (pbt) { + Free(pbt); } + erts_smp_proc_unlock(h_p, ERTS_PROC_LOCK_MAIN); } } } - bp_hash_delete(&(bdt->hash[i])); } - Free(bdt->hash); - bdt->hash = NULL; - bdt->n = 0; + bp_hash_delete(&(bdt->hash[i])); } - Free(bd); - if (bif == BREAK_IS_ERL) { - ASSERT(((BeamInstr) code_base[MI_NUM_BREAKPOINTS]) > 0); - --(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]); - } - if (*rs) { - for (ix = 1; ix < erts_no_schedulers; ++ix) { - (*rs)[ix] = (*rs)[0]; - } - } - } /* while bd != NULL */ - return 1; + Free(bdt->hash); + Free(bdt); + } } - - -/* -** Searches (linear forward) the breakpoint ring for a specified opcode -** and returns a pointer to the breakpoint data structure or NULL if -** not found. If the specified opcode is 0, the last breakpoint is -** returned. The program counter must point to the first executable -** (breakpoint) instruction of the function. -*/ - -BpData *erts_get_time_break(Process *p, BeamInstr *pc) { - return get_break(p, pc, (BeamInstr) BeamOp(op_i_time_breakpoint)); +static BpDataTime* +get_time_break(BeamInstr *pc) +{ + GenericBpData* bp = check_break(pc, ERTS_BPF_TIME_TRACE); + return bp ? bp->time : 0; } -static BpData *get_break(Process *p, BeamInstr *pc, BeamInstr break_op) { - ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); - if (! erts_is_native_break(pc)) { - BpData **rs = (BpData **) pc[-4]; - BpData *bd = NULL, *ebd = NULL; - - if (! rs) { - return NULL; - } - - bd = ebd = rs[bp_sched2ix_proc(p)]; - ASSERT(bd); - if (bd->this_instr == break_op) { - return bd; - } - - bd = bd->next; - while (bd != ebd) { - ASSERT(bd); - if (bd->this_instr == break_op) { - ASSERT(bd); - return bd; - } - bd = bd->next; - } - } - return NULL; -} +static GenericBpData* +check_break(BeamInstr *pc, Uint break_flags) +{ + GenericBp* g = (GenericBp *) pc[-4]; -static BpData *is_break(BeamInstr *pc, BeamInstr break_op) { - BpData **rs; - BpData *bd = NULL, *ebd = NULL; ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); - if (erts_is_native_break(pc)) { - return NULL; - } - rs = (BpData **) pc[-4]; - if (! rs) { - return NULL; - } - - bd = ebd = rs[erts_bp_sched2ix()]; - ASSERT(bd); - if ( (break_op == 0) || (bd->this_instr == break_op)) { - return bd; + return 0; } - - bd = bd->next; - while (bd != ebd) { - ASSERT(bd); - if (bd->this_instr == break_op) { - ASSERT(bd); - return bd; + if (g) { + GenericBpData* bp = &g->data[erts_active_bp_ix()]; + ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0); + if (bp->flags & break_flags) { + return bp; } - bd = bd->next; } - return NULL; + return 0; } diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index 167069552f..28aaaa462a 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -25,77 +25,6 @@ #include "erl_vm.h" #include "global.h" - - -/* A couple of gotchas: - * - * The breakpoint structure from BeamInstr, - * In beam_emu where the instruction counter pointer, I (or pc), - * points to the *current* instruction. At that time, if the instruction - * is a breakpoint instruction the pc looks like the following, - * - * I[-5] | op_i_func_info_IaaI | scheduler specific entries - * I[-4] | BpData** bpa | --> | BpData * bdas1 | ... | BpData * bdasN | - * I[-3] | Tagged Module | | | - * I[-2] | Tagged Function | V V - * I[-1] | Arity | BpData -> BpData -> BpData -> BpData - * I[0] | The bp instruction | ^ * the bp wheel * | - * |------------------------------ - * - * Common struct to all bp_data_* - * - * 1) The type of bp_data structure in the ring is deduced from the - * orig_instr field of the structure _before_ in the ring, except for - * the first structure in the ring that has its instruction in - * pc[0] of the code to execute. - * This is valid as long as you don't search for the function while it is - * being executed by something else. Or is in the middle of its rotation for - * any other reason. - * A key, the bp beam instruction, is included for this reason. - * - * 2) pc[-4][sched_id - 1] points to the _last_ structure in the ring before the - * breakpoints are being executed. - * - * So, as an example, when a breakpointed function starts to execute, - * the first instruction that is a breakpoint instruction at pc[0] finds - * its data at ((BpData **) pc[-4][sched_id - 1])->next and has to cast that pointer - * to the correct bp_data type. -*/ - -typedef struct bp_data { - struct bp_data *next; /* Doubly linked ring pointers */ - struct bp_data *prev; /* -"- */ - BeamInstr orig_instr; /* The original instruction to execute */ - BeamInstr this_instr; /* key */ -} BpData; -/* -** All the following bp_data_.. structs must begin the same way -*/ - -typedef struct bp_data_trace { - struct bp_data *next; - struct bp_data *prev; - BeamInstr orig_instr; - BeamInstr this_instr; /* key */ - Binary *match_spec; - Eterm tracer_pid; -} BpDataTrace; - -typedef struct bp_data_debug { - struct bp_data *next; - struct bp_data *prev; - BeamInstr orig_instr; - BeamInstr this_instr; /* key */ -} BpDataDebug; - -typedef struct bp_data_count { /* Call count */ - struct bp_data *next; - struct bp_data *prev; - BeamInstr orig_instr; - BeamInstr this_instr; /* key */ - erts_smp_atomic_t acount; -} BpDataCount; - typedef struct { Eterm pid; Sint count; @@ -110,13 +39,9 @@ typedef struct { } bp_time_hash_t; typedef struct bp_data_time { /* Call time */ - struct bp_data *next; - struct bp_data *prev; - BeamInstr orig_instr; - BeamInstr this_instr; /* key */ - Uint pause; - Uint n; - bp_time_hash_t *hash; + Uint n; + bp_time_hash_t *hash; + erts_refc_t refc; } BpDataTime; typedef struct { @@ -126,64 +51,42 @@ typedef struct { BeamInstr *pc; } process_breakpoint_time_t; /* used within psd */ -extern erts_smp_spinlock_t erts_bp_lock; +typedef struct { + erts_smp_atomic_t acount; + erts_refc_t refc; +} BpCount; + +typedef struct { + erts_smp_atomic_t pid; + erts_refc_t refc; +} BpMetaPid; + +typedef struct generic_bp_data { + Uint flags; + Binary* local_ms; /* Match spec for local call trace */ + Binary* meta_ms; /* Match spec for meta trace */ + BpMetaPid* meta_pid; /* Meta trace pid */ + BpCount* count; /* For call count */ + BpDataTime* time; /* For time trace */ +} GenericBpData; + +#define ERTS_NUM_BP_IX 2 + +typedef struct generic_bp { + BeamInstr orig_instr; + GenericBpData data[ERTS_NUM_BP_IX]; +} GenericBp; #define ERTS_BP_CALL_TIME_SCHEDULE_IN (0) #define ERTS_BP_CALL_TIME_SCHEDULE_OUT (1) #define ERTS_BP_CALL_TIME_SCHEDULE_EXITING (2) -#define ERTS_BP_CALL_TIME_CALL (0) -#define ERTS_BP_CALL_TIME_RETURN (1) -#define ERTS_BP_CALL_TIME_TAIL_CALL (2) - -#ifdef ERTS_SMP -#define ErtsSmpBPLock(BDC) erts_smp_spin_lock(&erts_bp_lock) -#define ErtsSmpBPUnlock(BDC) erts_smp_spin_unlock(&erts_bp_lock) -#else -#define ErtsSmpBPLock(BDC) -#define ErtsSmpBPUnlock(BDC) -#endif - #ifdef ERTS_SMP #define bp_sched2ix_proc(p) ((p)->scheduler_data->no - 1) #else #define bp_sched2ix_proc(p) (0) #endif -#define ErtsCountBreak(p, pc,instr_result) \ -do { \ - BpData **bds = (BpData **) (pc)[-4]; \ - BpDataCount *bdc = NULL; \ - Uint ix = bp_sched2ix_proc( (p) ); \ - erts_aint_t count = 0; \ - \ - ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \ - ASSERT(bds); \ - bdc = (BpDataCount *) bds[ix]; \ - bdc = (BpDataCount *) bdc->next; \ - ASSERT(bdc); \ - bds[ix] = (BpData *) bdc; \ - count = erts_smp_atomic_read_nob(&bdc->acount); \ - if (count >= 0) erts_smp_atomic_inc_nob(&bdc->acount); \ - *(instr_result) = bdc->orig_instr; \ -} while (0) - -#define ErtsBreakSkip(p, pc,instr_result) \ -do { \ - BpData **bds = (BpData **) (pc)[-4]; \ - BpData *bd = NULL; \ - Uint ix = bp_sched2ix_proc( (p) ); \ - \ - ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \ - ASSERT(bds); \ - bd = bds[ix]; \ - ASSERT(bd); \ - bd = bd->next; \ - ASSERT(bd); \ - bds[ix] = bd; \ - *(instr_result) = bd->orig_instr; \ -} while (0) - enum erts_break_op{ erts_break_nop = 0, /* Must be false */ erts_break_set = !0, /* Must be true */ @@ -191,7 +94,17 @@ enum erts_break_op{ erts_break_stop }; +typedef Uint32 ErtsBpIndex; +typedef struct { + BeamInstr* pc; + Module* mod; +} BpFunction; + +typedef struct { + Uint matched; /* Number matched */ + BpFunction* matching; /* Matching functions */ +} BpFunctions; /* ** Function interface exported from beam_bp.c @@ -199,49 +112,66 @@ enum erts_break_op{ void erts_bp_init(void); -int erts_set_trace_break(Eterm mfa[3], int specified, Binary *match_spec, - Eterm tracer_pid); -int erts_clear_trace_break(Eterm mfa[3], int specified); -int erts_set_mtrace_break(Eterm mfa[3], int specified, Binary *match_spec, +void erts_prepare_bp_staging(void); +void erts_commit_staged_bp(void); + +ERTS_GLB_INLINE ErtsBpIndex erts_active_bp_ix(void); +ERTS_GLB_INLINE ErtsBpIndex erts_staging_bp_ix(void); + +void erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified); +void erts_bp_match_export(BpFunctions* f, Eterm mfa[3], int specified); +void erts_bp_free_matched_functions(BpFunctions* f); + +void erts_install_breakpoints(BpFunctions* f); +void erts_uninstall_breakpoints(BpFunctions* f); +void erts_consolidate_bp_data(BpFunctions* f, int local); +void erts_consolidate_bif_bp_data(void); + +void erts_set_trace_break(BpFunctions *f, Binary *match_spec); +void erts_clear_trace_break(BpFunctions *f); + +void erts_set_call_trace_bif(BeamInstr *pc, Binary *match_spec, int local); +void erts_clear_call_trace_bif(BeamInstr *pc, int local); + +void erts_set_mtrace_break(BpFunctions *f, Binary *match_spec, Eterm tracer_pid); -int erts_clear_mtrace_break(Eterm mfa[3], int specified); +void erts_clear_mtrace_break(BpFunctions *f); void erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, Eterm tracer_pid); void erts_clear_mtrace_bif(BeamInstr *pc); -int erts_set_debug_break(Eterm mfa[3], int specified); -int erts_clear_debug_break(Eterm mfa[3], int specified); -int erts_set_count_break(Eterm mfa[3], int specified, enum erts_break_op); -int erts_clear_count_break(Eterm mfa[3], int specified); +void erts_set_debug_break(BpFunctions *f); +void erts_clear_debug_break(BpFunctions *f); +void erts_set_count_break(BpFunctions *f, enum erts_break_op); +void erts_clear_count_break(BpFunctions *f); -int erts_clear_break(Eterm mfa[3], int specified); + +void erts_clear_all_breaks(BpFunctions* f); int erts_clear_module_break(Module *modp); -int erts_clear_function_break(Module *modp, BeamInstr *pc); +void erts_clear_export_break(Module *modp, BeamInstr* pc); +BeamInstr erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg); BeamInstr erts_trace_break(Process *p, BeamInstr *pc, Eterm *args, Uint32 *ret_flags, Eterm *tracer_pid); -Uint32 erts_bif_mtrace(Process *p, BeamInstr *pc, Eterm *args, - int local, Eterm *tracer_pid); -int erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, - Eterm *tracer_pid_ret); +int erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, int local); int erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_rte); int erts_is_mtrace_bif(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret); int erts_is_native_break(BeamInstr *pc); -int erts_is_count_break(BeamInstr *pc, Sint *count_ret); +int erts_is_count_break(BeamInstr *pc, Uint *count_ret); int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *call_time); -void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type); +void erts_trace_time_call(Process* c_p, BeamInstr* pc, BpDataTime* bdt); +void erts_trace_time_return(Process* c_p, BeamInstr* pc); void erts_schedule_time_break(Process *p, Uint out); -int erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op); -int erts_clear_time_break(Eterm mfa[3], int specified); +void erts_set_time_break(BpFunctions *f, enum erts_break_op); +void erts_clear_time_break(BpFunctions *f); int erts_is_time_trace_bif(Process *p, BeamInstr *pc, Eterm *call_time); void erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op); void erts_clear_time_trace_bif(BeamInstr *pc); -BpData *erts_get_time_break(Process *p, BeamInstr *pc); BeamInstr *erts_find_local_func(Eterm mfa[3]); @@ -258,6 +188,19 @@ ERTS_GLB_INLINE Uint erts_bp_sched2ix(void) return 0; #endif } + +extern erts_smp_atomic32_t erts_active_bp_index; +extern erts_smp_atomic32_t erts_staging_bp_index; + +ERTS_GLB_INLINE ErtsBpIndex erts_active_bp_ix(void) +{ + return erts_smp_atomic32_read_nob(&erts_active_bp_index); +} + +ERTS_GLB_INLINE ErtsBpIndex erts_staging_bp_ix(void) +{ + return erts_smp_atomic32_read_nob(&erts_staging_bp_index); +} #endif #endif /* _BEAM_BP_H */ diff --git a/erts/emulator/beam/beam_catches.c b/erts/emulator/beam/beam_catches.c index 406ef1db5f..92f7ffe5a2 100644 --- a/erts/emulator/beam/beam_catches.c +++ b/erts/emulator/beam/beam_catches.c @@ -31,78 +31,144 @@ typedef struct { unsigned cdr; } beam_catch_t; -static int free_list; -static unsigned high_mark; -static unsigned tabsize; -static beam_catch_t *beam_catches; +#ifdef DEBUG +# define IF_DEBUG(x) x +#else +# define IF_DEBUG(x) +#endif + +struct bc_pool { + int free_list; + unsigned high_mark; + unsigned tabsize; + beam_catch_t *beam_catches; + /* + * Note that the 'beam_catches' area is shared by pools. Used slots + * are readonly as long as the module is not purgable. The free-list is + * protected by the code_ix lock. + */ + + IF_DEBUG(int is_staging;) +}; + +static struct bc_pool bccix[ERTS_NUM_CODE_IX]; void beam_catches_init(void) { - tabsize = DEFAULT_TABSIZE; - free_list = -1; - high_mark = 0; + int i; + + bccix[0].tabsize = DEFAULT_TABSIZE; + bccix[0].free_list = -1; + bccix[0].high_mark = 0; + bccix[0].beam_catches = erts_alloc(ERTS_ALC_T_CODE, + sizeof(beam_catch_t)*DEFAULT_TABSIZE); + IF_DEBUG(bccix[0].is_staging = 0); + for (i=1; i<ERTS_NUM_CODE_IX; i++) { + bccix[i] = bccix[i-1]; + } + /* For initial load: */ + IF_DEBUG(bccix[erts_staging_code_ix()].is_staging = 1); +} - beam_catches = erts_alloc(ERTS_ALC_T_CODE, sizeof(beam_catch_t)*DEFAULT_TABSIZE); + +static void gc_old_vec(beam_catch_t* vec) +{ + int i; + for (i=0; i<ERTS_NUM_CODE_IX; i++) { + if (bccix[i].beam_catches == vec) { + return; + } + } + erts_free(ERTS_ALC_T_CODE, vec); +} + + +void beam_catches_start_staging(void) +{ + ErtsCodeIndex dst = erts_staging_code_ix(); + ErtsCodeIndex src = erts_active_code_ix(); + beam_catch_t* prev_vec = bccix[dst].beam_catches; + + ASSERT(!bccix[src].is_staging && !bccix[dst].is_staging); + + bccix[dst] = bccix[src]; + gc_old_vec(prev_vec); + IF_DEBUG(bccix[dst].is_staging = 1); +} + +void beam_catches_end_staging(int commit) +{ + IF_DEBUG(bccix[erts_staging_code_ix()].is_staging = 0); } unsigned beam_catches_cons(BeamInstr *cp, unsigned cdr) { int i; + struct bc_pool* p = &bccix[erts_staging_code_ix()]; + ASSERT(p->is_staging); /* * Allocate from free_list while it is non-empty. * If free_list is empty, allocate at high_mark. - * - * This avoids the need to initialise the free list in - * beam_catches_init(), which would cost O(TABSIZ) time. */ - if( free_list >= 0 ) { - i = free_list; - free_list = beam_catches[i].cdr; - } else if( high_mark < tabsize ) { - i = high_mark; - high_mark++; - } else { - /* No free slots and table is full: realloc table */ - tabsize = 2*tabsize; - beam_catches = erts_realloc(ERTS_ALC_T_CODE, beam_catches, sizeof(beam_catch_t)*tabsize); - i = high_mark; - high_mark++; + if (p->free_list >= 0) { + i = p->free_list; + p->free_list = p->beam_catches[i].cdr; + } + else { + if (p->high_mark >= p->tabsize) { + /* No free slots and table is full: realloc table */ + beam_catch_t* prev_vec = p->beam_catches; + unsigned newsize = p->tabsize*2; + + p->beam_catches = erts_alloc(ERTS_ALC_T_CODE, + newsize*sizeof(beam_catch_t)); + sys_memcpy(p->beam_catches, prev_vec, + p->tabsize*sizeof(beam_catch_t)); + gc_old_vec(prev_vec); + p->tabsize = newsize; + } + i = p->high_mark++; } - beam_catches[i].cp = cp; - beam_catches[i].cdr = cdr; + p->beam_catches[i].cp = cp; + p->beam_catches[i].cdr = cdr; return i; } BeamInstr *beam_catches_car(unsigned i) { - if( i >= tabsize ) { + struct bc_pool* p = &bccix[erts_active_code_ix()]; + + if (i >= p->tabsize ) { erl_exit(1, "beam_catches_delmod: index %#x is out of range\r\n", i); } - return beam_catches[i].cp; + return p->beam_catches[i].cp; } -void beam_catches_delmod(unsigned head, BeamInstr *code, unsigned code_bytes) +void beam_catches_delmod(unsigned head, BeamInstr *code, unsigned code_bytes, + ErtsCodeIndex code_ix) { + struct bc_pool* p = &bccix[code_ix]; unsigned i, cdr; + ASSERT((code_ix == erts_active_code_ix()) != bccix[erts_staging_code_ix()].is_staging); for(i = head; i != (unsigned)-1;) { - if( i >= tabsize ) { + if (i >= p->tabsize) { erl_exit(1, "beam_catches_delmod: index %#x is out of range\r\n", i); } - if( (char*)beam_catches[i].cp - (char*)code >= code_bytes ) { + if( (char*)p->beam_catches[i].cp - (char*)code >= code_bytes ) { erl_exit(1, "beam_catches_delmod: item %#x has cp %#lx which is not " "in module's range [%#lx,%#lx[\r\n", - i, (long)beam_catches[i].cp, + i, (long)p->beam_catches[i].cp, (long)code, (long)((char*)code + code_bytes)); } - beam_catches[i].cp = 0; - cdr = beam_catches[i].cdr; - beam_catches[i].cdr = free_list; - free_list = i; + p->beam_catches[i].cp = 0; + cdr = p->beam_catches[i].cdr; + p->beam_catches[i].cdr = p->free_list; + p->free_list = i; i = cdr; } } diff --git a/erts/emulator/beam/beam_catches.h b/erts/emulator/beam/beam_catches.h index 6223427f0d..b2bd2351a5 100644 --- a/erts/emulator/beam/beam_catches.h +++ b/erts/emulator/beam/beam_catches.h @@ -20,12 +20,21 @@ #ifndef __BEAM_CATCHES_H #define __BEAM_CATCHES_H +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "sys.h" +#include "code_ix.h" + #define BEAM_CATCHES_NIL (-1) void beam_catches_init(void); +void beam_catches_start_staging(void); +void beam_catches_end_staging(int commit); unsigned beam_catches_cons(BeamInstr* cp, unsigned cdr); BeamInstr *beam_catches_car(unsigned i); -void beam_catches_delmod(unsigned head, BeamInstr* code, unsigned code_bytes); +void beam_catches_delmod(unsigned head, BeamInstr* code, unsigned code_bytes, + ErtsCodeIndex); #define catch_pc(x) beam_catches_car(catch_val((x))) diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 8041c92162..a609ed8c71 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -84,6 +84,7 @@ erts_debug_breakpoint_2(BIF_ALIST_2) int i; int specified = 0; Eterm res; + BpFunctions f; if (bool != am_true && bool != am_false) goto error; @@ -114,18 +115,30 @@ erts_debug_breakpoint_2(BIF_ALIST_2) mfa[2] = signed_val(mfa[2]); } + if (!erts_try_seize_code_write_permission(BIF_P)) { + ERTS_BIF_YIELD2(bif_export[BIF_erts_debug_breakpoint_2], + BIF_P, BIF_ARG_1, BIF_ARG_2); + } erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN); erts_smp_thr_progress_block(); + erts_bp_match_functions(&f, mfa, specified); if (bool == am_true) { - res = make_small(erts_set_debug_break(mfa, specified)); + erts_set_debug_break(&f); + erts_install_breakpoints(&f); + erts_commit_staged_bp(); } else { - res = make_small(erts_clear_debug_break(mfa, specified)); + erts_clear_debug_break(&f); + erts_commit_staged_bp(); + erts_uninstall_breakpoints(&f); } + erts_consolidate_bp_data(&f, 1); + res = make_small(f.matched); + erts_bp_free_matched_functions(&f); erts_smp_thr_progress_unblock(); erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); - + erts_release_code_write_permission(); return res; error: @@ -207,6 +220,7 @@ erts_debug_disassemble_1(BIF_ALIST_1) BIF_RET(am_false); } } else if (is_tuple(addr)) { + ErtsCodeIndex code_ix; Module* modp; Eterm mod; Eterm name; @@ -225,14 +239,14 @@ erts_debug_disassemble_1(BIF_ALIST_1) goto error; } arity = signed_val(tp[3]); - modp = erts_get_module(mod); + code_ix = erts_active_code_ix(); + modp = erts_get_module(mod, code_ix); /* * Try the export entry first to allow disassembly of special functions * such as erts_debug:apply/4. Then search for it in the module. */ - - if ((ep = erts_find_function(mod, name, arity)) != NULL) { + if ((ep = erts_find_function(mod, name, arity, code_ix)) != NULL) { /* XXX: add "&& ep->address != ep->code+3" condition? * Consider a traced function. * Its ep will have ep->address == ep->code+3. @@ -241,9 +255,9 @@ erts_debug_disassemble_1(BIF_ALIST_1) * But this code_ptr will point to the start of the Export, * not the function's func_info instruction. BOOM !? */ - code_ptr = ((BeamInstr *) ep->address) - 5; + code_ptr = ((BeamInstr *) ep->addressv[code_ix]) - 5; funcinfo = code_ptr+2; - } else if (modp == NULL || (code_base = modp->code) == NULL) { + } else if (modp == NULL || (code_base = modp->curr.code) == NULL) { BIF_RET(am_undef); } else { n = code_base[MI_NUM_FUNCTIONS]; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index b831147295..5dc5aa1e03 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -26,7 +26,6 @@ #include "erl_vm.h" #include "global.h" #include "erl_process.h" -#include "erl_nmgc.h" #include "error.h" #include "bif.h" #include "big.h" @@ -218,7 +217,6 @@ BeamInstr beam_continue_exit[1]; BeamInstr* em_call_error_handler; BeamInstr* em_apply_bif; -BeamInstr* em_call_traced_function; /* NOTE These should be the only variables containing trace instructions. @@ -249,20 +247,6 @@ void** beam_ops; extern int count_instructions; #endif -#if defined(HYBRID) -#define SWAPIN \ - g_htop = global_htop; \ - g_hend = global_hend; \ - HTOP = HEAP_TOP(c_p); \ - E = c_p->stop - -#define SWAPOUT \ - global_htop = g_htop; \ - global_hend = g_hend; \ - HEAP_TOP(c_p) = HTOP; \ - c_p->stop = E - -#else #define SWAPIN \ HTOP = HEAP_TOP(c_p); \ E = c_p->stop @@ -290,8 +274,6 @@ extern int count_instructions; #define LIGHT_SWAPIN HTOP = HEAP_TOP(c_p) -#endif - #ifdef FORCE_HEAP_FRAGS # define HEAP_SPACE_VERIFIED(Words) do { \ c_p->space_verified = (Words); \ @@ -453,36 +435,6 @@ extern int count_instructions; CHECK_TERM(r(0)); \ } while (0) -#ifdef HYBRID -#ifdef INCREMENTAL -#define TestGlobalHeap(Nh, Live, hp) \ - do { \ - unsigned need = (Nh); \ - ASSERT(global_heap <= g_htop && g_htop <= global_hend); \ - SWAPOUT; \ - reg[0] = r(0); \ - FCALLS -= need; \ - (hp) = IncAlloc(c_p,need,reg,(Live)); \ - r(0) = reg[0]; \ - SWAPIN; \ - } while (0) -#else -#define TestGlobalHeap(Nh, Live, hp) \ - do { \ - unsigned need = (Nh); \ - ASSERT(global_heap <= g_htop && g_htop <= global_hend); \ - if (g_hend - g_htop < need) { \ - SWAPOUT; \ - reg[0] = r(0); \ - FCALLS -= erts_global_garbage_collect(c_p, need, reg, (Live)); \ - r(0) = reg[0]; \ - SWAPIN; \ - } \ - (hp) = global_htop; \ - } while (0) -#endif -#endif /* HYBRID */ - #define Init(N) make_blank(yb(N)) #define Init2(Y1, Y2) do { make_blank(Y1); make_blank(Y2); } while (0) @@ -538,7 +490,7 @@ extern int count_instructions; do { \ if (FCALLS > 0) { \ Eterm* dis_next; \ - SET_I(((Export *) Arg(0))->address); \ + SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \ dis_next = (Eterm *) *I; \ FCALLS--; \ CHECK_ARGS(I); \ @@ -547,7 +499,7 @@ extern int count_instructions; && FCALLS > neg_o_reds) { \ goto save_calls1; \ } else { \ - SET_I(((Export *) Arg(0))->address); \ + SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \ CHECK_ARGS(I); \ goto context_switch; \ } \ @@ -1008,16 +960,9 @@ static void save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, static struct StackTrace * get_trace_from_exc(Eterm exc); static Eterm make_arglist(Process* c_p, Eterm* reg, int a); -#if defined(VXWORKS) -static int init_done; -#endif - void init_emulator(void) { -#if defined(VXWORKS) - init_done = 0; -#endif erts_smp_atomic_init_nob(&warned_for_tuple_funs, (erts_aint_t) 0); process_main(); } @@ -1150,9 +1095,7 @@ dtrace_drvport_str(ErlDrvPort drvport, char *port_buf) */ void process_main(void) { -#if !defined(VXWORKS) static int init_done = 0; -#endif Process* c_p = NULL; int reds_used; #ifdef DEBUG @@ -1174,12 +1117,6 @@ void process_main(void) */ register Eterm* HTOP REG_htop = NULL; - -#ifdef HYBRID - Eterm *g_htop; - Eterm *g_hend; -#endif - /* Stack pointer. Grows downwards; points * to last item pushed (normally a saved * continuation pointer). @@ -1556,7 +1493,7 @@ void process_main(void) */ #ifdef USE_VM_CALL_PROBES if (DTRACE_ENABLED(global_function_entry)) { - BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address); + BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->addressv[erts_active_code_ix()]); DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]); } #endif @@ -1571,7 +1508,7 @@ void process_main(void) SET_CP(c_p, I+2); #ifdef USE_VM_CALL_PROBES if (DTRACE_ENABLED(global_function_entry)) { - BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address); + BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->addressv[erts_active_code_ix()]); DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]); } #endif @@ -1584,7 +1521,7 @@ void process_main(void) OpCase(i_call_ext_only_e): #ifdef USE_VM_CALL_PROBES if (DTRACE_ENABLED(global_function_entry)) { - BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address); + BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->addressv[erts_active_code_ix()]); DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]); } #endif @@ -4624,64 +4561,6 @@ void process_main(void) * Trace and debugging support. */ - /* - * At this point, I points to the code[3] in the export entry for - * a trace-enabled function. - * - * code[0]: Module - * code[1]: Function - * code[2]: Arity - * code[3]: &&call_traced_function - * code[4]: Address of function. - */ - OpCase(call_traced_function): { - if (IS_TRACED_FL(c_p, F_TRACE_CALLS)) { - unsigned offset = offsetof(Export, code) + 3*sizeof(BeamInstr); - Export* ep = (Export *) (((char *)I)-offset); - Uint32 flags; - - SWAPOUT; - reg[0] = r(0); - PROCESS_MAIN_CHK_LOCKS(c_p); - ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); - flags = erts_call_trace(c_p, ep->code, ep->match_prog_set, reg, - 0, &c_p->tracer_proc); - ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); - PROCESS_MAIN_CHK_LOCKS(c_p); - ASSERT(!ERTS_PROC_IS_EXITING(c_p)); - SWAPIN; - - if (flags & MATCH_SET_RX_TRACE) { - ASSERT(c_p->htop <= E && E <= c_p->hend); - if (E - 3 < HTOP) { - /* SWAPOUT, SWAPIN was done and r(0) was saved above */ - PROCESS_MAIN_CHK_LOCKS(c_p); - FCALLS -= erts_garbage_collect(c_p, 3, reg, ep->code[2]); - ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - PROCESS_MAIN_CHK_LOCKS(c_p); - r(0) = reg[0]; - SWAPIN; - } - E -= 3; - ASSERT(c_p->htop <= E && E <= c_p->hend); - ASSERT(is_CP((BeamInstr)(ep->code))); - ASSERT(is_internal_pid(c_p->tracer_proc) || - is_internal_port(c_p->tracer_proc)); - E[2] = make_cp(c_p->cp); /* Code in lower range on halfword */ - E[1] = am_true; /* Process tracer */ - E[0] = make_cp(ep->code); - c_p->cp = (flags & MATCH_SET_EXCEPTION_TRACE) - ? beam_exception_trace : beam_return_trace; - erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); - c_p->trace_flags |= F_EXCEPTION_TRACE; - erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); - } - } - SET_I((BeamInstr *)Arg(0)); - Dispatch(); - } - OpCase(return_trace): { BeamInstr* code = (BeamInstr *) (UWord) E[0]; @@ -4696,80 +4575,22 @@ void process_main(void) Goto(*I); } - OpCase(i_count_breakpoint): { - BeamInstr real_I; - - ErtsCountBreak(c_p, (BeamInstr *) I, &real_I); - ASSERT(VALID_INSTR(real_I)); - Goto(real_I); - } - - /* need to send mfa instead of bdt pointer - * the pointer might be deallocated. - */ - - OpCase(i_time_breakpoint): { + OpCase(i_generic_breakpoint): { BeamInstr real_I; - BpData **bds = (BpData **) (I)[-4]; - BpDataTime *bdt = NULL; - Uint ix = 0; -#ifdef ERTS_SMP - ix = c_p->scheduler_data->no - 1; -#else - ix = 0; -#endif - bdt = (BpDataTime *)bds[ix]; - - ASSERT((I)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); - ASSERT(bdt); - bdt = (BpDataTime *) bdt->next; - ASSERT(bdt); - bds[ix] = (BpData *) bdt; - real_I = bdt->orig_instr; + ASSERT(I[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); + SWAPOUT; + reg[0] = r(0); + real_I = erts_generic_breakpoint(c_p, I, reg); + r(0) = reg[0]; + SWAPIN; ASSERT(VALID_INSTR(real_I)); - - if (IS_TRACED_FL(c_p, F_TRACE_CALLS) && !(bdt->pause)) { - if ( (*(c_p->cp) == (BeamInstr) OpCode(i_return_time_trace)) || - (*(c_p->cp) == (BeamInstr) OpCode(return_trace)) || - (*(c_p->cp) == (BeamInstr) OpCode(i_return_to_trace))) { - /* This _IS_ a tail recursive call */ - SWAPOUT; - erts_trace_time_break(c_p, I, bdt, ERTS_BP_CALL_TIME_TAIL_CALL); - SWAPIN; - } else { - SWAPOUT; - erts_trace_time_break(c_p, I, bdt, ERTS_BP_CALL_TIME_CALL); - - /* r register needs to be copied to the array - * for the garbage collector - */ - ASSERT(c_p->htop <= E && E <= c_p->hend); - if (E - 2 < HTOP) { - reg[0] = r(0); - PROCESS_MAIN_CHK_LOCKS(c_p); - FCALLS -= erts_garbage_collect(c_p, 2, reg, I[-1]); - ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - PROCESS_MAIN_CHK_LOCKS(c_p); - r(0) = reg[0]; - } - SWAPIN; - - ASSERT(c_p->htop <= E && E <= c_p->hend); - - E -= 2; - E[0] = make_cp(I); - E[1] = make_cp(c_p->cp); /* original return address */ - c_p->cp = beam_return_time_trace; - } - } - Goto(real_I); } OpCase(i_return_time_trace): { BeamInstr *pc = (BeamInstr *) (UWord) E[0]; SWAPOUT; - erts_trace_time_break(c_p, pc, NULL, ERTS_BP_CALL_TIME_RETURN); + erts_trace_time_return(c_p, pc); SWAPIN; c_p->cp = NULL; SET_I((BeamInstr *) cp_val(E[1])); @@ -4777,114 +4598,6 @@ void process_main(void) Goto(*I); } - OpCase(i_trace_breakpoint): - if (! IS_TRACED_FL(c_p, F_TRACE_CALLS)) { - BeamInstr real_I; - - ErtsBreakSkip(c_p, (BeamInstr *) I, &real_I); - Goto(real_I); - } - /* Fall through to next case */ - OpCase(i_mtrace_breakpoint): { - BeamInstr real_I; - Uint32 flags; - Eterm tracer_pid; - Uint* cpp; - int return_to_trace = 0, need = 0; - flags = 0; - SWAPOUT; - reg[0] = r(0); - - if (*(c_p->cp) == (BeamInstr) OpCode(return_trace)) { - cpp = &E[2]; - } else if (*(c_p->cp) == (BeamInstr) OpCode(i_return_to_trace)) { - return_to_trace = !0; - cpp = &E[0]; - } else if (*(c_p->cp) == (BeamInstr) OpCode(i_return_time_trace)) { - return_to_trace = !0; - cpp = &E[0]; - } else { - cpp = NULL; - } - if (cpp) { - /* This _IS_ a tail recursive call, if there are - * return_trace and/or i_return_to_trace stackframes - * on the stack, they are not intermixed with y registers - */ - BeamInstr *cp_save = c_p->cp; - for (;;) { - ASSERT(is_CP(*cpp)); - if (*cp_val(*cpp) == (BeamInstr) OpCode(return_trace)) { - cpp += 3; - } else if (*cp_val(*cpp) == (BeamInstr) OpCode(i_return_to_trace)) { - return_to_trace = !0; - cpp += 1; - } else if (*cp_val(*cpp) == (BeamInstr) OpCode(i_return_time_trace)) { - cpp += 2; - } else - break; - } - c_p->cp = (BeamInstr *) cp_val(*cpp); - ASSERT(is_CP(*cpp)); - ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); - real_I = erts_trace_break(c_p, I, reg, &flags, &tracer_pid); - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); - SWAPIN; /* Needed by shared heap. */ - c_p->cp = cp_save; - } else { - ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); - real_I = erts_trace_break(c_p, I, reg, &flags, &tracer_pid); - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); - SWAPIN; /* Needed by shared heap. */ - } - - ASSERT(!ERTS_PROC_IS_EXITING(c_p)); - - if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) { - need += 1; - } - if (flags & MATCH_SET_RX_TRACE) { - need += 3; - } - if (need) { - ASSERT(c_p->htop <= E && E <= c_p->hend); - if (E - need < HTOP) { - /* SWAPOUT was done and r(0) was saved above */ - PROCESS_MAIN_CHK_LOCKS(c_p); - FCALLS -= erts_garbage_collect(c_p, need, reg, I[-1]); - ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - PROCESS_MAIN_CHK_LOCKS(c_p); - r(0) = reg[0]; - SWAPIN; - } - } - if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) { - E -= 1; - ASSERT(c_p->htop <= E && E <= c_p->hend); - E[0] = make_cp(c_p->cp); - c_p->cp = (BeamInstr *) beam_return_to_trace; - } - if (flags & MATCH_SET_RX_TRACE) { - E -= 3; - ASSERT(c_p->htop <= E && E <= c_p->hend); - ASSERT(is_CP((Eterm) (UWord) (I - 3))); - ASSERT(am_true == tracer_pid || - is_internal_pid(tracer_pid) || is_internal_port(tracer_pid)); - E[2] = make_cp(c_p->cp); - E[1] = tracer_pid; - E[0] = make_cp(I - 3); /* We ARE at the beginning of an - instruction, - the funcinfo is above i. */ - c_p->cp = - (flags & MATCH_SET_EXCEPTION_TRACE) - ? beam_exception_trace : beam_return_trace; - erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); - c_p->trace_flags |= F_EXCEPTION_TRACE; - erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); - } - Goto(real_I); - } - OpCase(i_return_to_trace): { if (IS_TRACED_FL(c_p, F_TRACE_RETURN_TO)) { Uint *cpp = (Uint*) E; @@ -5234,7 +4947,6 @@ void process_main(void) #endif /* NO_JUMP_TABLE */ em_call_error_handler = OpCode(call_error_handler); - em_call_traced_function = OpCode(call_traced_function); em_apply_bif = OpCode(apply_bif); beam_apply[0] = (BeamInstr) OpCode(i_apply); @@ -5275,7 +4987,7 @@ void process_main(void) save_calls(c_p, (Export *) Arg(0)); - SET_I(((Export *) Arg(0))->address); + SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); dis_next = (Eterm *) *I; FCALLS--; @@ -5952,7 +5664,8 @@ call_error_handler(Process* p, BeamInstr* fi, Eterm* reg, Eterm func) /* * Search for the error_handler module. */ - ep = erts_find_function(erts_proc_get_error_handler(p), func, 3); + ep = erts_find_function(erts_proc_get_error_handler(p), func, 3, + erts_active_code_ix()); if (ep == NULL) { /* No error handler */ p->current = fi; p->freason = EXC_UNDEF; @@ -5982,7 +5695,7 @@ call_error_handler(Process* p, BeamInstr* fi, Eterm* reg, Eterm func) reg[0] = fi[0]; reg[1] = fi[1]; reg[2] = args; - return ep->address; + return ep->addressv[erts_active_code_ix()]; } @@ -5996,7 +5709,7 @@ apply_setup_error_handler(Process* p, Eterm module, Eterm function, Uint arity, * there is no error handler module. */ - if ((ep = erts_find_export_entry(erts_proc_get_error_handler(p), + if ((ep = erts_active_export_entry(erts_proc_get_error_handler(p), am_undefined_function, 3)) == NULL) { return NULL; } else { @@ -6103,7 +5816,7 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg) * Note: All BIFs have export entries; thus, no special case is needed. */ - if ((ep = erts_find_export_entry(module, function, arity)) == NULL) { + if ((ep = erts_active_export_entry(module, function, arity)) == NULL) { if ((ep = apply_setup_error_handler(p, module, function, arity, reg)) == NULL) goto error; } else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) { save_calls(p, ep); @@ -6111,11 +5824,11 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg) #ifdef USE_VM_CALL_PROBES if (DTRACE_ENABLED(global_function_entry)) { - BeamInstr *fptr = (BeamInstr *) ep->address; + BeamInstr *fptr = (BeamInstr *) ep->addressv[erts_active_code_ix()]; DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]); } #endif - return ep->address; + return ep->addressv[erts_active_code_ix()]; } static BeamInstr* @@ -6157,7 +5870,7 @@ fixed_apply(Process* p, Eterm* reg, Uint arity) * Note: All BIFs have export entries; thus, no special case is needed. */ - if ((ep = erts_find_export_entry(module, function, arity)) == NULL) { + if ((ep = erts_active_export_entry(module, function, arity)) == NULL) { if ((ep = apply_setup_error_handler(p, module, function, arity, reg)) == NULL) goto error; } else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) { @@ -6166,11 +5879,11 @@ fixed_apply(Process* p, Eterm* reg, Uint arity) #ifdef USE_VM_CALL_PROBES if (DTRACE_ENABLED(global_function_entry)) { - BeamInstr *fptr = (BeamInstr *) ep->address; + BeamInstr *fptr = (BeamInstr *) ep->addressv[erts_active_code_ix()]; DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]); } #endif - return ep->address; + return ep->addressv[erts_active_code_ix()]; } int @@ -6348,7 +6061,7 @@ call_fun(Process* p, /* Current process. */ Export* ep; Module* modp; Eterm module; - + ErtsCodeIndex code_ix = erts_active_code_ix(); /* * No arity. There is no module loaded that defines the fun, @@ -6356,9 +6069,9 @@ call_fun(Process* p, /* Current process. */ * representation (the module has never been loaded), * or the module defining the fun has been unloaded. */ - module = fe->module; - if ((modp = erts_get_module(module)) != NULL && modp->code != NULL) { + if ((modp = erts_get_module(module, code_ix)) != NULL + && modp->curr.code != NULL) { /* * There is a module loaded, but obviously the fun is not * defined in it. We must not call the error_handler @@ -6373,7 +6086,7 @@ call_fun(Process* p, /* Current process. */ */ ep = erts_find_function(erts_proc_get_error_handler(p), - am_undefined_lambda, 3); + am_undefined_lambda, 3, code_ix); if (ep == NULL) { /* No error handler */ p->current = NULL; p->freason = EXC_UNDEF; @@ -6383,7 +6096,7 @@ call_fun(Process* p, /* Current process. */ reg[1] = fun; reg[2] = args; reg[3] = NIL; - return ep->address; + return ep->addressv[erts_active_code_ix()]; } } } else if (is_export_header(hdr)) { @@ -6395,7 +6108,7 @@ call_fun(Process* p, /* Current process. */ if (arity == actual_arity) { DTRACE_GLOBAL_CALL(p, ep->code[0], ep->code[1], (Uint)ep->code[2]); - return ep->address; + return ep->addressv[erts_active_code_ix()]; } else { /* * Wrong arity. First build a list of the arguments. @@ -6446,8 +6159,8 @@ call_fun(Process* p, /* Current process. */ erts_send_warning_to_logger(p->group_leader, dsbufp); } - if ((ep = erts_find_export_entry(module, function, arity)) == NULL) { - ep = erts_find_export_entry(erts_proc_get_error_handler(p), + if ((ep = erts_active_export_entry(module, function, arity)) == NULL) { + ep = erts_active_export_entry(erts_proc_get_error_handler(p), am_undefined_function, 3); if (ep == NULL) { p->freason = EXC_UNDEF; @@ -6471,7 +6184,7 @@ call_fun(Process* p, /* Current process. */ reg[2] = args; } DTRACE_GLOBAL_CALL(p, module, function, arity); - return ep->address; + return ep->addressv[erts_active_code_ix()]; } else { badfun: p->current = NULL; @@ -6533,10 +6246,8 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) hp = funp->env; erts_refc_inc(&fe->refc, 2); funp->thing_word = HEADER_FUN; -#ifndef HYBRID /* FIND ME! */ funp->next = MSO(p).first; MSO(p).first = (struct erl_off_heap_header*) funp; -#endif funp->fe = fe; funp->num_free = num_free; funp->creator = p->id; @@ -6576,7 +6287,8 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity) if ((ep = export_get(&e)) == NULL) { return 0; } - return ep->address == ep->code+3 && (ep->code[3] == (BeamInstr) em_apply_bif); + return ep->addressv[erts_active_code_ix()] == ep->code+3 + && (ep->code[3] == (BeamInstr) em_apply_bif); } diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index dd788df6e4..d3f55a2ba4 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -352,27 +352,6 @@ typedef struct LoaderState { int loc_size; /* Size of location info in bytes (2/4) */ } LoaderState; -/* - * Layout of the line table. - */ - -#define MI_LINE_FNAME_PTR 0 -#define MI_LINE_LOC_TAB 1 -#define MI_LINE_LOC_SIZE 2 -#define MI_LINE_FUNC_TAB 3 - -#define LINE_INVALID_LOCATION (0) - -/* - * Macros for manipulating locations. - */ - -#define IS_VALID_LOCATION(File, Line) \ - ((unsigned) (File) < 255 && (unsigned) (Line) < ((1 << 24) - 1)) -#define MAKE_LOCATION(File, Line) (((File) << 24) | (Line)) -#define LOC_FILE(Loc) ((Loc) >> 24) -#define LOC_LINE(Loc) ((Loc) & ((1 << 24)-1)) - #define GetTagAndValue(Stp, Tag, Val) \ do { \ BeamInstr __w; \ @@ -496,7 +475,8 @@ typedef struct LoaderState { } while (0) -static void free_state(LoaderState* stp); +static void free_loader_state(Binary* magic); +static void loader_state_dtor(Binary* magic); static Eterm insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, Eterm group_leader, Eterm module, BeamInstr* code, Uint size); @@ -507,6 +487,7 @@ static int verify_chunks(LoaderState* stp); static int load_atom_table(LoaderState* stp); static int load_import_table(LoaderState* stp); static int read_export_table(LoaderState* stp); +static int is_bif(Eterm mod, Eterm func, unsigned arity); static int read_lambda_table(LoaderState* stp); static int read_literal_table(LoaderState* stp); static int read_line_table(LoaderState* stp); @@ -548,22 +529,9 @@ static Eterm native_addresses(Process* p, Eterm mod); int patch_funentries(Eterm Patchlist); int patch(Eterm Addresses, Uint fe); static int safe_mul(UWord a, UWord b, UWord* resp); -static void lookup_loc(FunctionInfo* fi, BeamInstr* pc, - BeamInstr* modp, int idx); - static int must_swap_floats; -/* - * The following variables keep a sorted list of address ranges for - * each module. It allows us to quickly find a function given an - * instruction pointer. - */ -Range* modules = NULL; /* Sorted lists of module addresses. */ -int num_loaded_modules; /* Number of loaded modules. */ -int allocated_modules; /* Number of slots allocated. */ -Range* mid_module = NULL; /* Cached search start point */ - Uint erts_total_code_size; /**********************************************************************/ @@ -579,11 +547,7 @@ void init_load(void) f.fd = 1.0; must_swap_floats = (f.fw[0] == 0); - allocated_modules = 128; - modules = (Range *) erts_alloc(ERTS_ALC_T_MODULE_REFS, - allocated_modules*sizeof(Range)); - mid_module = modules; - num_loaded_modules = 0; + erts_init_ranges(); } static void @@ -595,7 +559,7 @@ define_file(LoaderState* stp, char* name, int idx) } Eterm -erts_load_module(Process *c_p, +erts_preload_module(Process *c_p, ErtsProcLocks c_p_locks, Eterm group_leader, /* Group leader or NIL if none. */ Eterm* modp, /* @@ -605,15 +569,16 @@ erts_load_module(Process *c_p, byte* code, /* Points to the code to load */ Uint size) /* Size of code to load. */ { - LoaderState* stp = erts_alloc_loader_state(); + Binary* magic = erts_alloc_loader_state(); Eterm retval; - retval = erts_prepare_loading(stp, c_p, group_leader, modp, + ASSERT(!erts_initialized); + retval = erts_prepare_loading(magic, c_p, group_leader, modp, code, size); if (retval != NIL) { return retval; } - return erts_finish_loading(stp, c_p, c_p_locks, modp); + return erts_finish_loading(magic, c_p, c_p_locks, modp); } /* #define LOAD_MEMORY_HARD_DEBUG 1*/ @@ -629,11 +594,13 @@ extern void check_allocated_block(Uint type, void *blk); #endif Eterm -erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader, +erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader, Eterm* modp, byte* code, Uint unloaded_size) { Eterm retval = am_badfile; + LoaderState* stp; + stp = ERTS_MAGIC_BIN_DATA(magic); stp->module = *modp; stp->group_leader = group_leader; @@ -666,7 +633,7 @@ erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader, /* * Initialize code area. */ - stp->code_buffer_size = erts_next_heap_size(2048 + stp->num_functions, 0); + stp->code_buffer_size = 2048 + stp->num_functions; stp->code = (BeamInstr *) erts_alloc(ERTS_ALC_T_CODE, sizeof(BeamInstr) * stp->code_buffer_size); @@ -679,8 +646,6 @@ erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader, stp->code[MI_COMPILE_PTR] = 0; stp->code[MI_COMPILE_SIZE] = 0; stp->code[MI_COMPILE_SIZE_ON_HEAP] = 0; - stp->code[MI_NUM_BREAKPOINTS] = 0; - /* * Read the atom table. @@ -774,23 +739,24 @@ erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader, load_error: if (retval != NIL) { - free_state(stp); + free_loader_state(magic); } return retval; } Eterm -erts_finish_loading(LoaderState* stp, Process* c_p, +erts_finish_loading(Binary* magic, Process* c_p, ErtsProcLocks c_p_locks, Eterm* modp) { Eterm retval; + LoaderState* stp = ERTS_MAGIC_BIN_DATA(magic); /* * No other process may run since we will update the export * table which is not protected by any locks. */ - ERTS_SMP_LC_ASSERT(erts_initialized == 0 || + ERTS_SMP_LC_ASSERT(erts_initialized == 0 || erts_is_code_ix_locked() || erts_smp_thr_progress_is_blocking()); /* @@ -811,7 +777,6 @@ erts_finish_loading(LoaderState* stp, Process* c_p, * exported and imported functions. This can't fail. */ - erts_export_consolidate(); CHKBLK(ERTS_ALC_T_CODE,stp->code); final_touch(stp); @@ -837,16 +802,20 @@ erts_finish_loading(LoaderState* stp, Process* c_p, } load_error: - free_state(stp); + free_loader_state(magic); return retval; } -LoaderState* +Binary* erts_alloc_loader_state(void) { LoaderState* stp; + Binary* magic; - stp = erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(LoaderState)); + magic = erts_create_magic_binary(sizeof(LoaderState), + loader_state_dtor); + erts_refc_inc(&magic->refc, 1); + stp = ERTS_MAGIC_BIN_DATA(magic); stp->bin = NULL; stp->function = THE_NON_VALUE; /* Function not known yet */ stp->arity = 0; @@ -875,76 +844,123 @@ erts_alloc_loader_state(void) stp->line_instr = 0; stp->func_line = 0; stp->fname = 0; - return stp; + return magic; +} + +/* + * Return the module name (a tagged atom) for the prepared code + * in the magic binary, or NIL if the binary does not contain + * prepared code. + */ +Eterm +erts_module_for_prepared_code(Binary* magic) +{ + LoaderState* stp; + + if (ERTS_MAGIC_BIN_DESTRUCTOR(magic) != loader_state_dtor) { + return NIL; + } + stp = ERTS_MAGIC_BIN_DATA(magic); + if (stp->code != 0) { + return stp->module; + } else { + return NIL; + } } static void -free_state(LoaderState* stp) +free_loader_state(Binary* magic) { + loader_state_dtor(magic); + if (erts_refc_dectest(&magic->refc, 0) == 0) { + erts_bin_free(magic); + } +} + +/* + * This destructor function can safely be called multiple times. + */ +static void +loader_state_dtor(Binary* magic) +{ + LoaderState* stp = ERTS_MAGIC_BIN_DATA(magic); + if (stp->bin != 0) { driver_free_binary(stp->bin); + stp->bin = 0; } if (stp->code != 0) { erts_free(ERTS_ALC_T_CODE, stp->code); + stp->code = 0; } - if (stp->labels != NULL) { - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->labels); + if (stp->labels != 0) { + erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->labels); + stp->labels = 0; } - if (stp->atom != NULL) { - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->atom); + if (stp->atom != 0) { + erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->atom); + stp->atom = 0; } - if (stp->import != NULL) { - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->import); + if (stp->import != 0) { + erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->import); + stp->import = 0; } - if (stp->export != NULL) { - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->export); + if (stp->export != 0) { + erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->export); + stp->export = 0; } if (stp->lambdas != stp->def_lambdas) { - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->lambdas); + erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->lambdas); + stp->lambdas = stp->def_lambdas; } - if (stp->literals != NULL) { + if (stp->literals != 0) { int i; for (i = 0; i < stp->num_literals; i++) { - if (stp->literals[i].heap != NULL) { - erts_free(ERTS_ALC_T_LOADER_TMP, + if (stp->literals[i].heap != 0) { + erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->literals[i].heap); + stp->literals[i].heap = 0; } } - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->literals); + erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->literals); + stp->literals = 0; } - while (stp->literal_patches != NULL) { + while (stp->literal_patches != 0) { LiteralPatch* next = stp->literal_patches->next; - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->literal_patches); + erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->literal_patches); stp->literal_patches = next; } - while (stp->string_patches != NULL) { + while (stp->string_patches != 0) { StringPatch* next = stp->string_patches->next; - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->string_patches); + erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->string_patches); stp->string_patches = next; } - while (stp->genop_blocks) { - GenOpBlock* next = stp->genop_blocks->next; - erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->genop_blocks); - stp->genop_blocks = next; - } if (stp->line_item != 0) { - erts_free(ERTS_ALC_T_LOADER_TMP, stp->line_item); + erts_free(ERTS_ALC_T_PREPARED_CODE, stp->line_item); + stp->line_item = 0; } if (stp->line_instr != 0) { - erts_free(ERTS_ALC_T_LOADER_TMP, stp->line_instr); + erts_free(ERTS_ALC_T_PREPARED_CODE, stp->line_instr); + stp->line_instr = 0; } if (stp->func_line != 0) { - erts_free(ERTS_ALC_T_LOADER_TMP, stp->func_line); + erts_free(ERTS_ALC_T_PREPARED_CODE, stp->func_line); + stp->func_line = 0; } if (stp->fname != 0) { - erts_free(ERTS_ALC_T_LOADER_TMP, stp->fname); + erts_free(ERTS_ALC_T_PREPARED_CODE, stp->fname); + stp->fname = 0; } - erts_free(ERTS_ALC_T_LOADER_TMP, stp); + /* + * The following data items should have been freed earlier. + */ + + ASSERT(stp->genop_blocks == 0); } static Eterm @@ -954,7 +970,6 @@ insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, { Module* modp; Eterm retval; - int i; if ((retval = beam_make_current_old(c_p, c_p_locks, module)) != NIL) { erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); @@ -971,30 +986,15 @@ insert_new_code(Process *c_p, ErtsProcLocks c_p_locks, erts_total_code_size += size; modp = erts_put_module(module); - modp->code = code; - modp->code_length = size; - modp->catches = BEAM_CATCHES_NIL; /* Will be filled in later. */ + modp->curr.code = code; + modp->curr.code_length = size; + modp->curr.catches = BEAM_CATCHES_NIL; /* Will be filled in later. */ /* - * Update address table (used for finding a function from a PC value). + * Update ranges (used for finding a function from a PC value). */ - if (num_loaded_modules == allocated_modules) { - allocated_modules *= 2; - modules = (Range *) erts_realloc(ERTS_ALC_T_MODULE_REFS, - (void *) modules, - allocated_modules * sizeof(Range)); - } - for (i = num_loaded_modules; i > 0; i--) { - if (code > modules[i-1].start) { - break; - } - modules[i] = modules[i-1]; - } - modules[i].start = code; - modules[i].end = (BeamInstr *) (((byte *)code) + size); - num_loaded_modules++; - mid_module = &modules[num_loaded_modules/2]; + erts_update_ranges(code, size); return NIL; } @@ -1217,9 +1217,8 @@ load_atom_table(LoaderState* stp) GetInt(stp, 4, stp->num_atoms); stp->num_atoms++; - stp->atom = erts_alloc(ERTS_ALC_T_LOADER_TMP, - erts_next_heap_size((stp->num_atoms*sizeof(Eterm)), - 0)); + stp->atom = erts_alloc(ERTS_ALC_T_PREPARED_CODE, + stp->num_atoms*sizeof(Eterm)); /* * Read all atoms. @@ -1263,10 +1262,8 @@ load_import_table(LoaderState* stp) int i; GetInt(stp, 4, stp->num_imports); - stp->import = erts_alloc(ERTS_ALC_T_LOADER_TMP, - erts_next_heap_size((stp->num_imports * - sizeof(ImportEntry)), - 0)); + stp->import = erts_alloc(ERTS_ALC_T_PREPARED_CODE, + stp->num_imports * sizeof(ImportEntry)); for (i = 0; i < stp->num_imports; i++) { int n; Eterm mod; @@ -1296,7 +1293,7 @@ load_import_table(LoaderState* stp) * If the export entry refers to a BIF, get the pointer to * the BIF function. */ - if ((e = erts_find_export_entry(mod, func, arity)) != NULL) { + if ((e = erts_active_export_entry(mod, func, arity)) != NULL) { if (e->code[3] == (BeamInstr) em_apply_bif) { stp->import[i].bf = (BifFunction) e->code[4]; if (func == am_load_nif && mod == am_erlang && arity == 2) { @@ -1315,16 +1312,8 @@ load_import_table(LoaderState* stp) static int read_export_table(LoaderState* stp) { - static struct { - Eterm mod; - Eterm func; - int arity; - } allow_redef[] = { - /* The BIFs that are allowed to be redefined by Erlang code */ - {am_erlang,am_apply,2}, - {am_erlang,am_apply,3}, - }; int i; + BeamInstr* address; GetInt(stp, 4, stp->num_exps); if (stp->num_exps > stp->num_functions) { @@ -1332,7 +1321,7 @@ read_export_table(LoaderState* stp) stp->num_exps, stp->num_functions); } stp->export - = (ExportEntry *) erts_alloc(ERTS_ALC_T_LOADER_TMP, + = (ExportEntry *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, (stp->num_exps * sizeof(ExportEntry))); for (i = 0; i < stp->num_exps; i++) { @@ -1340,7 +1329,6 @@ read_export_table(LoaderState* stp) Uint value; Eterm func; Uint arity; - Export* e; GetInt(stp, 4, n); GetAtom(stp, n, func); @@ -1358,29 +1346,34 @@ read_export_table(LoaderState* stp) if (value == 0) { LoadError2(stp, "export table entry %d: label %d not resolved", i, n); } - stp->export[i].address = stp->code + value; + stp->export[i].address = address = stp->code + value; /* - * Check that we are not redefining a BIF (except the ones allowed to - * redefine). + * Find out if there is a BIF with the same name. */ - if ((e = erts_find_export_entry(stp->module, func, arity)) != NULL) { - if (e->code[3] == (BeamInstr) em_apply_bif) { - int j; - for (j = 0; j < sizeof(allow_redef)/sizeof(allow_redef[0]); j++) { - if (stp->module == allow_redef[j].mod && - func == allow_redef[j].func && - arity == allow_redef[j].arity) { - break; - } - } - if (j == sizeof(allow_redef)/sizeof(allow_redef[0])) { - LoadError2(stp, "exported function %T/%d redefines BIF", - func, arity); - } - } + if (!is_bif(stp->module, func, arity)) { + continue; + } + + /* + * This is a stub for a BIF. + * + * It should not be exported, and the information in its + * func_info instruction should be invalidated so that it + * can be filtered out by module_info(functions) and by + * any other functions that walk through all local functions. + */ + + if (stp->labels[n].patches) { + LoadError3(stp, "there are local calls to the stub for " + "the BIF %T:%T/%d", + stp->module, func, arity); } + stp->export[i].address = NULL; + address[-1] = 0; + address[-2] = NIL; + address[-3] = NIL; } return 1; @@ -1388,15 +1381,39 @@ read_export_table(LoaderState* stp) return 0; } + +static int +is_bif(Eterm mod, Eterm func, unsigned arity) +{ + Export* e = erts_active_export_entry(mod, func, arity); + if (e == NULL) { + return 0; + } + if (e->code[3] != (BeamInstr) em_apply_bif) { + return 0; + } + if (mod == am_erlang && func == am_apply && arity == 3) { + /* + * erlang:apply/3 is a special case -- it is implemented + * as an instruction and it is OK to redefine it. + */ + return 0; + } + return 1; +} + static int read_lambda_table(LoaderState* stp) { int i; GetInt(stp, 4, stp->num_lambdas); - stp->lambdas_allocated = stp->num_lambdas; - stp->lambdas = (Lambda *) erts_alloc(ERTS_ALC_T_LOADER_TMP, - stp->num_lambdas * sizeof(Lambda)); + if (stp->num_lambdas > stp->lambdas_allocated) { + ASSERT(stp->lambdas == stp->def_lambdas); + stp->lambdas_allocated = stp->num_lambdas; + stp->lambdas = (Lambda *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, + stp->num_lambdas * sizeof(Lambda)); + } for (i = 0; i < stp->num_lambdas; i++) { Uint n; Uint32 Index; @@ -1446,7 +1463,7 @@ read_literal_table(LoaderState* stp) stp->file_p = uncompressed; stp->file_left = (unsigned) uncompressed_sz; GetInt(stp, 4, stp->num_literals); - stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_LOADER_TMP, + stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, stp->num_literals * sizeof(Literal)); stp->allocated_literals = stp->num_literals; @@ -1466,7 +1483,7 @@ read_literal_table(LoaderState* stp) if ((heap_size = erts_decode_ext_size(p, sz)) < 0) { LoadError1(stp, "literal %d: bad external format", i); } - hp = stp->literals[i].heap = erts_alloc(ERTS_ALC_T_LOADER_TMP, + hp = stp->literals[i].heap = erts_alloc(ERTS_ALC_T_PREPARED_CODE, heap_size*sizeof(Eterm)); stp->literals[i].off_heap.first = 0; stp->literals[i].off_heap.overhead = 0; @@ -1538,7 +1555,7 @@ read_line_table(LoaderState* stp) */ num_line_items++; - lp = (BeamInstr *) erts_alloc(ERTS_ALC_T_LOADER_TMP, + lp = (BeamInstr *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, num_line_items * sizeof(BeamInstr)); stp->line_item = lp; stp->num_line_items = num_line_items; @@ -1594,7 +1611,7 @@ read_line_table(LoaderState* stp) */ if (stp->num_fnames != 0) { - stp->fname = (Eterm *) erts_alloc(ERTS_ALC_T_LOADER_TMP, + stp->fname = (Eterm *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, stp->num_fnames * sizeof(Eterm)); for (i = 0; i < stp->num_fnames; i++) { @@ -1610,11 +1627,11 @@ read_line_table(LoaderState* stp) /* * Allocate the arrays to be filled while code is being loaded. */ - stp->line_instr = (LineInstr *) erts_alloc(ERTS_ALC_T_LOADER_TMP, + stp->line_instr = (LineInstr *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, stp->num_line_instrs * sizeof(LineInstr)); stp->current_li = 0; - stp->func_line = (int *) erts_alloc(ERTS_ALC_T_LOADER_TMP, + stp->func_line = (int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, stp->num_functions * sizeof(int)); @@ -1677,7 +1694,7 @@ read_code_header(LoaderState* stp) * Initialize label table. */ - stp->labels = (Label *) erts_alloc(ERTS_ALC_T_LOADER_TMP, + stp->labels = (Label *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, stp->num_labels * sizeof(Label)); for (i = 0; i < stp->num_labels; i++) { stp->labels[i].value = 0; @@ -1703,9 +1720,9 @@ read_code_header(LoaderState* stp) #define CodeNeed(w) do { \ ASSERT(ci <= code_buffer_size); \ if (code_buffer_size < ci+(w)) { \ - code_buffer_size = erts_next_heap_size(ci+(w), 0); \ - stp->code = code \ - = (BeamInstr *) erts_realloc(ERTS_ALC_T_CODE, \ + code_buffer_size = 2*ci+(w); \ + stp->code = code = \ + (BeamInstr *) erts_realloc(ERTS_ALC_T_CODE, \ (void *) code, \ code_buffer_size * sizeof(BeamInstr)); \ } \ @@ -1731,6 +1748,7 @@ load_code(LoaderState* stp) GenOp* last_op = NULL; GenOp** last_op_next = NULL; int arity; + int retval = 1; /* * The size of the loaded func_info instruction is needed @@ -2457,7 +2475,11 @@ load_code(LoaderState* stp) case op_int_code_end: stp->code_buffer_size = code_buffer_size; stp->ci = ci; - return 1; + stp->function = THE_NON_VALUE; + stp->genop = NULL; + stp->specific_op = -1; + retval = 1; + goto cleanup; } /* @@ -2471,9 +2493,20 @@ load_code(LoaderState* stp) } } - load_error: - return 0; + retval = 0; + + cleanup: + /* + * Clean up everything that is not needed any longer. + */ + + while (stp->genop_blocks) { + GenOpBlock* next = stp->genop_blocks->next; + erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->genop_blocks); + stp->genop_blocks = next; + } + return retval; } @@ -4265,24 +4298,31 @@ final_touch(LoaderState* stp) index = next; } modp = erts_put_module(stp->module); - modp->catches = catches; + modp->curr.catches = catches; /* * Export functions. */ for (i = 0; i < stp->num_exps; i++) { - Export* ep = erts_export_put(stp->module, stp->export[i].function, - stp->export[i].arity); + Export* ep; + BeamInstr* address = stp->export[i].address; + + if (address == NULL) { + /* Skip stub for a BIF */ + continue; + } + ep = erts_export_put(stp->module, stp->export[i].function, + stp->export[i].arity); if (!on_load) { - ep->address = stp->export[i].address; + ep->addressv[erts_staging_code_ix()] = address; } else { /* * Don't make any of the exported functions * callable yet. */ - ep->address = ep->code+3; - ep->code[4] = (BeamInstr) stp->export[i].address; + ep->addressv[erts_staging_code_ix()] = ep->code+3; + ep->code[4] = (BeamInstr) address; } } @@ -4926,7 +4966,7 @@ new_label(LoaderState* stp) int num = stp->num_labels; stp->num_labels++; - stp->labels = (Label *) erts_realloc(ERTS_ALC_T_LOADER_TMP, + stp->labels = (Label *) erts_realloc(ERTS_ALC_T_PREPARED_CODE, (void *) stp->labels, stp->num_labels * sizeof(Label)); stp->labels[num].value = 0; @@ -4937,7 +4977,8 @@ new_label(LoaderState* stp) static void new_literal_patch(LoaderState* stp, int pos) { - LiteralPatch* p = erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(LiteralPatch)); + LiteralPatch* p = erts_alloc(ERTS_ALC_T_PREPARED_CODE, + sizeof(LiteralPatch)); p->pos = pos; p->next = stp->literal_patches; stp->literal_patches = p; @@ -4946,7 +4987,7 @@ new_literal_patch(LoaderState* stp, int pos) static void new_string_patch(LoaderState* stp, int pos) { - StringPatch* p = erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(StringPatch)); + StringPatch* p = erts_alloc(ERTS_ALC_T_PREPARED_CODE, sizeof(StringPatch)); p->pos = pos; p->next = stp->string_patches; stp->string_patches = p; @@ -4964,14 +5005,14 @@ new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size) ASSERT(stp->num_literals == 0); stp->allocated_literals = 8; need = stp->allocated_literals * sizeof(Literal); - stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_LOADER_TMP, + stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, need); } else if (stp->allocated_literals <= stp->num_literals) { Uint need; stp->allocated_literals *= 2; need = stp->allocated_literals * sizeof(Literal); - stp->literals = (Literal *) erts_realloc(ERTS_ALC_T_LOADER_TMP, + stp->literals = (Literal *) erts_realloc(ERTS_ALC_T_PREPARED_CODE, (void *) stp->literals, need); } @@ -4980,7 +5021,7 @@ new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size) lit = stp->literals + stp->num_literals; lit->offset = 0; lit->heap_size = heap_size; - lit->heap = erts_alloc(ERTS_ALC_T_LOADER_TMP, heap_size*sizeof(Eterm)); + lit->heap = erts_alloc(ERTS_ALC_T_PREPARED_CODE, heap_size*sizeof(Eterm)); lit->term = make_boxed(lit->heap); lit->off_heap.first = 0; lit->off_heap.overhead = 0; @@ -4999,7 +5040,7 @@ erts_module_info_0(Process* p, Eterm module) return THE_NON_VALUE; } - if (erts_get_module(module) == NULL) { + if (erts_get_module(module, erts_active_code_ix()) == NULL) { return THE_NON_VALUE; } @@ -5054,32 +5095,43 @@ functions_in_module(Process* p, /* Process whose heap to use. */ BeamInstr* code; int i; Uint num_functions; + Uint need; Eterm* hp; + Eterm* hp_end; Eterm result = NIL; if (is_not_atom(mod)) { return THE_NON_VALUE; } - modp = erts_get_module(mod); + modp = erts_get_module(mod, erts_active_code_ix()); if (modp == NULL) { return THE_NON_VALUE; } - code = modp->code; + code = modp->curr.code; num_functions = code[MI_NUM_FUNCTIONS]; - hp = HAlloc(p, 5*num_functions); + need = 5*num_functions; + hp = HAlloc(p, need); + hp_end = hp + need; for (i = num_functions-1; i >= 0 ; i--) { BeamInstr* func_info = (BeamInstr *) code[MI_FUNCTIONS+i]; Eterm name = (Eterm) func_info[3]; int arity = (int) func_info[4]; Eterm tuple; - ASSERT(is_atom(name)); - tuple = TUPLE2(hp, name, make_small(arity)); - hp += 3; - result = CONS(hp, tuple, result); - hp += 2; + /* + * If the function name is [], this entry is a stub for + * a BIF that should be ignored. + */ + ASSERT(is_atom(name) || is_nil(name)); + if (is_atom(name)) { + tuple = TUPLE2(hp, name, make_small(arity)); + hp += 3; + result = CONS(hp, tuple, result); + hp += 2; + } } + HRelease(p, hp_end, hp); return result; } @@ -5106,12 +5158,12 @@ native_addresses(Process* p, Eterm mod) return THE_NON_VALUE; } - modp = erts_get_module(mod); + modp = erts_get_module(mod, erts_active_code_ix()); if (modp == NULL) { return THE_NON_VALUE; } - code = modp->code; + code = modp->curr.code; num_functions = code[MI_NUM_FUNCTIONS]; need = (6+BIG_UINT_HEAP_SIZE)*num_functions; hp = HAlloc(p, need); @@ -5122,9 +5174,11 @@ native_addresses(Process* p, Eterm mod) int arity = (int) func_info[4]; Eterm tuple; - ASSERT(is_atom(name)); + ASSERT(is_atom(name) || is_nil(name)); /* [] if BIF stub */ if (func_info[1] != 0) { - Eterm addr = erts_bld_uint(&hp, NULL, func_info[1]); + Eterm addr; + ASSERT(is_atom(name)); + addr = erts_bld_uint(&hp, NULL, func_info[1]); tuple = erts_bld_tuple(&hp, NULL, 3, name, make_small(arity), addr); result = erts_bld_cons(&hp, NULL, tuple, result); } @@ -5149,18 +5203,20 @@ exported_from_module(Process* p, /* Process whose heap to use. */ Eterm* hp = NULL; Eterm* hend = NULL; Eterm result = NIL; + ErtsCodeIndex code_ix; if (is_not_atom(mod)) { return THE_NON_VALUE; } - for (i = 0; i < export_list_size(); i++) { - Export* ep = export_list(i); + code_ix = erts_active_code_ix(); + for (i = 0; i < export_list_size(code_ix); i++) { + Export* ep = export_list(i,code_ix); if (ep->code[0] == mod) { Eterm tuple; - if (ep->address == ep->code+3 && + if (ep->addressv[code_ix] == ep->code+3 && ep->code[3] == (BeamInstr) em_call_error_handler) { /* There is a call to the function, but it does not exist. */ continue; @@ -5204,11 +5260,11 @@ attributes_for_module(Process* p, /* Process whose heap to use. */ return THE_NON_VALUE; } - modp = erts_get_module(mod); + modp = erts_get_module(mod, erts_active_code_ix()); if (modp == NULL) { return THE_NON_VALUE; } - code = modp->code; + code = modp->curr.code; ext = (byte *) code[MI_ATTR_PTR]; if (ext != NULL) { hp = HAlloc(p, code[MI_ATTR_SIZE_ON_HEAP]); @@ -5244,11 +5300,11 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */ return THE_NON_VALUE; } - modp = erts_get_module(mod); + modp = erts_get_module(mod, erts_active_code_ix()); if (modp == NULL) { return THE_NON_VALUE; } - code = modp->code; + code = modp->curr.code; ext = (byte *) code[MI_COMPILE_PTR]; if (ext != NULL) { hp = HAlloc(p, code[MI_COMPILE_SIZE_ON_HEAP]); @@ -5263,113 +5319,6 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */ } /* - * Find a function from the given pc and fill information in - * the FunctionInfo struct. If the full_info is non-zero, fill - * in all available information (including location in the - * source code). If no function is found, the 'current' field - * will be set to NULL. - */ - -void -erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info) -{ - Range* low = modules; - Range* high = low + num_loaded_modules; - Range* mid = mid_module; - - fi->current = NULL; - fi->needed = 5; - fi->loc = LINE_INVALID_LOCATION; - while (low < high) { - if (pc < mid->start) { - high = mid; - } else if (pc > mid->end) { - low = mid + 1; - } else { - BeamInstr** low1 = (BeamInstr **) (mid->start + MI_FUNCTIONS); - BeamInstr** high1 = low1 + mid->start[MI_NUM_FUNCTIONS]; - BeamInstr** mid1; - - while (low1 < high1) { - mid1 = low1 + (high1-low1) / 2; - if (pc < mid1[0]) { - high1 = mid1; - } else if (pc < mid1[1]) { - mid_module = mid; - fi->current = mid1[0]+2; - if (full_info) { - BeamInstr** fp = (BeamInstr **) (mid->start + - MI_FUNCTIONS); - int idx = mid1 - fp; - lookup_loc(fi, pc, mid->start, idx); - } - return; - } else { - low1 = mid1 + 1; - } - } - return; - } - mid = low + (high-low) / 2; - } -} - -static void -lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamInstr* modp, int idx) -{ - Eterm* line = (Eterm *) modp[MI_LINE_TABLE]; - Eterm* low; - Eterm* high; - Eterm* mid; - Eterm pc; - - if (line == 0) { - return; - } - - pc = (Eterm) (BeamInstr) orig_pc; - fi->fname_ptr = (Eterm *) (BeamInstr) line[MI_LINE_FNAME_PTR]; - low = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx]; - high = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx+1]; - while (high > low) { - mid = low + (high-low) / 2; - if (pc < mid[0]) { - high = mid; - } else if (pc < mid[1]) { - int file; - int index = mid - (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB]; - - if (line[MI_LINE_LOC_SIZE] == 2) { - Uint16* loc_table = - (Uint16 *) (BeamInstr) line[MI_LINE_LOC_TAB]; - fi->loc = loc_table[index]; - } else { - Uint32* loc_table = - (Uint32 *) (BeamInstr) line[MI_LINE_LOC_TAB]; - ASSERT(line[MI_LINE_LOC_SIZE] == 4); - fi->loc = loc_table[index]; - } - if (fi->loc == LINE_INVALID_LOCATION) { - return; - } - fi->needed += 3+2+3+2; - file = LOC_FILE(fi->loc); - if (file == 0) { - /* Special case: Module name with ".erl" appended */ - Atom* mod_atom = atom_tab(atom_val(fi->current[0])); - fi->needed += 2*(mod_atom->len+4); - } else { - Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1])); - fi->needed += 2*ap->len; - } - return; - } else { - low = mid + 1; - } - } -} - -/* * Build a single {M,F,A,Loction} item to be part of * a stack trace. */ @@ -5449,6 +5398,7 @@ code_get_chunk_2(BIF_ALIST_2) Process* p = BIF_P; Eterm Bin = BIF_ARG_1; Eterm Chunk = BIF_ARG_2; + Binary* magic = 0; LoaderState* stp; Uint chunk = 0; ErlSubBin* sb; @@ -5461,12 +5411,13 @@ code_get_chunk_2(BIF_ALIST_2) Eterm real_bin; byte* temp_alloc = NULL; - stp = erts_alloc_loader_state(); + magic = erts_alloc_loader_state(); + stp = ERTS_MAGIC_BIN_DATA(magic); if ((start = erts_get_aligned_binary_bytes(Bin, &temp_alloc)) == NULL) { error: erts_free_aligned_binary_bytes(temp_alloc); - if (stp) { - free_state(stp); + if (magic) { + free_loader_state(magic); } BIF_ERROR(p, BADARG); } @@ -5511,7 +5462,7 @@ code_get_chunk_2(BIF_ALIST_2) done: erts_free_aligned_binary_bytes(temp_alloc); - free_state(stp); + free_loader_state(magic); return res; } @@ -5524,14 +5475,16 @@ code_module_md5_1(BIF_ALIST_1) { Process* p = BIF_P; Eterm Bin = BIF_ARG_1; + Binary* magic; LoaderState* stp; byte* bytes; byte* temp_alloc = NULL; Eterm res; - stp = erts_alloc_loader_state(); + magic = erts_alloc_loader_state(); + stp = ERTS_MAGIC_BIN_DATA(magic); if ((bytes = erts_get_aligned_binary_bytes(Bin, &temp_alloc)) == NULL) { - free_state(stp); + free_loader_state(magic); BIF_ERROR(p, BADARG); } stp->module = THE_NON_VALUE; /* Suppress diagnostiscs */ @@ -5545,7 +5498,7 @@ code_module_md5_1(BIF_ALIST_1) done: erts_free_aligned_binary_bytes(temp_alloc); - free_state(stp); + free_loader_state(magic); return res; } @@ -5601,7 +5554,7 @@ stub_read_export_table(LoaderState* stp) stp->num_exps, stp->num_functions); } stp->export - = (ExportEntry *) erts_alloc(ERTS_ALC_T_LOADER_TMP, + = (ExportEntry *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, stp->num_exps * sizeof(ExportEntry)); for (i = 0; i < stp->num_exps; i++) { @@ -5627,20 +5580,29 @@ stub_final_touch(LoaderState* stp, BeamInstr* fp) { int i; int n = stp->num_exps; + Eterm mod = fp[2]; Eterm function = fp[3]; int arity = fp[4]; #ifdef HIPE Lambda* lp; #endif + if (is_bif(mod, function, arity)) { + fp[1] = 0; + fp[2] = 0; + fp[3] = 0; + fp[4] = 0; + return; + } + /* * Test if the function should be exported. */ for (i = 0; i < n; i++) { if (stp->export[i].function == function && stp->export[i].arity == arity) { - Export* ep = erts_export_put(fp[2], function, arity); - ep->address = fp+5; + Export* ep = erts_export_put(mod, function, arity); + ep->addressv[erts_staging_code_ix()] = fp+5; return; } } @@ -5819,6 +5781,7 @@ patch_funentries(Eterm Patchlist) Eterm erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) { + Binary* magic; LoaderState* stp; BeamInstr Funcs; BeamInstr Patchlist; @@ -5840,7 +5803,8 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) * Must initialize stp->lambdas here because the error handling code * at label 'error' uses it. */ - stp = erts_alloc_loader_state(); + magic = erts_alloc_loader_state(); + stp = ERTS_MAGIC_BIN_DATA(magic); if (is_not_atom(Mod)) { goto error; @@ -5920,7 +5884,6 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) code[MI_COMPILE_PTR] = 0; code[MI_COMPILE_SIZE] = 0; code[MI_COMPILE_SIZE_ON_HEAP] = 0; - code[MI_NUM_BREAKPOINTS] = 0; code[MI_LITERALS_START] = 0; code[MI_LITERALS_END] = 0; code[MI_LITERALS_OFF_HEAP] = 0; @@ -6028,13 +5991,13 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) if (patch_funentries(Patchlist)) { erts_free_aligned_binary_bytes(temp_alloc); - free_state(stp); + free_loader_state(magic); return Mod; } error: erts_free_aligned_binary_bytes(temp_alloc); - free_state(stp); + free_loader_state(magic); BIF_ERROR(p, BADARG); } @@ -6051,3 +6014,4 @@ static int safe_mul(UWord a, UWord b, UWord* resp) return (res / b) == a; } } + diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index 997ba197db..1048f258a5 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -49,11 +49,6 @@ extern void** beam_ops; extern BeamInstr beam_debug_apply[]; extern BeamInstr* em_call_error_handler; extern BeamInstr* em_apply_bif; -extern BeamInstr* em_call_traced_function; -typedef struct { - BeamInstr* start; /* Pointer to start of module. */ - BeamInstr* end; /* Points one word beyond last function in module. */ -} Range; /* * The following variables keep a sorted list of address ranges for @@ -61,11 +56,6 @@ typedef struct { * instruction pointer. */ -extern Range* modules; -extern int num_loaded_modules; -extern int allocated_modules; -extern Range* mid_module; - /* Total code size in bytes */ extern Uint erts_total_code_size; /* @@ -94,27 +84,22 @@ extern Uint erts_total_code_size; #define MI_COMPILE_SIZE_ON_HEAP 6 /* - * Number of breakpoints in module is stored in this word - */ -#define MI_NUM_BREAKPOINTS 7 - -/* * Literal area (constant pool). */ -#define MI_LITERALS_START 8 -#define MI_LITERALS_END 9 -#define MI_LITERALS_OFF_HEAP 10 +#define MI_LITERALS_START 7 +#define MI_LITERALS_END 8 +#define MI_LITERALS_OFF_HEAP 9 /* * Pointer to the on_load function (or NULL if none). */ -#define MI_ON_LOAD_FUNCTION_PTR 11 +#define MI_ON_LOAD_FUNCTION_PTR 10 /* * Pointer to the line table (or NULL if none). */ -#define MI_LINE_TABLE 12 +#define MI_LINE_TABLE 11 /* * Start of function pointer table. This table contains pointers to @@ -125,5 +110,27 @@ extern Uint erts_total_code_size; * this table. */ -#define MI_FUNCTIONS 13 +#define MI_FUNCTIONS 12 + +/* + * Layout of the line table. + */ + +#define MI_LINE_FNAME_PTR 0 +#define MI_LINE_LOC_TAB 1 +#define MI_LINE_LOC_SIZE 2 +#define MI_LINE_FUNC_TAB 3 + +#define LINE_INVALID_LOCATION (0) + +/* + * Macros for manipulating locations. + */ + +#define IS_VALID_LOCATION(File, Line) \ + ((unsigned) (File) < 255 && (unsigned) (Line) < ((1 << 24) - 1)) +#define MAKE_LOCATION(File, Line) (((File) << 24) | (Line)) +#define LOC_FILE(Loc) ((Loc) >> 24) +#define LOC_LINE(Loc) ((Loc) & ((1 << 24)-1)) + #endif /* _BEAM_LOAD_H */ diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c new file mode 100644 index 0000000000..0f2d5d0c2a --- /dev/null +++ b/erts/emulator/beam/beam_ranges.c @@ -0,0 +1,349 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "erl_vm.h" +#include "global.h" +#include "beam_load.h" + +typedef struct { + BeamInstr* start; /* Pointer to start of module. */ + erts_smp_atomic_t end; /* (BeamInstr*) Points one word beyond last function in module. */ +} Range; + +/* Range 'end' needs to be atomic as we purge module + by setting end=start in active code_ix */ +#define RANGE_END(R) ((BeamInstr*)erts_smp_atomic_read_nob(&(R)->end)) + +static Range* find_range(BeamInstr* pc); +static void lookup_loc(FunctionInfo* fi, BeamInstr* pc, + BeamInstr* modp, int idx); + +/* + * The following variables keep a sorted list of address ranges for + * each module. It allows us to quickly find a function given an + * instruction pointer. + */ +struct ranges { + Range* modules; /* Sorted lists of module addresses. */ + Sint n; /* Number of range entries. */ + Sint allocated; /* Number of allocated entries. */ + erts_smp_atomic_t mid; /* Cached search start point */ +}; +static struct ranges r[ERTS_NUM_CODE_IX]; +static erts_smp_atomic_t mem_used; + +#ifdef HARD_DEBUG +static void check_consistency(struct ranges* p) +{ + int i; + + ASSERT(p->n <= p->allocated); + ASSERT((Uint)(p->mid - p->modules) < p->n || + (p->mid == p->modules && p->n == 0)); + for (i = 0; i < p->n; i++) { + ASSERT(p->modules[i].start <= RANGE_END(&p->modules[i])); + ASSERT(!i || RANGE_END(&p->modules[i-1]) < p->modules[i].start); + } +} +# define CHECK(r) check_consistency(r) +#else +# define CHECK(r) +#endif /* HARD_DEBUG */ + + +void +erts_init_ranges(void) +{ + Sint i; + + erts_smp_atomic_init_nob(&mem_used, 0); + for (i = 0; i < ERTS_NUM_CODE_IX; i++) { + r[i].modules = 0; + r[i].n = 0; + r[i].allocated = 0; + erts_smp_atomic_init_nob(&r[i].mid, 0); + } +} + +void +erts_start_staging_ranges(void) +{ + ErtsCodeIndex dst = erts_staging_code_ix(); + + if (r[dst].modules) { + erts_smp_atomic_add_nob(&mem_used, -r[dst].allocated); + erts_free(ERTS_ALC_T_MODULE_REFS, r[dst].modules); + r[dst].modules = NULL; + } +} + +void +erts_end_staging_ranges(int commit) +{ + ErtsCodeIndex dst = erts_staging_code_ix(); + + if (commit && r[dst].modules == NULL) { + Sint i; + Sint n; + + /* No modules added, just clone src and remove purged code. */ + ErtsCodeIndex src = erts_active_code_ix(); + + erts_smp_atomic_add_nob(&mem_used, r[src].n); + r[dst].modules = erts_alloc(ERTS_ALC_T_MODULE_REFS, + r[src].n * sizeof(Range)); + r[dst].allocated = r[src].n; + n = 0; + for (i = 0; i < r[src].n; i++) { + Range* rp = r[src].modules+i; + if (rp->start < RANGE_END(rp)) { + /* Only insert a module that has not been purged. */ + r[dst].modules[n] = *rp; + n++; + } + } + r[dst].n = n; + erts_smp_atomic_set_nob(&r[dst].mid, + (erts_aint_t) (r[dst].modules + n / 2)); + } +} + +void +erts_update_ranges(BeamInstr* code, Uint size) +{ + ErtsCodeIndex dst = erts_staging_code_ix(); + ErtsCodeIndex src = erts_active_code_ix(); + Sint i; + Sint n; + Sint need; + + if (src == dst) { + ASSERT(!erts_initialized); + + /* + * During start-up of system, the indices are the same. + * Handle this by faking a source area. + */ + src = (src+1) % ERTS_NUM_CODE_IX; + if (r[src].modules) { + erts_smp_atomic_add_nob(&mem_used, -r[src].allocated); + erts_free(ERTS_ALC_T_MODULE_REFS, r[src].modules); + } + r[src] = r[dst]; + r[dst].modules = 0; + } + + CHECK(&r[src]); + + ASSERT(r[dst].modules == NULL); + need = r[dst].allocated = r[src].n + 1; + erts_smp_atomic_add_nob(&mem_used, need); + r[dst].modules = (Range *) erts_alloc(ERTS_ALC_T_MODULE_REFS, + need * sizeof(Range)); + n = 0; + for (i = 0; i < r[src].n; i++) { + Range* rp = r[src].modules+i; + if (code < rp->start) { + r[dst].modules[n].start = code; + erts_smp_atomic_init_nob(&r[dst].modules[n].end, + (erts_aint_t)(((byte *)code) + size)); + ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < code); + n++; + break; + } + if (rp->start < RANGE_END(rp)) { + /* Only insert a module that has not been purged. */ + r[dst].modules[n].start = rp->start; + erts_smp_atomic_init_nob(&r[dst].modules[n].end, + (erts_aint_t)(RANGE_END(rp))); + ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < rp->start); + n++; + } + } + + while (i < r[src].n) { + Range* rp = r[src].modules+i; + if (rp->start < RANGE_END(rp)) { + /* Only insert a module that has not been purged. */ + r[dst].modules[n].start = rp->start; + erts_smp_atomic_init_nob(&r[dst].modules[n].end, + (erts_aint_t)(RANGE_END(rp))); + ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < rp->start); + n++; + } + i++; + } + + if (n == 0 || code > r[dst].modules[n-1].start) { + r[dst].modules[n].start = code; + erts_smp_atomic_init_nob(&r[dst].modules[n].end, + (erts_aint_t)(((byte *)code) + size)); + ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < code); + n++; + } + + ASSERT(n <= r[src].n+1); + r[dst].n = n; + erts_smp_atomic_set_nob(&r[dst].mid, + (erts_aint_t) (r[dst].modules + n / 2)); + + CHECK(&r[dst]); + CHECK(&r[src]); +} + +void +erts_remove_from_ranges(BeamInstr* code) +{ + Range* rp = find_range(code); + erts_smp_atomic_set_nob(&rp->end, (erts_aint_t)rp->start); +} + +UWord +erts_ranges_sz(void) +{ + return erts_smp_atomic_read_nob(&mem_used) * sizeof(Range); +} + +/* + * Find a function from the given pc and fill information in + * the FunctionInfo struct. If the full_info is non-zero, fill + * in all available information (including location in the + * source code). If no function is found, the 'current' field + * will be set to NULL. + */ + +void +erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info) +{ + BeamInstr** low; + BeamInstr** high; + BeamInstr** mid; + Range* rp; + + fi->current = NULL; + fi->needed = 5; + fi->loc = LINE_INVALID_LOCATION; + rp = find_range(pc); + if (rp == 0) { + return; + } + + low = (BeamInstr **) (rp->start + MI_FUNCTIONS); + high = low + rp->start[MI_NUM_FUNCTIONS]; + while (low < high) { + mid = low + (high-low) / 2; + if (pc < mid[0]) { + high = mid; + } else if (pc < mid[1]) { + fi->current = mid[0]+2; + if (full_info) { + BeamInstr** fp = (BeamInstr **) (rp->start + + MI_FUNCTIONS); + int idx = mid - fp; + lookup_loc(fi, pc, rp->start, idx); + } + return; + } else { + low = mid + 1; + } + } +} + +static Range* +find_range(BeamInstr* pc) +{ + ErtsCodeIndex active = erts_active_code_ix(); + Range* low = r[active].modules; + Range* high = low + r[active].n; + Range* mid = (Range *) erts_smp_atomic_read_nob(&r[active].mid); + + CHECK(&r[active]); + while (low < high) { + if (pc < mid->start) { + high = mid; + } else if (pc > RANGE_END(mid)) { + low = mid + 1; + } else { + erts_smp_atomic_set_nob(&r[active].mid, (erts_aint_t) mid); + return mid; + } + mid = low + (high-low) / 2; + } + return 0; +} + +static void +lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamInstr* modp, int idx) +{ + Eterm* line = (Eterm *) modp[MI_LINE_TABLE]; + Eterm* low; + Eterm* high; + Eterm* mid; + Eterm pc; + + if (line == 0) { + return; + } + + pc = (Eterm) (BeamInstr) orig_pc; + fi->fname_ptr = (Eterm *) (BeamInstr) line[MI_LINE_FNAME_PTR]; + low = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx]; + high = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx+1]; + while (high > low) { + mid = low + (high-low) / 2; + if (pc < mid[0]) { + high = mid; + } else if (pc < mid[1]) { + int file; + int index = mid - (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB]; + + if (line[MI_LINE_LOC_SIZE] == 2) { + Uint16* loc_table = + (Uint16 *) (BeamInstr) line[MI_LINE_LOC_TAB]; + fi->loc = loc_table[index]; + } else { + Uint32* loc_table = + (Uint32 *) (BeamInstr) line[MI_LINE_LOC_TAB]; + ASSERT(line[MI_LINE_LOC_SIZE] == 4); + fi->loc = loc_table[index]; + } + if (fi->loc == LINE_INVALID_LOCATION) { + return; + } + fi->needed += 3+2+3+2; + file = LOC_FILE(fi->loc); + if (file == 0) { + /* Special case: Module name with ".erl" appended */ + Atom* mod_atom = atom_tab(atom_val(fi->current[0])); + fi->needed += 2*(mod_atom->len+4); + } else { + Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1])); + fi->needed += 2*ap->len; + } + return; + } else { + low = mid + 1; + } + } +} diff --git a/erts/emulator/beam/benchmark.c b/erts/emulator/beam/benchmark.c index 7fbf44a03c..7ac14b8e8b 100644 --- a/erts/emulator/beam/benchmark.c +++ b/erts/emulator/beam/benchmark.c @@ -33,17 +33,6 @@ unsigned long long messages_copied; unsigned long long messages_ego; unsigned long long minor_gc; unsigned long long major_gc; -#ifdef HYBRID -unsigned long long minor_global_gc; -unsigned long long major_global_gc; -unsigned long long gc_in_copy; -#ifdef INCREMENTAL -unsigned long long minor_gc_cycles; -unsigned long long major_gc_cycles; -unsigned long long minor_gc_stages; -unsigned long long major_gc_stages; -#endif -#endif #endif /* BM_COUNTERS */ #ifdef BM_TIMERS @@ -191,17 +180,6 @@ void init_benchmarking() messages_ego = 0; minor_gc = 0; major_gc = 0; -#ifdef HYBRID - minor_global_gc = 0; - major_global_gc = 0; - gc_in_copy = 0; -#ifdef INCREMENTAL - minor_gc_cycles = 0; - major_gc_cycles = 0; - minor_gc_stages = 0; - major_gc_stages = 0; -#endif -#endif #endif /* BM_COUNTERS */ #ifdef BM_HEAP_SIZES @@ -243,16 +221,6 @@ void save_statistics() erts_fprintf(file,"Number of processes spawned: %lld\n",processes_spawned); erts_fprintf(file,"Number of local minor GCs: %lld\n",minor_gc); erts_fprintf(file,"Number of local major GCs: %lld\n",major_gc); -#ifdef HYBRID - erts_fprintf(file,"Number of global minor GCs: %lld\n",minor_global_gc); - erts_fprintf(file,"Number of global major GCs: %lld\n",major_global_gc); -#ifdef INCREMENTAL - erts_fprintf(file,"Number of minor GC-cycles: %lld\n",minor_gc_cycles); - erts_fprintf(file,"Number of major GC-cycles: %lld\n",major_gc_cycles); - erts_fprintf(file,"Number of minor GC-stages: %lld\n",minor_gc_stages); - erts_fprintf(file,"Number of major GC-stages: %lld\n",major_gc_stages); -#endif -#endif erts_fprintf(file,"Number of messages sent: %lld\n",messages_sent); erts_fprintf(file,"Number of messages copied: %lld\n",messages_copied); erts_fprintf(file,"Number of messages sent to self: %lld\n",messages_ego); diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h index eedb06a1b6..003e821bce 100644 --- a/erts/emulator/beam/benchmark.h +++ b/erts/emulator/beam/benchmark.h @@ -99,17 +99,6 @@ extern unsigned long long messages_copied; extern unsigned long long messages_ego; extern unsigned long long minor_gc; extern unsigned long long major_gc; -#ifdef HYBRID -extern unsigned long long minor_global_gc; -extern unsigned long long major_global_gc; -extern unsigned long long gc_in_copy; -#ifdef INCREMENTAL -extern unsigned long long minor_gc_cycles; -extern unsigned long long major_gc_cycles; -extern unsigned long long minor_gc_stages; -extern unsigned long long major_gc_stages; -#endif -#endif #define BM_COUNT(var) (var)++; diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 20ac0637e5..66f4259d20 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3500,22 +3500,6 @@ BIF_RETTYPE garbage_collect_0(BIF_ALIST_0) } /**********************************************************************/ -/* Perform garbage collection of the message area */ - -BIF_RETTYPE garbage_collect_message_area_0(BIF_ALIST_0) -{ -#if defined(HYBRID) && !defined(INCREMENTAL) - int reds = 0; - - FLAGS(BIF_P) |= F_NEED_FULLSWEEP; - reds = erts_global_garbage_collect(BIF_P, 0, NULL, 0); - BIF_RET2(am_true, reds); -#else - BIF_RET(am_false); -#endif -} - -/**********************************************************************/ /* Return a list of active ports */ BIF_RETTYPE ports_0(BIF_ALIST_0) @@ -3779,7 +3763,8 @@ BIF_RETTYPE function_exported_3(BIF_ALIST_3) is_not_small(BIF_ARG_3)) { BIF_ERROR(BIF_P, BADARG); } - if (erts_find_function(BIF_ARG_1, BIF_ARG_2, signed_val(BIF_ARG_3)) == NULL) { + if (erts_find_function(BIF_ARG_1, BIF_ARG_2, signed_val(BIF_ARG_3), + erts_active_code_ix()) == NULL) { BIF_RET(am_false); } BIF_RET(am_true); @@ -4512,6 +4497,21 @@ erts_bif_prep_await_proc_exit_apply_trap(Process *c_p, Export bif_return_trap_export; +void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a, + Eterm (*bif)(BIF_ALIST_0)) +{ + int i; + sys_memset((void *) ep, 0, sizeof(Export)); + for (i=0; i<ERTS_NUM_CODE_IX; i++) { + ep->addressv[i] = &ep->code[3]; + } + ep->code[0] = m; + ep->code[1] = f; + ep->code[2] = a; + ep->code[3] = (BeamInstr) em_apply_bif; + ep->code[4] = (BeamInstr) bif; +} + void erts_init_bif(void) { reference0 = 0; @@ -4527,17 +4527,13 @@ void erts_init_bif(void) * yield the calling process traps to. The only thing it does: * return the value passed as argument. */ - sys_memset((void *) &bif_return_trap_export, 0, sizeof(Export)); - bif_return_trap_export.address = &bif_return_trap_export.code[3]; - bif_return_trap_export.code[0] = am_erlang; - bif_return_trap_export.code[1] = am_bif_return_trap; + erts_init_trap_export(&bif_return_trap_export, am_erlang, am_bif_return_trap, #ifdef DEBUG - bif_return_trap_export.code[2] = 2; + 2 #else - bif_return_trap_export.code[2] = 1; + 1 #endif - bif_return_trap_export.code[3] = (BeamInstr) em_apply_bif; - bif_return_trap_export.code[4] = (BeamInstr) &bif_return_trap; + , &bif_return_trap); flush_monitor_message_trap = erts_export_put(am_erlang, am_flush_monitor_message, diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index d20089a9fb..7cb2c78815 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -125,7 +125,7 @@ do { \ #define ERTS_BIF_PREP_TRAP0(Ret, Trap, Proc) \ do { \ (Proc)->arity = 0; \ - (Proc)->i = (BeamInstr*) ((Trap)->address); \ + (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \ (Proc)->freason = TRAP; \ (Ret) = THE_NON_VALUE; \ } while (0) @@ -135,7 +135,7 @@ do { \ Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \ (Proc)->arity = 1; \ reg[0] = (Eterm) (A0); \ - (Proc)->i = (BeamInstr*) ((Trap)->address); \ + (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \ (Proc)->freason = TRAP; \ (Ret) = THE_NON_VALUE; \ } while (0) @@ -146,7 +146,7 @@ do { \ (Proc)->arity = 2; \ reg[0] = (Eterm) (A0); \ reg[1] = (Eterm) (A1); \ - (Proc)->i = (BeamInstr*) ((Trap)->address); \ + (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \ (Proc)->freason = TRAP; \ (Ret) = THE_NON_VALUE; \ } while (0) @@ -158,7 +158,7 @@ do { \ reg[0] = (Eterm) (A0); \ reg[1] = (Eterm) (A1); \ reg[2] = (Eterm) (A2); \ - (Proc)->i = (BeamInstr*) ((Trap)->address); \ + (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \ (Proc)->freason = TRAP; \ (Ret) = THE_NON_VALUE; \ } while (0) @@ -170,13 +170,13 @@ do { \ reg[0] = (Eterm) (A0); \ reg[1] = (Eterm) (A1); \ reg[2] = (Eterm) (A2); \ - (Proc)->i = (BeamInstr*) ((Trap)->address); \ + (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \ (Proc)->freason = TRAP; \ } while (0) #define BIF_TRAP0(p, Trap_) do { \ (p)->arity = 0; \ - (p)->i = (BeamInstr*) ((Trap_)->address); \ + (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \ (p)->freason = TRAP; \ return THE_NON_VALUE; \ } while(0) @@ -185,7 +185,7 @@ do { \ Eterm* reg = ERTS_PROC_GET_SCHDATA((p))->x_reg_array; \ (p)->arity = 1; \ reg[0] = (A0); \ - (p)->i = (BeamInstr*) ((Trap_)->address); \ + (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \ (p)->freason = TRAP; \ return THE_NON_VALUE; \ } while(0) @@ -195,7 +195,7 @@ do { \ (p)->arity = 2; \ reg[0] = (A0); \ reg[1] = (A1); \ - (p)->i = (BeamInstr*) ((Trap_)->address); \ + (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \ (p)->freason = TRAP; \ return THE_NON_VALUE; \ } while(0) @@ -206,7 +206,7 @@ do { \ reg[0] = (A0); \ reg[1] = (A1); \ reg[2] = (A2); \ - (p)->i = (BeamInstr*) ((Trap_)->address); \ + (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \ (p)->freason = TRAP; \ return THE_NON_VALUE; \ } while(0) diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 8a85e102d1..f7dad2767f 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -99,8 +99,6 @@ bif erlang:garbage_collect/0 bif 'erl.system':garbage_collect/0 ebif_garbage_collect_0 bif erlang:garbage_collect/1 bif 'erl.system':garbage_collect/1 ebif_garbage_collect_1 -bif erlang:garbage_collect_message_area/0 -bif 'erl.system':garbage_collect_message_area/0 ebif_garbage_collect_message_area_0 bif erlang:get/0 bif 'erl.lang.proc.pdict':get/0 ebif_get_0 bif erlang:get/1 @@ -144,8 +142,6 @@ bif erlang:list_to_pid/1 bif 'erl.lang.proc':string_to_pid/1 ebif_string_to_pid_1 list_to_pid_1 bif erlang:list_to_tuple/1 bif 'erl.lang.tuple':from_list/1 ebif_list_to_tuple_1 -bif erlang:load_module/2 -bif 'erl.system.code':load/2 ebif_load_module_2 bif erlang:loaded/0 bif 'erl.system.code':loaded/0 ebif_loaded_0 bif erlang:localtime/0 @@ -831,6 +827,13 @@ bif erlang:dt_restore_tag/1 bif erlang:dt_prepend_vm_tag_data/1 bif erlang:dt_append_vm_tag_data/1 + +# +# New in R16B. +# +bif erlang:prepare_loading/2 +bif erlang:finish_loading/1 + # # Obsolete # diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 9cb5f2cc16..0a51e00016 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -382,17 +382,22 @@ loaded(int to, void *to_arg) int old = 0; int cur = 0; BeamInstr* code; + Module* modp; + ErtsCodeIndex code_ix; + + code_ix = erts_active_code_ix(); + erts_rlock_old_code(code_ix); /* * Calculate and print totals. */ - for (i = 0; i < module_code_size(); i++) { - if (module_code(i) != NULL && - ((module_code(i)->code_length != 0) || - (module_code(i)->old_code_length != 0))) { - cur += module_code(i)->code_length; - if (module_code(i)->old_code_length != 0) { - old += module_code(i)->old_code_length; + for (i = 0; i < module_code_size(code_ix); i++) { + if ((modp = module_code(i, code_ix)) != NULL && + ((modp->curr.code_length != 0) || + (modp->old.code_length != 0))) { + cur += modp->curr.code_length; + if (modp->old.code_length != 0) { + old += modp->old.code_length; } } } @@ -403,21 +408,22 @@ loaded(int to, void *to_arg) * Print one line per module. */ - for (i = 0; i < module_code_size(); i++) { + for (i = 0; i < module_code_size(code_ix); i++) { + modp = module_code(i, code_ix); if (!ERTS_IS_CRASH_DUMPING) { /* * Interactive dump; keep it brief. */ - if (module_code(i) != NULL && - ((module_code(i)->code_length != 0) || - (module_code(i)->old_code_length != 0))) { - erts_print(to, to_arg, "%T", make_atom(module_code(i)->module)); - cur += module_code(i)->code_length; - erts_print(to, to_arg, " %d", module_code(i)->code_length ); - if (module_code(i)->old_code_length != 0) { + if (modp != NULL && + ((modp->curr.code_length != 0) || + (modp->old.code_length != 0))) { + erts_print(to, to_arg, "%T", make_atom(modp->module)); + cur += modp->curr.code_length; + erts_print(to, to_arg, " %d", modp->curr.code_length ); + if (modp->old.code_length != 0) { erts_print(to, to_arg, " (%d old)", - module_code(i)->old_code_length ); - old += module_code(i)->old_code_length; + modp->old.code_length ); + old += modp->old.code_length; } erts_print(to, to_arg, "\n"); } @@ -425,15 +431,15 @@ loaded(int to, void *to_arg) /* * To crash dump; make it parseable. */ - if (module_code(i) != NULL && - ((module_code(i)->code_length != 0) || - (module_code(i)->old_code_length != 0))) { + if (modp != NULL && + ((modp->curr.code_length != 0) || + (modp->old.code_length != 0))) { erts_print(to, to_arg, "=mod:"); - erts_print(to, to_arg, "%T", make_atom(module_code(i)->module)); + erts_print(to, to_arg, "%T", make_atom(modp->module)); erts_print(to, to_arg, "\n"); erts_print(to, to_arg, "Current size: %d\n", - module_code(i)->code_length); - code = module_code(i)->code; + modp->curr.code_length); + code = modp->curr.code; if (code != NULL && code[MI_ATTR_PTR]) { erts_print(to, to_arg, "Current attributes: "); dump_attributes(to, to_arg, (byte *) code[MI_ATTR_PTR], @@ -445,9 +451,9 @@ loaded(int to, void *to_arg) code[MI_COMPILE_SIZE]); } - if (module_code(i)->old_code_length != 0) { - erts_print(to, to_arg, "Old size: %d\n", module_code(i)->old_code_length); - code = module_code(i)->old_code; + if (modp->old.code_length != 0) { + erts_print(to, to_arg, "Old size: %d\n", modp->old.code_length); + code = modp->old.code; if (code[MI_ATTR_PTR]) { erts_print(to, to_arg, "Old attributes: "); dump_attributes(to, to_arg, (byte *) code[MI_ATTR_PTR], @@ -462,6 +468,7 @@ loaded(int to, void *to_arg) } } } + erts_runlock_old_code(code_ix); } diff --git a/erts/emulator/beam/code_ix.c b/erts/emulator/beam/code_ix.c new file mode 100644 index 0000000000..8025058ee0 --- /dev/null +++ b/erts/emulator/beam/code_ix.c @@ -0,0 +1,166 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "code_ix.h" +#include "global.h" +#include "beam_catches.h" + + + +#if 0 +# define CIX_TRACE(text) erts_fprintf(stderr, "CIX_TRACE: " text " act=%u load=%u\r\n", erts_active_code_ix(), erts_staging_code_ix()) +#else +# define CIX_TRACE(text) +#endif + +erts_smp_atomic32_t the_active_code_index; +erts_smp_atomic32_t the_staging_code_index; + +static int the_code_ix_lock = 0; +struct code_ix_queue_item { + Process *p; + struct code_ix_queue_item* next; +}; +static struct code_ix_queue_item* the_code_ix_queue = NULL; +static erts_smp_mtx_t the_code_ix_queue_lock; + +#ifdef ERTS_ENABLE_LOCK_CHECK +static erts_tsd_key_t has_code_write_permission; +#endif + +void erts_code_ix_init(void) +{ + /* We start emulator by initializing preloaded modules + * single threaded with active and staging set both to zero. + * Preloading is finished by a commit that will set things straight. + */ + erts_smp_atomic32_init_nob(&the_active_code_index, 0); + erts_smp_atomic32_init_nob(&the_staging_code_index, 0); + erts_smp_mtx_init(&the_code_ix_queue_lock, "code_ix_queue"); +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_tsd_key_create(&has_code_write_permission); +#endif + CIX_TRACE("init"); +} + +void erts_start_staging_code_ix(void) +{ + beam_catches_start_staging(); + export_start_staging(); + module_start_staging(); + erts_start_staging_ranges(); + CIX_TRACE("start"); +} + + +void erts_end_staging_code_ix(void) +{ + beam_catches_end_staging(1); + export_end_staging(1); + module_end_staging(1); + erts_end_staging_ranges(1); + CIX_TRACE("end"); +} + +void erts_commit_staging_code_ix(void) +{ + ErtsCodeIndex ix; + /* We need to this lock as we are now making the staging export table active */ + export_staging_lock(); + ix = erts_staging_code_ix(); + erts_smp_atomic32_set_nob(&the_active_code_index, ix); + ix = (ix + 1) % ERTS_NUM_CODE_IX; + erts_smp_atomic32_set_nob(&the_staging_code_index, ix); + export_staging_unlock(); + CIX_TRACE("activate"); +} + +void erts_abort_staging_code_ix(void) +{ + beam_catches_end_staging(0); + export_end_staging(0); + module_end_staging(0); + erts_end_staging_ranges(0); + CIX_TRACE("abort"); +} + + +/* + * Calller _must_ yield if we return 0 + */ +int erts_try_seize_code_write_permission(Process* c_p) +{ + int success; +#ifdef ERTS_SMP + ASSERT(!erts_smp_thr_progress_is_blocking()); /* to avoid deadlock */ +#endif + + erts_smp_mtx_lock(&the_code_ix_queue_lock); + success = !the_code_ix_lock; + if (success) { + the_code_ix_lock = 1; +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_tsd_set(has_code_write_permission, (void *) 1); +#endif + } + else { /* Already locked */ + struct code_ix_queue_item* qitem; + qitem = erts_alloc(ERTS_ALC_T_CODE_IX_LOCK_Q, sizeof(*qitem)); + qitem->p = c_p; + erts_smp_proc_inc_refc(c_p); + qitem->next = the_code_ix_queue; + the_code_ix_queue = qitem; + erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); + } + erts_smp_mtx_unlock(&the_code_ix_queue_lock); + return success; +} + +void erts_release_code_write_permission(void) +{ + ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked()); + erts_smp_mtx_lock(&the_code_ix_queue_lock); + while (the_code_ix_queue != NULL) { /* unleash the entire herd */ + struct code_ix_queue_item* qitem = the_code_ix_queue; + erts_smp_proc_lock(qitem->p, ERTS_PROC_LOCK_STATUS); + if (!ERTS_PROC_IS_EXITING(qitem->p)) { + erts_resume(qitem->p, ERTS_PROC_LOCK_STATUS); + } + erts_smp_proc_unlock(qitem->p, ERTS_PROC_LOCK_STATUS); + the_code_ix_queue = qitem->next; + erts_smp_proc_dec_refc(qitem->p); + erts_free(ERTS_ALC_T_CODE_IX_LOCK_Q, qitem); + } + the_code_ix_lock = 0; +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_tsd_set(has_code_write_permission, (void *) 0); +#endif + erts_smp_mtx_unlock(&the_code_ix_queue_lock); +} + +#ifdef ERTS_ENABLE_LOCK_CHECK +int erts_is_code_ix_locked(void) +{ + return the_code_ix_lock && erts_tsd_get(has_code_write_permission); +} +#endif diff --git a/erts/emulator/beam/code_ix.h b/erts/emulator/beam/code_ix.h new file mode 100644 index 0000000000..6b2680044e --- /dev/null +++ b/erts/emulator/beam/code_ix.h @@ -0,0 +1,141 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* Description: + * This is the interface that facilitate changing the beam code + * (load,upgrade,delete) while allowing executing Erlang processes to + * access the code without any locks or other expensive memory barriers. + * + * The basic idea is to maintain several "logical copies" of the code. These + * copies are identified by a global 'code index', an integer of 0, 1 or 2. + * The code index is used as argument to code access structures like + * export, module, beam_catches, beam_ranges. + * + * The current 'active' code index is used to access the current running + * code. The 'staging' code index is used by the process that performs + * a code change operation. When a code change operation completes + * succesfully, the staging code index becomes the new active code index. + * + * The third code index is not explicitly used. It can be thought of as + * the "previous active" or the "next staging" index. It is needed to make + * sure that we do not reuse a code index for staging until we are sure + * that no executing BIFs are still referring it. + * We could get by with only two (0 and 1), but that would require that we + * must wait for all schedulers to re-schedule before each code change + * operation can start staging. + * + * Note that the 'code index' is very loosely coupled to the concept of + * 'current' and 'old' module versions. You can almost say that they are + * orthogonal to each other. Code index is an emulator global concept while + * 'current' and 'old' is specific for each module. + */ + +#ifndef __CODE_IX_H__ +#define __CODE_IX_H__ + +#ifndef __SYS_H__ +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif +# include "sys.h" +#endif +struct process; + + +#define ERTS_NUM_CODE_IX 3 +typedef unsigned ErtsCodeIndex; + + +/* Called once at emulator initialization. + */ +void erts_code_ix_init(void); + +/* Return active code index. + * Is guaranteed to be valid until the calling BIF returns. + * To get a consistent view of the code, only one call to erts_active_code_ix() + * should be made and the returned ix reused within the same BIF call. + */ +ERTS_GLB_INLINE +ErtsCodeIndex erts_active_code_ix(void); + +/* Return staging code ix. + * Only used by a process performing code loading/upgrading/deleting/purging. + * code_ix must be locked. + */ +ERTS_GLB_INLINE +ErtsCodeIndex erts_staging_code_ix(void); + +/* Try seize exclusive code write permission. Needed for code staging. + * Main process lock (only) must be held. + * System thread progress must not be blocked. + * Caller is suspended and *must* yield if 0 is returned. + */ +int erts_try_seize_code_write_permission(struct process*); + +/* Release code write permission. + * Will resume any suspended waiters. + */ +void erts_release_code_write_permission(void); + +/* Prepare the "staging area" to be a complete copy of the active code. + * Code write permission must have been seized. + * Must be followed by calls to either "end" and "commit" or "abort" before + * code write permission can be released. + */ +void erts_start_staging_code_ix(void); + +/* End the staging. + * Preceded by "start" and must be followed by "commit". + */ +void erts_end_staging_code_ix(void); + +/* Set staging code index as new active code index. + * Preceded by "end". + */ +void erts_commit_staging_code_ix(void); + +/* Abort the staging. + * Preceded by "start". + */ +void erts_abort_staging_code_ix(void); + +#ifdef ERTS_ENABLE_LOCK_CHECK +int erts_is_code_ix_locked(void); +#endif + + + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +extern erts_smp_atomic32_t the_active_code_index; +extern erts_smp_atomic32_t the_staging_code_index; + +ERTS_GLB_INLINE ErtsCodeIndex erts_active_code_ix(void) +{ + return erts_smp_atomic32_read_nob(&the_active_code_index); +} +ERTS_GLB_INLINE ErtsCodeIndex erts_staging_code_ix(void) +{ + return erts_smp_atomic32_read_nob(&the_staging_code_index); +} + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +#endif /* !__CODE_IX_H__ */ + diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c index d7345c2f54..36eda04de2 100644 --- a/erts/emulator/beam/copy.c +++ b/erts/emulator/beam/copy.c @@ -26,30 +26,13 @@ #include "global.h" #include "erl_process.h" #include "erl_gc.h" -#include "erl_nmgc.h" #include "big.h" #include "erl_binary.h" #include "erl_bits.h" #include "dtrace-wrapper.h" -#ifdef HYBRID -MA_STACK_DECLARE(src); -MA_STACK_DECLARE(dst); -MA_STACK_DECLARE(offset); -#endif - static void move_one_frag(Eterm** hpp, Eterm* src, Uint src_sz, ErlOffHeap*); -void -init_copy(void) -{ -#ifdef HYBRID - MA_STACK_ALLOC(src); - MA_STACK_ALLOC(dst); - MA_STACK_ALLOC(offset); -#endif -} - /* * Copy object "obj" to process p. */ @@ -432,12 +415,10 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) while (i--) { *htop++ = *objp++; } -#ifndef HYBRID /* FIND ME! */ funp = (ErlFunThing *) tp; funp->next = off_heap->first; off_heap->first = (struct erl_off_heap_header*) funp; erts_refc_inc(&funp->fe->refc, 2); -#endif *argp = make_fun_rel(tp, dst_base); } break; @@ -500,420 +481,6 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap) return res; } -#ifdef HYBRID - -#ifdef BM_MESSAGE_SIZES -# define BM_ADD(var,val) (var) += (val); -#else -# define BM_ADD(var,val) -#endif - -#ifdef DEBUG -# define CLEARMEM(PTR,SIZE) memset(PTR,0,SIZE*sizeof(Eterm)) -#else -# define CLEARMEM(PTR,SIZE) -#endif - -#ifdef INCREMENTAL -#define GlobalAlloc(p, need, hp) \ -do { \ - Uint n = (need); \ - BM_ADD(words_copied,n); \ - BM_SWAP_TIMER(copy,system); \ - /* If a new collection cycle is started during copy, the message * \ - * will end up in the old generation and all allocations * \ - * thereafter must go directly into the old generation. */ \ - if (alloc_old) { \ - erts_incremental_gc((p),n,&dest,1); \ - (hp) = erts_inc_alloc(n); \ - } else { \ - (hp) = IncAlloc((p),n,&dest,1); \ - if (ma_gc_flags & GC_CYCLE_START) { \ - alloc_old = 1; \ - global_htop = global_heap; \ - (hp) = erts_inc_alloc(n); \ - } \ - } \ - CLEARMEM((hp),(n)); \ - BM_SWAP_TIMER(system,copy); \ -} while(0) - -#else /* no INCREMELNTAL */ - -#define GlobalAlloc(p, need, hp) \ -do { \ - Uint n = (need); \ - total_need += n; \ - if (total_need >= global_heap_sz) \ - erl_exit(ERTS_ABORT_EXIT, "Copying a message (%d words) larger than the nursery simply won't work...\n", total_need); \ - if (global_hend - n < global_htop) { \ - BM_SWAP_TIMER(copy,system); \ - erts_global_garbage_collect((p),total_need,NULL,0); \ - BM_SWAP_TIMER(system,copy); \ - total_need = 0; \ - ma_src_top = 0; \ - ma_dst_top = 0; \ - ma_offset_top = 0; \ - goto copy_start; \ - } \ - (hp) = global_htop; \ - global_htop += n; \ - BM_ADD(words_copied,n); \ -} while(0) -#endif /* INCREMENTAL */ - -/* Copy a message to the message area. */ -Eterm copy_struct_lazy(Process *from, Eterm orig, Uint offs) -{ - Eterm obj; - Eterm dest; -#ifdef INCREMENTAL - int alloc_old = 0; -#else - int total_need = 0; -#endif - - VERBOSE(DEBUG_MESSAGES, - ("COPY START; %T is sending a message @ 0x%016x\n%T\n", - from->id, orig, orig)); - -#ifndef INCREMENTAL - copy_start: -#endif - MA_STACK_PUSH(src,orig); - MA_STACK_PUSH(dst,&dest); - MA_STACK_PUSH(offset,offs); - - while (ma_src_top > 0) { - obj = MA_STACK_POP(src); - - /* copy_struct_lazy should never be called with something that - * do not need to be copied. Within the loop, nothing that do - * not need copying should be placed in the src-stack. - */ - ASSERT(!NO_COPY(obj)); - - switch (primary_tag(obj)) { - case TAG_PRIMARY_LIST: { - Eterm *hp; - Eterm *objp; - - GlobalAlloc(from,2,hp); - objp = list_val(obj); - - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_list(hp)); - MA_STACK_POP(dst); - - /* TODO: Byt ordningen nedan så att CDR pushas först. */ - - if (NO_COPY(*objp)) { - hp[0] = *objp; -#ifdef INCREMENTAL - if (ptr_within(ptr_val(*objp),inc_fromspc,inc_fromend)) - INC_STORE(gray,hp,2); -#endif - } else { - MA_STACK_PUSH(src,*objp); - MA_STACK_PUSH(dst,hp); - MA_STACK_PUSH(offset,0); - } - - objp++; - - if (NO_COPY(*objp)) { - hp[1] = *objp; -#ifdef INCREMENTAL - if (ptr_within(ptr_val(*objp),inc_fromspc,inc_fromend)) - INC_STORE(gray,hp,2); -#endif - } - else { - MA_STACK_PUSH(src,*objp); - MA_STACK_PUSH(dst,hp); - MA_STACK_PUSH(offset,1); - } - continue; - } - - case TAG_PRIMARY_BOXED: { - Eterm *objp = boxed_val(obj); - - switch (*objp & _TAG_HEADER_MASK) { - case ARITYVAL_SUBTAG: { - Uint ari = arityval(*objp); - Uint i; - Eterm *hp; - GlobalAlloc(from,ari + 1,hp); - /* A GC above might invalidate the value of objp */ - objp = boxed_val(obj); - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_tuple(hp)); - MA_STACK_POP(dst); - *hp = *objp++; - for (i = 1; i <= ari; i++) { - switch (primary_tag(*objp)) { - case TAG_PRIMARY_LIST: - case TAG_PRIMARY_BOXED: - if (NO_COPY(*objp)) { - hp[i] = *objp; -#ifdef INCREMENTAL - if (ptr_within(ptr_val(*objp), - inc_fromspc,inc_fromend)) - INC_STORE(gray,hp,BOXED_NEED(hp,*hp)); -#endif - objp++; - } else { - MA_STACK_PUSH(src,*objp++); - MA_STACK_PUSH(dst,hp); - MA_STACK_PUSH(offset,i); - } - break; - default: - hp[i] = *objp++; - } - } - continue; - } - - case REFC_BINARY_SUBTAG: { - ProcBin *pb; - Uint i = thing_arityval(*objp) + 1; - Eterm *hp; - GlobalAlloc(from,i,hp); - /* A GC above might invalidate the value of objp */ - objp = boxed_val(obj); - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_binary(hp)); - MA_STACK_POP(dst); - pb = (ProcBin*) hp; - while (i--) { - *hp++ = *objp++; - } - erts_refc_inc(&pb->val->refc, 2); - pb->next = erts_global_offheap.first; - erts_global_offheap.first = pb; - OH_OVERHEAD(off_heap, pb->size / sizeof(Eterm)); - continue; - } - - case FUN_SUBTAG: { - ErlFunThing *funp = (ErlFunThing*) objp; - Uint i = thing_arityval(*objp) + 1; - Uint j = i + 1 + funp->num_free; - Uint k = i; - Eterm *hp, *hp_start; - GlobalAlloc(from,j,hp); - /* A GC above might invalidate the value of objp */ - objp = boxed_val(obj); - hp_start = hp; - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_fun(hp)); - MA_STACK_POP(dst); - funp = (ErlFunThing*) hp; - while (i--) { - *hp++ = *objp++; - } -#ifndef HYBRID /* FIND ME! */ - funp->next = erts_global_offheap.first; - erts_global_offheap.first = funp; - erts_refc_inc(&funp->fe->refc, 2); -#endif - for (i = k; i < j; i++) { - switch (primary_tag(*objp)) { - case TAG_PRIMARY_LIST: - case TAG_PRIMARY_BOXED: - if (NO_COPY(*objp)) { -#ifdef INCREMENTAL - if (ptr_within(ptr_val(*objp), - inc_fromspc,inc_fromend)) - INC_STORE(gray,hp,BOXED_NEED(hp,*hp)); -#endif - *hp++ = *objp++; - } else { - MA_STACK_PUSH(src,*objp++); - MA_STACK_PUSH(dst,hp_start); - MA_STACK_PUSH(offset,i); - hp++; - } - break; - default: - *hp++ = *objp++; - } - } - continue; - } - - case EXTERNAL_PID_SUBTAG: - case EXTERNAL_PORT_SUBTAG: - case EXTERNAL_REF_SUBTAG: { - ExternalThing *etp; - Uint i = thing_arityval(*objp) + 1; - Eterm *hp; - GlobalAlloc(from,i,hp); - /* A GC above might invalidate the value of objp */ - objp = boxed_val(obj); - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_external(hp)); - MA_STACK_POP(dst); - etp = (ExternalThing*) hp; - while (i--) { - *hp++ = *objp++; - } - - etp->next = erts_global_offheap.first; - erts_global_offheap.first = etp; - erts_refc_inc(&etp->node->refc, 2); - continue; - } - - case SUB_BINARY_SUBTAG: { - ErlSubBin *sb = (ErlSubBin *) objp; - Eterm *hp; - Eterm res_binary; - Eterm real_bin = sb->orig; - Uint bit_offset = sb->bitoffs; - Uint bit_size = sb -> bitsize; - Uint sub_offset = sb->offs; - size_t size = sb->size; - Uint extra_bytes; - Uint real_size; - Uint sub_binary_heapneed; - if ((bit_size + bit_offset) > 8) { - extra_bytes = 2; - sub_binary_heapneed = ERL_SUB_BIN_SIZE; - } else if ((bit_size + bit_offset) > 0) { - extra_bytes = 1; - sub_binary_heapneed = ERL_SUB_BIN_SIZE; - } else { - extra_bytes = 0; - sub_binary_heapneed = 0; - } - - real_size = size+extra_bytes; - objp = binary_val(real_bin); - if (thing_subtag(*objp) == HEAP_BINARY_SUBTAG) { - ErlHeapBin *from_bin; - ErlHeapBin *to_bin; - Uint i = heap_bin_size(real_size); - GlobalAlloc(from,i+sub_binary_heapneed,hp); - from_bin = (ErlHeapBin *) objp; - to_bin = (ErlHeapBin *) hp; - to_bin->thing_word = header_heap_bin(real_size); - to_bin->size = real_size; - sys_memcpy(to_bin->data, ((byte *)from_bin->data) + - sub_offset, real_size); - res_binary = make_binary(to_bin); - hp += i; - } else { - ProcBin *from_bin; - ProcBin *to_bin; - - ASSERT(thing_subtag(*objp) == REFC_BINARY_SUBTAG); - from_bin = (ProcBin *) objp; - erts_refc_inc(&from_bin->val->refc, 2); - GlobalAlloc(from,PROC_BIN_SIZE+sub_binary_heapneed,hp); - to_bin = (ProcBin *) hp; - to_bin->thing_word = HEADER_PROC_BIN; - to_bin->size = real_size; - to_bin->val = from_bin->val; - to_bin->bytes = from_bin->bytes + sub_offset; - to_bin->next = erts_global_offheap.first; - erts_global_offheap.first = to_bin; - OH_OVERHEAD(&erts_global_offheap, to_bin->size / sizeof(Eterm)); - res_binary=make_binary(to_bin); - hp += PROC_BIN_SIZE; - } - if (extra_bytes != 0) { - ErlSubBin* res; - res = (ErlSubBin *) hp; - res->thing_word = HEADER_SUB_BIN; - res->size = size; - res->bitsize = bit_size; - res->bitoffs = bit_offset; - res->offs = 0; - res->is_writable = 0; - res->orig = res_binary; - res_binary = make_binary(hp); - } - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),res_binary); - MA_STACK_POP(dst); - continue; - } - - case BIN_MATCHSTATE_SUBTAG: - erl_exit(ERTS_ABORT_EXIT, - "copy_struct_lazy: matchstate term not allowed"); - - default: { - Uint size = thing_arityval(*objp) + 1; - Eterm *hp; - GlobalAlloc(from,size,hp); - /* A GC above might invalidate the value of objp */ - objp = boxed_val(obj); - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_boxed(hp)); - MA_STACK_POP(dst); - while (size--) { - *hp++ = *objp++; - } - continue; - } - } - continue; - } - - case TAG_PRIMARY_HEADER: - ASSERT((obj & _TAG_HEADER_MASK) == ARITYVAL_SUBTAG); - { - Eterm *objp = &obj; - Uint ari = arityval(obj); - Uint i; - Eterm *hp; - GlobalAlloc(from,ari + 1,hp); - MA_STACK_UPDATE(dst,MA_STACK_POP(offset),make_tuple(hp)); - MA_STACK_POP(dst); - *hp = *objp++; - for (i = 1; i <= ari; i++) { - switch (primary_tag(*objp)) { - case TAG_PRIMARY_LIST: - case TAG_PRIMARY_BOXED: - if (NO_COPY(*objp)) { -#ifdef INCREMENTAL - if (ptr_within(ptr_val(*objp),inc_fromspc,inc_fromend)) - INC_STORE(gray,hp,ari + 1); -#endif - hp[i] = *objp++; - } else { - MA_STACK_PUSH(src,*objp++); - MA_STACK_PUSH(dst,hp); - MA_STACK_PUSH(offset,i); - } - break; - default: - hp[i] = *objp++; - } - } - continue; - } - - default: - erl_exit(ERTS_ABORT_EXIT, - "%s, line %d: Internal error in copy_struct_lazy: 0x%08x\n", - __FILE__, __LINE__,obj); - } - } - - VERBOSE(DEBUG_MESSAGES, - ("Copy allocated @ 0x%08lx:\n%T\n", - (unsigned long)ptr_val(dest),dest)); - - ma_gc_flags &= ~GC_CYCLE_START; - - ASSERT(eq(orig, dest)); - ASSERT(ma_src_top == 0); - ASSERT(ma_dst_top == 0); - ASSERT(ma_offset_top == 0); - return dest; -} - -#undef NO_COPY -#endif /* HYBRID */ - /* * Copy a term that is guaranteed to be contained in a single * heap block. The heap block is copied word by word, and any diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index a7f11740f5..5db68f6d45 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1578,11 +1578,9 @@ int erts_net_message(Port *prt, } erts_cleanup_offheap(&off_heap); -#ifndef HYBRID /* FIND ME! */ if (ctl != ctl_default) { erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl); } -#endif UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE); ERTS_SMP_CHK_NO_PROC_LOCKS; return 0; @@ -1595,11 +1593,9 @@ int erts_net_message(Port *prt, data_error: PURIFY_MSG("data error"); erts_cleanup_offheap(&off_heap); -#ifndef HYBRID /* FIND ME! */ if (ctl != ctl_default) { erts_free(ERTS_ALC_T_DCTRL_BUF, (void *) ctl); } -#endif UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE); erts_do_exit_port(prt, dep->cid, am_killed); ERTS_SMP_CHK_NO_PROC_LOCKS; @@ -1973,6 +1969,7 @@ erts_dist_command(Port *prt, int reds_limit) bw(foq.first->extp, size); #endif reds += ERTS_PORT_REDS_DIST_CMD_DATA(size); + erts_smp_atomic_add_nob(&erts_bytes_out, size); fob = foq.first; obufsize += size_obuf(fob); foq.first = foq.first->next; @@ -2056,6 +2053,7 @@ erts_dist_command(Port *prt, int reds_limit) bw(oq.first->extp, size); #endif reds += ERTS_PORT_REDS_DIST_CMD_DATA(size); + erts_smp_atomic_add_nob(&erts_bytes_out, size); fob = oq.first; obufsize += size_obuf(fob); oq.first = oq.first->next; @@ -2451,15 +2449,15 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2) goto error; /* Check that all trap functions are defined !! */ - if (dsend2_trap->address == NULL || - dsend3_trap->address == NULL || + if (dsend2_trap->addressv[0] == NULL || + dsend3_trap->addressv[0] == NULL || /* dsend_nosuspend_trap->address == NULL ||*/ - dlink_trap->address == NULL || - dunlink_trap->address == NULL || - dmonitor_node_trap->address == NULL || - dgroup_leader_trap->address == NULL || - dmonitor_p_trap->address == NULL || - dexit_trap->address == NULL) { + dlink_trap->addressv[0] == NULL || + dunlink_trap->addressv[0] == NULL || + dmonitor_node_trap->addressv[0] == NULL || + dgroup_leader_trap->addressv[0] == NULL || + dmonitor_p_trap->addressv[0] == NULL || + dexit_trap->addressv[0] == NULL) { goto error; } diff --git a/erts/emulator/beam/dtrace-wrapper.h b/erts/emulator/beam/dtrace-wrapper.h index 1aeb7f9221..6ec0c91e21 100644 --- a/erts/emulator/beam/dtrace-wrapper.h +++ b/erts/emulator/beam/dtrace-wrapper.h @@ -42,6 +42,8 @@ #define DTRACE_CHARBUF(name, size) \ char name##_BUFFER[size], *name = name##_BUFFER +#define DTRACE_CHARBUF_NAME(name) name##_BUFFER + #if defined(USE_DYNAMIC_TRACE) && defined(USE_VM_PROBES) #include "erlang_dtrace.h" diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 8130d5c576..ba73ca6da7 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -2138,9 +2138,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) tmp = alcu_size(ERTS_ALC_A_EHEAP, NULL, 0); } tmp += erts_max_processes*sizeof(Process*); -#ifdef HYBRID - tmp += erts_max_processes*sizeof(Process*); -#endif tmp += erts_bif_timer_memory_size(); tmp += erts_tot_link_lh_size(); @@ -2185,9 +2182,9 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) if (want.code) { size.code = module_table_sz(); size.code += export_table_sz(); - size.code += export_list_size() * sizeof(Export); + size.code += export_entries_sz(); size.code += erts_fun_table_sz(); - size.code += allocated_modules*sizeof(Range); + size.code += erts_ranges_sz(); size.code += erts_total_code_size; } @@ -2303,11 +2300,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc) values[i].name = "static"; values[i].ui[0] = erts_max_ports*sizeof(Port) /* Port table */ - + erts_timer_wheel_memory_size() /* Timer wheel */ -#ifdef SYS_TMP_BUF_SIZE - + SYS_TMP_BUF_SIZE /* tmp_buf in sys on vxworks & ose */ -#endif - ; + + erts_timer_wheel_memory_size(); /* Timer wheel */ i++; erts_atom_get_text_space_sizes(&reserved_atom_space, &atom_space); @@ -2335,7 +2328,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc) values[i].arity = 2; values[i].name = "export_list"; - values[i].ui[0] = export_list_size() * sizeof(Export); + values[i].ui[0] = export_entries_sz(); i++; values[i].arity = 2; @@ -2350,7 +2343,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc) values[i].arity = 2; values[i].name = "module_refs"; - values[i].ui[0] = allocated_modules*sizeof(Range); + values[i].ui[0] = erts_ranges_sz(); i++; values[i].arity = 2; diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index d1e3b4b0ef..0a4407f009 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -164,6 +164,7 @@ type MSG_REF FIXED_SIZE PROCESSES msg_ref type MSG_ROOTS TEMPORARY PROCESSES msg_roots type ROOTSET TEMPORARY PROCESSES root_set type LOADER_TMP TEMPORARY CODE loader_tmp +type PREPARED_CODE SHORT_LIVED CODE prepared_code type BIF_TIMER_TABLE LONG_LIVED SYSTEM bif_timer_table type SL_BIF_TIMER SHORT_LIVED PROCESSES bif_timer_sl type LL_BIF_TIMER STANDARD PROCESSES bif_timer_ll @@ -263,6 +264,7 @@ type ZLIB STANDARD SYSTEM zlib type CPU_GRPS_MAP LONG_LIVED SYSTEM cpu_groups_map type AUX_WORK_TMO LONG_LIVED SYSTEM aux_work_timeouts type MISC_AUX_WORK_Q LONG_LIVED SYSTEM misc_aux_work_q +type CODE_IX_LOCK_Q SHORT_LIVED SYSTEM code_ix_lock_q type PROC_INTERVAL LONG_LIVED SYSTEM process_interval +if threads_no_smp @@ -292,7 +294,6 @@ type PORT_LOCK STANDARD SYSTEM port_lock type DRIVER_LOCK STANDARD SYSTEM driver_lock type XPORTS_LIST SHORT_LIVED SYSTEM extra_port_list type PROC_LCK_WTR LONG_LIVED SYSTEM proc_lock_waiter -type PROC_LCK_QS LONG_LIVED SYSTEM proc_lock_queues type RUNQ_BLNS LONG_LIVED SYSTEM run_queue_balancing type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data @@ -318,19 +319,6 @@ type ACTIVE_PROCS STANDARD PROCESSES active_procs +endif -+if hybrid - -type ACTIVE_PROCS STANDARD PROCESSES active_procs - -# Used for all memory involved in incremental gc of the message area -# that is, young (x2) and old generation, forwarding pointers and blackmap -type MESSAGE_AREA LONG_LIVED PROCESSES message_area - -# Used in MA_STACK (global.h) and INC_STORAGE (erl_nmgc.h) -type OBJECT_STACK STANDARD PROCESSES object_stack - -+endif - +if smp type SL_PTIMER SHORT_LIVED SYSTEM ptimer_sl type LL_PTIMER STANDARD SYSTEM ptimer_ll @@ -431,28 +419,4 @@ type CON_VPRINTF_BUF TEMPORARY SYSTEM con_vprintf_buf +endif -+if vxworks - -type SYS_TMP_BUF LONG_LIVED SYSTEM sys_tmp_buf -type PEND_DATA SYSTEM SYSTEM pending_data -type FD_TAB LONG_LIVED SYSTEM fd_tab -type FD_ENTRY_BUF SYSTEM SYSTEM fd_entry_buf - -+endif - -+if ose - -type SYS_TMP_BUF LONG_LIVED SYSTEM sys_tmp_buf -type PUTENV_STR SYSTEM SYSTEM putenv_string -type GETENV_STR SYSTEM SYSTEM getenv_string -type GETENV_STATE SYSTEM SYSTEM getenv_state -type SIG_ENTRY SYSTEM SYSTEM sig_entry -type DRIVER_DATA SYSTEM SYSTEM driver_data -type PGM_TAB SYSTEM SYSTEM pgm_tab -type PGM_ENTRY SYSTEM SYSTEM pgm_entry -type PRT_TAB SYSTEM SYSTEM prt_tab -type PRT_ENTRY SYSTEM SYSTEM prt_entry - -+endif - # ---------------------------------------------------------------------------- diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 62225d3572..97ba306a79 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -829,49 +829,83 @@ init_dd_queue(ErtsAllctrDDQueue_t *ddq) ddq->head.used_marker = 1; } -static ERTS_INLINE erts_aint_t -ddq_managed_thread_enqueue(ErtsAllctrDDQueue_t *ddq, void *ptr) +static ERTS_INLINE int +ddq_managed_thread_enqueue(ErtsAllctrDDQueue_t *ddq, void *ptr, int cinit) { - erts_aint_t first_ilast, ilast, itmp; - ErtsAllctrDDBlock_t *this = ptr; + erts_aint_t itmp; + ErtsAllctrDDBlock_t *enq, *this = ptr; erts_atomic_init_nob(&this->atmc_next, ERTS_AINT_NULL); - /* Enqueue at end of list... */ - first_ilast = ilast = erts_atomic_read_nob(&ddq->tail.data.last); - while (1) { - ErtsAllctrDDBlock_t *last = (ErtsAllctrDDBlock_t *) ilast; - itmp = erts_atomic_cmpxchg_mb(&last->atmc_next, - (erts_aint_t) this, - ERTS_AINT_NULL); - if (itmp == ERTS_AINT_NULL) - break; - ilast = itmp; + enq = (ErtsAllctrDDBlock_t *) erts_atomic_read_nob(&ddq->tail.data.last); + itmp = erts_atomic_cmpxchg_relb(&enq->atmc_next, + (erts_aint_t) this, + ERTS_AINT_NULL); + if (itmp == ERTS_AINT_NULL) { + /* We are required to move last pointer */ +#ifdef DEBUG + ASSERT(ERTS_AINT_NULL == erts_atomic_read_nob(&this->atmc_next)); + ASSERT(((erts_aint_t) enq) + == erts_atomic_xchg_relb(&ddq->tail.data.last, + (erts_aint_t) this)); +#else + erts_atomic_set_relb(&ddq->tail.data.last, (erts_aint_t) this); +#endif + return 1; } + else { + /* + * We *need* to insert element somewhere in between the + * last element we read earlier and the actual last element. + */ + int i = cinit; - /* Move last pointer forward... */ - while (1) { - if (erts_atomic_read_rb(&this->atmc_next) != ERTS_AINT_NULL) { - ilast = erts_atomic_read_rb(&ddq->tail.data.last); - if (first_ilast != ilast) { - /* Someone else will move it forward */ - return ilast; + while (1) { + erts_aint_t itmp2; + erts_atomic_set_nob(&this->atmc_next, itmp); + itmp2 = erts_atomic_cmpxchg_relb(&enq->atmc_next, + (erts_aint_t) this, + itmp); + if (itmp == itmp2) + return 0; /* inserted this */ + if ((i & 1) == 0) + itmp = itmp2; + else { + enq = (ErtsAllctrDDBlock_t *) itmp2; + itmp = erts_atomic_read_acqb(&enq->atmc_next); + ASSERT(itmp != ERTS_AINT_NULL); } + i++; } - itmp = erts_atomic_cmpxchg_mb(&ddq->tail.data.last, - (erts_aint_t) this, - ilast); - if (ilast == itmp) - return (erts_aint_t) this; - ilast = itmp; } } +static ERTS_INLINE erts_aint_t +check_insert_marker(ErtsAllctrDDQueue_t *ddq, erts_aint_t ilast) +{ + if (!ddq->head.used_marker + && ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) { + erts_aint_t itmp; + ErtsAllctrDDBlock_t *last = (ErtsAllctrDDBlock_t *) ilast; + + erts_atomic_init_nob(&ddq->tail.data.marker.atmc_next, ERTS_AINT_NULL); + itmp = erts_atomic_cmpxchg_relb(&last->atmc_next, + (erts_aint_t) &ddq->tail.data.marker, + ERTS_AINT_NULL); + if (itmp == ERTS_AINT_NULL) { + ilast = (erts_aint_t) &ddq->tail.data.marker; + ddq->head.used_marker = !0; + erts_atomic_set_relb(&ddq->tail.data.last, ilast); + } + } + return ilast; +} + static ERTS_INLINE int -ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr) +ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr, int cinit) { - erts_aint_t ilast; + int last_elem; int um_refc_ix = 0; int managed_thread = erts_thr_progress_is_managed_thread(); if (!managed_thread) { @@ -887,11 +921,11 @@ ddq_enqueue(ErtsAlcType_t type, ErtsAllctrDDQueue_t *ddq, void *ptr) } } - ilast = ddq_managed_thread_enqueue(ddq, ptr); + last_elem = ddq_managed_thread_enqueue(ddq, ptr, cinit); if (!managed_thread) erts_atomic_dec_relb(&ddq->tail.data.um_refc[um_refc_ix]); - return ilast == (erts_aint_t) ptr; + return last_elem; } static ERTS_INLINE void * @@ -937,22 +971,18 @@ ddq_check_incoming(ErtsAllctrDDQueue_t *ddq) int um_refc_ix; ddq->head.next.thr_progress_reached = 1; um_refc_ix = ddq->head.next.um_refc_ix; - if (erts_atomic_read_acqb(&ddq->tail.data.um_refc[um_refc_ix]) == 0) { + if (erts_atomic_read_nob(&ddq->tail.data.um_refc[um_refc_ix]) == 0) { /* Move unreferenced end pointer forward... */ + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); + ddq->head.unref_end = ddq->head.next.unref_end; - if (!ddq->head.used_marker - && ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) { - ddq->head.used_marker = 1; - ilast = ddq_managed_thread_enqueue(ddq, &ddq->tail.data.marker); - } + ilast = check_insert_marker(ddq, ilast); - if (ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) - ERTS_THR_MEMORY_BARRIER; - else { + if (ddq->head.unref_end != (ErtsAllctrDDBlock_t *) ilast) { ddq->head.next.unref_end = (ErtsAllctrDDBlock_t *) ilast; - ddq->head.next.thr_progress = erts_thr_progress_later(); + ddq->head.next.thr_progress = erts_thr_progress_later(NULL); erts_atomic32_set_relb(&ddq->tail.data.um_refc_ix, um_refc_ix); ddq->head.next.um_refc_ix = um_refc_ix == 0 ? 1 : 0; @@ -1095,12 +1125,15 @@ handle_delayed_dealloc(Allctr_t *allctr, } static ERTS_INLINE void -enqueue_dealloc_other_instance(ErtsAlcType_t type, Allctr_t *allctr, void *ptr) +enqueue_dealloc_other_instance(ErtsAlcType_t type, + Allctr_t *allctr, + void *ptr, + int cinit) { if (allctr->fix) ((UWord *) ptr)[ERTS_ALCU_DD_FIX_TYPE_OFFS] = (UWord) type; - if (ddq_enqueue(type, &allctr->dd.q, ptr)) + if (ddq_enqueue(type, &allctr->dd.q, ptr, cinit)) erts_alloc_notify_delayed_dealloc(allctr->ix); } @@ -3616,7 +3649,11 @@ erts_alcu_free_thr_pref(ErtsAlcType_t type, void *extra, void *p) get_pref_allctr(extra, &pref_allctr); ptr = get_used_allctr(extra, p, &used_allctr, NULL); if (pref_allctr != used_allctr) - enqueue_dealloc_other_instance(type, used_allctr, ptr); + enqueue_dealloc_other_instance(type, + used_allctr, + ptr, + (used_allctr->dd.ix + - pref_allctr->dd.ix)); else { if (used_allctr->thread_safe) erts_mtx_lock(&used_allctr->mutex); @@ -3991,7 +4028,11 @@ realloc_thr_pref(ErtsAlcType_t type, void *extra, void *p, Uint size, sys_memcpy(res, p, cpy_size); if (!force_move || used_allctr != pref_allctr) - enqueue_dealloc_other_instance(type, used_allctr, ptr); + enqueue_dealloc_other_instance(type, + used_allctr, + ptr, + (used_allctr->dd.ix + - pref_allctr->dd.ix)); else { do_erts_alcu_free(type, used_allctr, ptr); ASSERT(pref_allctr == used_allctr); @@ -4182,6 +4223,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) allctr->dd.use = 1; init_dd_queue(&allctr->dd.q); + allctr->dd.ix = init->ix; } else #endif diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index f321ed21aa..d85c6feba7 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -122,6 +122,8 @@ typedef struct { #endif } ErtsAsyncData; +#if defined(USE_THREADS) && defined(USE_VM_PROBES) + /* * Some compilers, e.g. GCC 4.2.1 and -O3, will optimize away DTrace * calls if they're the last thing in the function. :-( @@ -129,6 +131,7 @@ typedef struct { * https://github.com/memcached/memcached/commit/6298b3978687530bc9d219b6ac707a1b681b2a46 */ static unsigned gcc_optimizer_hack = 0; +#endif int erts_async_max_threads; /* Initialized by erl_init.c */ int erts_async_thread_suggested_stack_size; /* Initialized by erl_init.c */ @@ -253,7 +256,9 @@ erts_get_async_ready_queue(Uint sched_id) static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q) { +#ifdef USE_VM_PROBES int len; +#endif if (is_internal_port(a->port)) { #if ERTS_USE_ASYNC_READY_Q @@ -279,8 +284,8 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q) len = -1; DTRACE2(aio_pool_add, port_str, len); } -#endif gcc_optimizer_hack++; +#endif } static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, @@ -291,7 +296,9 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, int saved_fin_deq = 0; ErtsThrQFinDeQ_t fin_deq; #endif +#ifdef USE_VM_PROBES int len; +#endif while (1) { ErtsAsync *a = (ErtsAsync *) erts_thr_q_dequeue(q); diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index cc4f2be8eb..474151d454 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -72,52 +72,29 @@ binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3); void erts_init_bif_binary(void) { - sys_memset((void *) &binary_match_trap_export, 0, sizeof(Export)); - binary_match_trap_export.address = &binary_match_trap_export.code[3]; - binary_match_trap_export.code[0] = am_erlang; - binary_match_trap_export.code[1] = am_binary_match_trap; - binary_match_trap_export.code[2] = 3; - binary_match_trap_export.code[3] = (BeamInstr) em_apply_bif; - binary_match_trap_export.code[4] = (BeamInstr) &binary_match_trap; - - sys_memset((void *) &binary_matches_trap_export, 0, sizeof(Export)); - binary_matches_trap_export.address = &binary_matches_trap_export.code[3]; - binary_matches_trap_export.code[0] = am_erlang; - binary_matches_trap_export.code[1] = am_binary_matches_trap; - binary_matches_trap_export.code[2] = 3; - binary_matches_trap_export.code[3] = (BeamInstr) em_apply_bif; - binary_matches_trap_export.code[4] = (BeamInstr) &binary_matches_trap; - - sys_memset((void *) &binary_longest_prefix_trap_export, 0, sizeof(Export)); - binary_longest_prefix_trap_export.address = &binary_longest_prefix_trap_export.code[3]; - binary_longest_prefix_trap_export.code[0] = am_erlang; - binary_longest_prefix_trap_export.code[1] = am_binary_longest_prefix_trap; - binary_longest_prefix_trap_export.code[2] = 3; - binary_longest_prefix_trap_export.code[3] = (BeamInstr) em_apply_bif; - binary_longest_prefix_trap_export.code[4] = (BeamInstr) &binary_longest_prefix_trap; - - sys_memset((void *) &binary_longest_suffix_trap_export, 0, sizeof(Export)); - binary_longest_suffix_trap_export.address = &binary_longest_suffix_trap_export.code[3]; - binary_longest_suffix_trap_export.code[0] = am_erlang; - binary_longest_suffix_trap_export.code[1] = am_binary_longest_suffix_trap; - binary_longest_suffix_trap_export.code[2] = 3; - binary_longest_suffix_trap_export.code[3] = (BeamInstr) em_apply_bif; - binary_longest_suffix_trap_export.code[4] = (BeamInstr) &binary_longest_suffix_trap; - - sys_memset((void *) &binary_bin_to_list_trap_export, 0, sizeof(Export)); - binary_bin_to_list_trap_export.address = &binary_bin_to_list_trap_export.code[3]; - binary_bin_to_list_trap_export.code[0] = am_erlang; - binary_bin_to_list_trap_export.code[1] = am_binary_bin_to_list_trap; - binary_bin_to_list_trap_export.code[2] = 3; - binary_bin_to_list_trap_export.code[3] = (BeamInstr) em_apply_bif; - binary_bin_to_list_trap_export.code[4] = (BeamInstr) &binary_bin_to_list_trap; - sys_memset((void *) &binary_copy_trap_export, 0, sizeof(Export)); - binary_copy_trap_export.address = &binary_copy_trap_export.code[3]; - binary_copy_trap_export.code[0] = am_erlang; - binary_copy_trap_export.code[1] = am_binary_copy_trap; - binary_copy_trap_export.code[2] = 2; - binary_copy_trap_export.code[3] = (BeamInstr) em_apply_bif; - binary_copy_trap_export.code[4] = (BeamInstr) &binary_copy_trap; + erts_init_trap_export(&binary_match_trap_export, + am_erlang, am_binary_match_trap, 3, + &binary_match_trap); + + erts_init_trap_export(&binary_matches_trap_export, + am_erlang, am_binary_matches_trap, 3, + &binary_matches_trap); + + erts_init_trap_export(&binary_longest_prefix_trap_export, + am_erlang, am_binary_longest_prefix_trap, 3, + &binary_longest_prefix_trap); + + erts_init_trap_export(&binary_longest_suffix_trap_export, + am_erlang, am_binary_longest_suffix_trap, 3, + &binary_longest_suffix_trap); + + erts_init_trap_export(&binary_bin_to_list_trap_export, + am_erlang, am_binary_bin_to_list_trap, 3, + &binary_bin_to_list_trap); + + erts_init_trap_export(&binary_copy_trap_export, + am_erlang, am_binary_copy_trap, 2, + &binary_copy_trap); max_loop_limit = 0; return; diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c index 06b7ffdf32..ff5ce3cc7a 100644 --- a/erts/emulator/beam/erl_bif_chksum.c +++ b/erts/emulator/beam/erl_bif_chksum.c @@ -42,16 +42,9 @@ static Export chksum_md5_2_exp; void erts_init_bif_chksum(void) { /* Non visual BIF to trap to. */ - memset(&chksum_md5_2_exp, 0, sizeof(Export)); - chksum_md5_2_exp.address = - &chksum_md5_2_exp.code[3]; - chksum_md5_2_exp.code[0] = am_erlang; - chksum_md5_2_exp.code[1] = am_atom_put("md5_trap",8); - chksum_md5_2_exp.code[2] = 2; - chksum_md5_2_exp.code[3] = - (BeamInstr) em_apply_bif; - chksum_md5_2_exp.code[4] = - (BeamInstr) &md5_2; + erts_init_trap_export(&chksum_md5_2_exp, + am_erlang, am_atom_put("md5_trap",8), 2, + &md5_2); } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 90ddaa4fac..e2f7c8673f 100644..100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -25,7 +25,6 @@ #include "erl_vm.h" #include "global.h" #include "erl_process.h" -#include "erl_nmgc.h" #include "error.h" #include "erl_driver.h" #include "bif.h" @@ -89,12 +88,6 @@ static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE #ifdef ERTS_ENABLE_KERNEL_POLL " [kernel-poll:%s]" #endif -#ifdef HYBRID - " [hybrid heap]" -#endif -#ifdef INCREMENTAL - " [incremental GC]" -#endif #ifdef ET_DEBUG #if ET_DEBUG " [type-assertions]" @@ -576,9 +569,6 @@ static Eterm pi_args[] = { am_min_bin_vheap_size, am_current_location, am_current_stacktrace, -#ifdef HYBRID - am_message_binary -#endif }; #define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(Eterm))) @@ -626,9 +616,6 @@ pi_arg2ix(Eterm arg) case am_min_bin_vheap_size: return 28; case am_current_location: return 29; case am_current_stacktrace: return 30; -#ifdef HYBRID - case am_message_binary: return 31; -#endif default: return -1; } } @@ -1081,12 +1068,8 @@ process_info_aux(Process *BIF_P, if (rp != BIF_P) { Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp); if (is_value(msg)) { - mq[i].copy_struct_size = (is_immed(msg) -#ifdef HYBRID - || NO_COPY(msg) -#endif - ? 0 - : size_object(msg)); + mq[i].copy_struct_size = (is_immed(msg)? 0 : + size_object(msg)); } else if (mq[i].msgp->data.attached) { mq[i].copy_struct_size @@ -1528,16 +1511,6 @@ process_info_aux(Process *BIF_P, break; } -#ifdef HYBRID - case am_message_binary: { - Uint sz = 3; - (void) bld_bin_list(NULL, &sz, erts_global_offheap.mso); - hp = HAlloc(BIF_P, sz); - res = bld_bin_list(&hp, NULL, erts_global_offheap.mso); - break; - } -#endif - case am_sequential_trace_token: res = copy_object(rp->seq_trace_token, BIF_P); hp = HAlloc(BIF_P, 3); @@ -2356,36 +2329,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) #endif } else if (BIF_ARG_1 == am_heap_sizes) { return erts_heap_sizes(BIF_P); - } else if (BIF_ARG_1 == am_global_heaps_size) { -#ifdef HYBRID - Uint hsz = 0; - Uint sz = 0; - - sz += global_heap_sz; -#ifdef INCREMENTAL - /* The size of the old generation is a bit hard to define here... - * The amount of live data in the last collection perhaps..? */ - sz = 0; -#else - if (global_old_hend && global_old_heap) - sz += global_old_hend - global_old_heap; -#endif - - sz *= sizeof(Eterm); - - (void) erts_bld_uint(NULL, &hsz, sz); - hp = hsz ? HAlloc(BIF_P, hsz) : NULL; - res = erts_bld_uint(&hp, NULL, sz); -#else - res = make_small(0); -#endif - return res; } else if (BIF_ARG_1 == am_heap_type) { -#if defined(HYBRID) - return am_hybrid; -#else return am_private; -#endif } else if (ERTS_IS_ATOM_STR("cpu_topology", BIF_ARG_1)) { res = erts_get_cpu_topology_term(BIF_P, am_used); BIF_TRAP1(erts_format_cpu_topology_trap, BIF_P, res); @@ -2767,7 +2712,8 @@ port_info_1(BIF_ALIST_1) am_id, am_connected, am_input, - am_output + am_output, + am_os_pid }; Eterm items[ASIZE(keys)]; Eterm result = NIL; @@ -2824,6 +2770,7 @@ port_info_1(BIF_ALIST_1) ** name String ** input Number of bytes input from port program ** output Number of bytes output to the port program +** os_pid The child's process ID */ BIF_RETTYPE port_info_2(BIF_ALIST_2) @@ -2924,6 +2871,18 @@ static BIF_RETTYPE port_info(Process* p, Eterm portid, Eterm item) hp = HAlloc(p, hsz); res = erts_bld_uint(&hp, NULL, n); } + else if (item == am_os_pid) { + if (prt->os_pid >= 0) { + Uint hsz = 3; + UWord n = prt->os_pid; + (void) erts_bld_uword(NULL, &hsz, n); + hp = HAlloc(p, hsz); + res = erts_bld_uword(&hp, NULL, n); + } else { + hp = HAlloc(p, 3); + res = am_undefined; + } + } else if (item == am_registered_name) { RegProc *reg; reg = prt->reg; @@ -4108,48 +4067,52 @@ BIF_RETTYPE erts_debug_lock_counters_1(BIF_ALIST_1) Eterm* tp = tuple_val(BIF_ARG_1); switch (arityval(tp[0])) { - case 2: + case 2: { + int opt = 0; + int val = 0; if (ERTS_IS_ATOM_STR("copy_save", tp[1])) { - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - if (tp[2] == am_true) { - - res = erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_COPYSAVE) ? am_true : am_false; - - } else if (tp[2] == am_false) { - - res = erts_lcnt_clear_rt_opt(ERTS_LCNT_OPT_COPYSAVE) ? am_true : am_false; - - } else { - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_ERROR(BIF_P, BADARG); - } - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_RET(res); - + opt = ERTS_LCNT_OPT_COPYSAVE; } else if (ERTS_IS_ATOM_STR("process_locks", tp[1])) { - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - if (tp[2] == am_true) { - - res = erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_PROCLOCK) ? am_true : am_false; - - } else if (tp[2] == am_false) { - - res = erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_PROCLOCK) ? am_true : am_false; + opt = ERTS_LCNT_OPT_PROCLOCK; + } else if (ERTS_IS_ATOM_STR("port_locks", tp[1])) { + opt = ERTS_LCNT_OPT_PORTLOCK; + } else if (ERTS_IS_ATOM_STR("suspend", tp[1])) { + opt = ERTS_LCNT_OPT_SUSPEND; + } else if (ERTS_IS_ATOM_STR("location", tp[1])) { + opt = ERTS_LCNT_OPT_LOCATION; + } else { + BIF_ERROR(BIF_P, BADARG); + } + if (tp[2] == am_true) { + val = 1; + } else if (tp[2] == am_false) { + val = 0; + } else { + BIF_ERROR(BIF_P, BADARG); + } - } else { - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_ERROR(BIF_P, BADARG); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + + if (val) { + res = erts_lcnt_set_rt_opt(opt) ? am_true : am_false; + } else { + res = erts_lcnt_clear_rt_opt(opt) ? am_true : am_false; + } +#ifdef ERTS_SMP + if (res != tp[2]) { + if (opt == ERTS_LCNT_OPT_PORTLOCK) { + erts_lcnt_enable_io_lock_count(val); + } else if (opt == ERTS_LCNT_OPT_PROCLOCK) { + erts_lcnt_enable_proc_lock_count(val); } - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); - BIF_RET(res); - } - break; + } +#endif + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_RET(res); + break; + } default: break; diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 5525426824..2a1b01b107 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -265,7 +265,7 @@ port_call(Process* c_p, Eterm arg1, Eterm arg2, Eterm arg3) Eterm res; Sint result_size; Eterm *hp; - Eterm *hp_end; /* To satisfy hybrid heap architecture */ + Eterm *hp_end; unsigned ret_flags = 0U; int fpe_was_unmasked; diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index 6b843d2e08..b036c5ef5c 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -71,14 +71,9 @@ void erts_init_bif_re(void) erts_pcre_stack_free = &erts_erts_pcre_stack_free; default_table = NULL; /* ISO8859-1 default, forced into pcre */ max_loop_limit = CONTEXT_REDS * LOOP_FACTOR; - - sys_memset((void *) &re_exec_trap_export, 0, sizeof(Export)); - re_exec_trap_export.address = &re_exec_trap_export.code[3]; - re_exec_trap_export.code[0] = am_erlang; - re_exec_trap_export.code[1] = am_re_run_trap; - re_exec_trap_export.code[2] = 3; - re_exec_trap_export.code[3] = (BeamInstr) em_apply_bif; - re_exec_trap_export.code[4] = (BeamInstr) &re_exec_trap; + + erts_init_trap_export(&re_exec_trap_export, am_erlang, am_re_run_trap, 3, + &re_exec_trap); grun_trap_exportp = erts_export_put(am_re,am_grun,3); urun_trap_exportp = erts_export_put(am_re,am_urun,3); diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 8fc8e363b1..e88fb8c9f4 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -42,12 +42,24 @@ #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) const struct trace_pattern_flags erts_trace_pattern_flags_off = {0, 0, 0, 0, 0}; + +/* + * The following variables are protected by code write permission. + */ static int erts_default_trace_pattern_is_on; static Binary *erts_default_match_spec; static Binary *erts_default_meta_match_spec; static struct trace_pattern_flags erts_default_trace_pattern_flags; static Eterm erts_default_meta_tracer_pid; +static struct { /* Protected by code write permission */ + int current; + int install; + int local; + BpFunctions f; /* Local functions */ + BpFunctions e; /* Export entries */ +} finish_bp; + static Eterm trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist); static BIF_RETTYPE @@ -60,12 +72,11 @@ static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_on_load(Process* p, Eterm key); -static int setup_func_trace(Export* ep, void* match_prog); -static int reset_func_trace(Export* ep); -static void reset_bif_trace(int bif_index); -static void setup_bif_trace(int bif_index); -static void set_trace_bif(int bif_index, void* match_prog); -static void clear_trace_bif(int bif_index); +static void reset_bif_trace(void); +static void setup_bif_trace(void); +static void install_exp_breakpoints(BpFunctions* f); +static void uninstall_exp_breakpoints(BpFunctions* f); +static void clean_export_entries(BpFunctions* f); void erts_bif_trace_init(void) @@ -98,7 +109,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) { DeclareTmpHeap(mfa,3,p); /* Not really heap here, but might be when setting pattern */ int i; - int matches = 0; + int matches = -1; int specified = 0; enum erts_break_op on; Binary* match_prog_set; @@ -107,9 +118,12 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) int is_global; Process *meta_tracer_proc = p; Eterm meta_tracer_pid = p->id; + int is_blocking = 0; - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); + if (!erts_try_seize_code_write_permission(p)) { + ERTS_BIF_YIELD3(bif_export[BIF_trace_pattern_3], p, MFA, Pattern, flaglist); + } + finish_bp.current = -1; UseTmpHeap(3,p); /* @@ -241,7 +255,6 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) erts_default_meta_match_spec = NULL; erts_default_meta_tracer_pid = NIL; } - MatchSetUnref(match_prog_set); if (erts_default_trace_pattern_flags.breakpoint && flags.breakpoint) { /* Breakpoint trace -> breakpoint trace */ @@ -297,8 +310,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) erts_default_trace_pattern_is_on = !!flags.breakpoint; } } - - goto done; + matches = 0; } else if (is_tuple(MFA)) { Eterm *tp = tuple_val(MFA); if (tp[0] != make_arityval(3)) { @@ -322,35 +334,37 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist) if (is_small(mfa[2])) { mfa[2] = signed_val(mfa[2]); } - } else { - goto error; - } - if (meta_tracer_proc) { - meta_tracer_proc->trace_flags |= F_TRACER; - } + if (meta_tracer_proc) { + meta_tracer_proc->trace_flags |= F_TRACER; + } + matches = erts_set_trace_pattern(p, mfa, specified, + match_prog_set, match_prog_set, + on, flags, meta_tracer_pid, 0); + } - matches = erts_set_trace_pattern(mfa, specified, - match_prog_set, match_prog_set, - on, flags, meta_tracer_pid); + error: MatchSetUnref(match_prog_set); - - done: UnUseTmpHeap(3,p); - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); - return make_small(matches); - - error: +#ifdef ERTS_SMP + if (finish_bp.current >= 0) { + ASSERT(matches >= 0); + erts_notify_finish_breakpointing(p); + erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL); + ERTS_BIF_YIELD_RETURN(p, make_small(matches)); + } +#endif - MatchSetUnref(match_prog_set); + erts_release_code_write_permission(); - UnUseTmpHeap(3,p); - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); - BIF_ERROR(p, BADARG); + if (matches >= 0) { + return make_small(matches); + } + else { + BIF_ERROR(p, BADARG); + } } void @@ -360,6 +374,8 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on, struct trace_pattern_flags *trace_pattern_flags, Eterm *meta_tracer_pid) { + ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked() || + erts_smp_thr_progress_is_blocking()); if (trace_pattern_is_on) *trace_pattern_is_on = erts_default_trace_pattern_is_on; if (match_spec) @@ -372,7 +388,12 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on, *meta_tracer_pid = erts_default_meta_tracer_pid; } - +int erts_is_default_trace_enabled(void) +{ + ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked() || + erts_smp_thr_progress_is_blocking()); + return erts_default_trace_pattern_is_on; +} Uint erts_trace_flag2bit(Eterm flag) @@ -466,6 +487,10 @@ Eterm trace_3(BIF_ALIST_3) BIF_ERROR(p, BADARG); } + if (!erts_try_seize_code_write_permission(BIF_P)) { + ERTS_BIF_YIELD3(bif_export[BIF_trace_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); + } + if (is_nil(tracer) || is_internal_pid(tracer)) { Process *tracer_proc = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, @@ -729,6 +754,7 @@ Eterm trace_3(BIF_ALIST_3) erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); } #endif + erts_release_code_write_permission(); BIF_RET(make_small(matches)); @@ -744,6 +770,7 @@ Eterm trace_3(BIF_ALIST_3) erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN); } #endif + erts_release_code_write_permission(); BIF_ERROR(p, BADARG); } @@ -838,6 +865,11 @@ Eterm trace_info_2(BIF_ALIST_2) Eterm What = BIF_ARG_1; Eterm Key = BIF_ARG_2; Eterm res; + + if (!erts_try_seize_code_write_permission(p)) { + ERTS_BIF_YIELD2(bif_export[BIF_trace_info_2], p, What, Key); + } + if (What == am_on_load) { res = trace_info_on_load(p, Key); } else if (is_atom(What) || is_pid(What)) { @@ -845,8 +877,10 @@ Eterm trace_info_2(BIF_ALIST_2) } else if (is_tuple(What)) { res = trace_info_func(p, What, Key); } else { + erts_release_code_write_permission(); BIF_ERROR(p, BADARG); } + erts_release_code_write_permission(); BIF_RET(res); } @@ -974,64 +1008,54 @@ static int function_is_traced(Process *p, Binary **ms, /* out */ Binary **ms_meta, /* out */ Eterm *tracer_pid_meta, /* out */ - Sint *count, /* out */ + Uint *count, /* out */ Eterm *call_time) /* out */ { Export e; Export* ep; - int i; - BeamInstr *code; + BeamInstr* pc; /* First look for an export entry */ e.code[0] = mfa[0]; e.code[1] = mfa[1]; e.code[2] = mfa[2]; if ((ep = export_get(&e)) != NULL) { - if (ep->address == ep->code+3 && - ep->code[3] != (BeamInstr) em_call_error_handler) { - if (ep->code[3] == (BeamInstr) em_call_traced_function) { - *ms = ep->match_prog_set; + pc = ep->code+3; + if (ep->addressv[erts_active_code_ix()] == pc && + *pc != (BeamInstr) em_call_error_handler) { + + int r = 0; + + ASSERT(*pc == (BeamInstr) em_apply_bif || + *pc == (BeamInstr) BeamOp(op_i_generic_breakpoint)); + + if (erts_is_trace_break(pc, ms, 0)) { return FUNC_TRACE_GLOBAL_TRACE; } - if (ep->code[3] == (BeamInstr) em_apply_bif) { - for (i = 0; i < BIF_SIZE; ++i) { - if (bif_export[i] == ep) { - int r = 0; - - if (erts_bif_trace_flags[i] & BIF_TRACE_AS_GLOBAL) { - *ms = ep->match_prog_set; - return FUNC_TRACE_GLOBAL_TRACE; - } else { - if (erts_bif_trace_flags[i] & BIF_TRACE_AS_LOCAL) { - r |= FUNC_TRACE_LOCAL_TRACE; - *ms = ep->match_prog_set; - } - if (erts_is_mtrace_break(ep->code+3, ms_meta, - tracer_pid_meta)) { - r |= FUNC_TRACE_META_TRACE; - } - if (erts_is_time_break(p, ep->code+3, call_time)) { - r |= FUNC_TRACE_TIME_TRACE; - } - } - return r ? r : FUNC_TRACE_UNTRACED; - } - } - erl_exit(1,"Impossible ghost bif encountered in trace_info."); + + if (erts_is_trace_break(pc, ms, 1)) { + r |= FUNC_TRACE_LOCAL_TRACE; + } + if (erts_is_mtrace_break(pc, ms_meta, tracer_pid_meta)) { + r |= FUNC_TRACE_META_TRACE; } + if (erts_is_time_break(p, pc, call_time)) { + r |= FUNC_TRACE_TIME_TRACE; + } + return r ? r : FUNC_TRACE_UNTRACED; } } /* OK, now look for breakpoint tracing */ - if ((code = erts_find_local_func(mfa)) != NULL) { + if ((pc = erts_find_local_func(mfa)) != NULL) { int r = - (erts_is_trace_break(code, ms, NULL) + (erts_is_trace_break(pc, ms, 1) ? FUNC_TRACE_LOCAL_TRACE : 0) - | (erts_is_mtrace_break(code, ms_meta, tracer_pid_meta) + | (erts_is_mtrace_break(pc, ms_meta, tracer_pid_meta) ? FUNC_TRACE_META_TRACE : 0) - | (erts_is_count_break(code, count) + | (erts_is_count_break(pc, count) ? FUNC_TRACE_COUNT_TRACE : 0) - | (erts_is_time_break(p, code, call_time) + | (erts_is_time_break(p, pc, call_time) ? FUNC_TRACE_TIME_TRACE : 0); return r ? r : FUNC_TRACE_UNTRACED; @@ -1046,7 +1070,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) Eterm* hp; DeclareTmpHeap(mfa,3,p); /* Not really heap here, but might be when setting pattern */ Binary *ms = NULL, *ms_meta = NULL; - Sint count = 0; + Uint count = 0; Eterm traced = am_false; Eterm match_spec = am_false; Eterm retval = am_false; @@ -1134,9 +1158,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) break; case am_call_count: if (r & FUNC_TRACE_COUNT_TRACE) { - retval = count < 0 ? - erts_make_integer(-count-1, p) : - erts_make_integer(count, p); + retval = erts_make_integer(count, p); } break; case am_call_time: @@ -1325,38 +1347,46 @@ trace_info_on_load(Process* p, Eterm key) #undef FUNC_TRACE_LOCAL_TRACE int -erts_set_trace_pattern(Eterm* mfa, int specified, +erts_set_trace_pattern(Process*p, Eterm* mfa, int specified, Binary* match_prog_set, Binary *meta_match_prog_set, int on, struct trace_pattern_flags flags, - Eterm meta_tracer_pid) + Eterm meta_tracer_pid, int is_blocking) { + const ErtsCodeIndex code_ix = erts_active_code_ix(); int matches = 0; int i; + int n; + BpFunction* fp; /* * First work on normal functions (not real BIFs). */ - - for (i = 0; i < export_list_size(); i++) { - Export* ep = export_list(i); - int j; - - if (ExportIsBuiltIn(ep)) { - continue; - } - - for (j = 0; j < specified && mfa[j] == ep->code[j]; j++) { - /* Empty loop body */ - } - if (j == specified) { - if (on) { - if (! flags.breakpoint) - matches += setup_func_trace(ep, match_prog_set); - else - reset_func_trace(ep); - } else if (! flags.breakpoint) { - matches += reset_func_trace(ep); + erts_bp_match_export(&finish_bp.e, mfa, specified); + fp = finish_bp.e.matching; + n = finish_bp.e.matched; + + for (i = 0; i < n; i++) { + BeamInstr* pc = fp[i].pc; + Export* ep = (Export *)(((char *)(pc-3)) - offsetof(Export, code)); + + if (!on || flags.breakpoint) { + erts_clear_call_trace_bif(pc, 0); + if (pc[0] == (BeamInstr) BeamOp(op_i_generic_breakpoint)) { + pc[0] = (BeamInstr) BeamOp(op_jump_f); + } + } else { + if (ep->addressv[code_ix] != pc) { + fp[i].mod->curr.num_traced_exports++; +#ifdef DEBUG + pc[-5] = (BeamInstr) BeamOp(op_i_func_info_IaaI); +#endif + pc[0] = (BeamInstr) BeamOp(op_jump_f); + pc[1] = (BeamInstr) ep->addressv[code_ix]; + } + erts_set_call_trace_bif(pc, match_prog_set, 0); + if (ep->addressv[code_ix] != pc) { + pc[0] = (BeamInstr) BeamOp(op_i_generic_breakpoint); } } } @@ -1381,26 +1411,15 @@ erts_set_trace_pattern(Eterm* mfa, int specified, /* Empty loop body */ } if (j == specified) { + BeamInstr* pc = (BeamInstr *)bif_export[i]->code + 3; + if (! flags.breakpoint) { /* Export entry call trace */ if (on) { - if (erts_bif_trace_flags[i] & BIF_TRACE_AS_META) { - ASSERT(ExportIsBuiltIn(bif_export[i])); - erts_clear_mtrace_bif - ((BeamInstr *)bif_export[i]->code + 3); - erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_META; - } - set_trace_bif(i, match_prog_set); - erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_LOCAL; - erts_bif_trace_flags[i] |= BIF_TRACE_AS_GLOBAL; - setup_bif_trace(i); + erts_clear_call_trace_bif(pc, 1); + erts_clear_mtrace_bif(pc); + erts_set_call_trace_bif(pc, match_prog_set, 0); } else { /* off */ - if (erts_bif_trace_flags[i] & BIF_TRACE_AS_GLOBAL) { - clear_trace_bif(i); - erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_GLOBAL; - } - if (! erts_bif_trace_flags[i]) { - reset_bif_trace(i); - } + erts_clear_call_trace_bif(pc, 0); } matches++; } else { /* Breakpoint call trace */ @@ -1408,52 +1427,33 @@ erts_set_trace_pattern(Eterm* mfa, int specified, if (on) { if (flags.local) { - set_trace_bif(i, match_prog_set); - erts_bif_trace_flags[i] |= BIF_TRACE_AS_LOCAL; - erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_GLOBAL; + erts_clear_call_trace_bif(pc, 0); + erts_set_call_trace_bif(pc, match_prog_set, 1); m = 1; } if (flags.meta) { - erts_set_mtrace_bif - ((BeamInstr *)bif_export[i]->code + 3, - meta_match_prog_set, meta_tracer_pid); - erts_bif_trace_flags[i] |= BIF_TRACE_AS_META; - erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_GLOBAL; + erts_set_mtrace_bif(pc, meta_match_prog_set, + meta_tracer_pid); m = 1; } if (flags.call_time) { - erts_set_time_trace_bif(bif_export[i]->code + 3, on); + erts_set_time_trace_bif(pc, on); /* I don't want to remove any other tracers */ - erts_bif_trace_flags[i] |= BIF_TRACE_AS_CALL_TIME; m = 1; } - if (erts_bif_trace_flags[i]) { - setup_bif_trace(i); - } } else { /* off */ if (flags.local) { - if (erts_bif_trace_flags[i] & BIF_TRACE_AS_LOCAL) { - clear_trace_bif(i); - erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_LOCAL; - } + erts_clear_call_trace_bif(pc, 1); m = 1; } if (flags.meta) { - if (erts_bif_trace_flags[i] & BIF_TRACE_AS_META) { - erts_clear_mtrace_bif - ((BeamInstr *)bif_export[i]->code + 3); - erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_META; - } + erts_clear_mtrace_bif(pc); m = 1; } if (flags.call_time) { - erts_clear_time_trace_bif(bif_export[i]->code + 3); - erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_CALL_TIME; + erts_clear_time_trace_bif(pc); m = 1; } - if (! erts_bif_trace_flags[i]) { - reset_bif_trace(i); - } } matches += m; } @@ -1463,176 +1463,242 @@ erts_set_trace_pattern(Eterm* mfa, int specified, /* ** So, now for breakpoint tracing */ + erts_bp_match_functions(&finish_bp.f, mfa, specified); if (on) { if (! flags.breakpoint) { - erts_clear_trace_break(mfa, specified); - erts_clear_mtrace_break(mfa, specified); - erts_clear_count_break(mfa, specified); - erts_clear_time_break(mfa, specified); + erts_clear_all_breaks(&finish_bp.f); } else { - int m = 0; if (flags.local) { - m = erts_set_trace_break(mfa, specified, match_prog_set, - am_true); + erts_set_trace_break(&finish_bp.f, match_prog_set); } if (flags.meta) { - m = erts_set_mtrace_break(mfa, specified, meta_match_prog_set, - meta_tracer_pid); + erts_set_mtrace_break(&finish_bp.f, meta_match_prog_set, + meta_tracer_pid); } if (flags.call_count) { - m = erts_set_count_break(mfa, specified, on); + erts_set_count_break(&finish_bp.f, on); } if (flags.call_time) { - m = erts_set_time_break(mfa, specified, on); + erts_set_time_break(&finish_bp.f, on); } - /* All assignments to 'm' above should give the same value, - * so just use the last */ - matches += m; } } else { - int m = 0; if (flags.local) { - m = erts_clear_trace_break(mfa, specified); + erts_clear_trace_break(&finish_bp.f); } if (flags.meta) { - m = erts_clear_mtrace_break(mfa, specified); + erts_clear_mtrace_break(&finish_bp.f); } if (flags.call_count) { - m = erts_clear_count_break(mfa, specified); + erts_clear_count_break(&finish_bp.f); } if (flags.call_time) { - m = erts_clear_time_break(mfa, specified); + erts_clear_time_break(&finish_bp.f); + } + } + + finish_bp.current = 0; + finish_bp.install = on; + finish_bp.local = flags.breakpoint; + +#ifdef ERTS_SMP + if (is_blocking) { + ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking()); +#endif + while (erts_finish_breakpointing()) { + /* Empty loop body */ } - /* All assignments to 'm' above should give the same value, - * so just use the last */ - matches += m; +#ifdef ERTS_SMP + finish_bp.current = -1; } +#endif + if (flags.breakpoint) { + matches += finish_bp.f.matched; + } else { + matches += finish_bp.e.matched; + } return matches; } -/* - * Setup function tracing for the given exported function. - * - * Return Value: 1 if entry refers to a BIF or loaded function, - * 0 if the entry refers to a function not loaded. - */ - -static int -setup_func_trace(Export* ep, void* match_prog) +int +erts_finish_breakpointing(void) { - if (ep->address == ep->code+3) { - if (ep->code[3] == (BeamInstr) em_call_error_handler) { - return 0; - } else if (ep->code[3] == (BeamInstr) em_call_traced_function) { - MatchSetUnref(ep->match_prog_set); - ep->match_prog_set = match_prog; - MatchSetRef(ep->match_prog_set); - return 1; - } else { - /* - * We ignore apply/3 and anything else. - */ - return 0; - } - } - + ERTS_SMP_LC_ASSERT(erts_is_code_ix_locked()); + /* - * Currently no trace support for native code. + * Memory barriers will be issued for all processes *before* + * each of the stages below. (Unless the other schedulers + * are blocked, in which case memory barriers will be issued + * when they are awaken.) */ - if (erts_is_native_break(ep->address)) { + + switch (finish_bp.current++) { + case 0: + /* + * At this point, in all functions that are to be breakpointed, + * a pointer to a GenericBp struct has already been added, + * + * Insert the new breakpoints (if any) into the + * code. Different schedulers may see breakpoint instruction + * at different times, but it does not matter since the newly + * added breakpoints are disabled. + */ + if (finish_bp.install) { + if (finish_bp.local) { + erts_install_breakpoints(&finish_bp.f); + } else { + install_exp_breakpoints(&finish_bp.e); + } + } + setup_bif_trace(); + return 1; + case 1: + /* + * Switch index for the breakpoint data, activating the staged + * data. (Depending on the changes in the breakpoint data, + * that could either activate breakpoints or disable + * breakpoints.) + */ + erts_commit_staged_bp(); + return 1; + case 2: + /* + * Remove breakpoints instructions for disabled breakpoints + * (if any). + */ + if (finish_bp.install) { + if (finish_bp.local) { + uninstall_exp_breakpoints(&finish_bp.e); + } else { + erts_uninstall_breakpoints(&finish_bp.f); + } + } else { + if (finish_bp.local) { + erts_uninstall_breakpoints(&finish_bp.f); + } else { + uninstall_exp_breakpoints(&finish_bp.e); + } + } + reset_bif_trace(); + return 1; + case 3: + /* + * Now all breakpoints have either been inserted or removed. + * For all updated breakpoints, copy the active breakpoint + * data to the staged breakpoint data to make them equal + * (simplifying for the next time breakpoints are to be + * updated). If any breakpoints have been totally disabled, + * deallocate the GenericBp structs for them. + */ + erts_consolidate_bif_bp_data(); + clean_export_entries(&finish_bp.e); + erts_consolidate_bp_data(&finish_bp.e, 0); + erts_consolidate_bp_data(&finish_bp.f, 1); + erts_bp_free_matched_functions(&finish_bp.e); + erts_bp_free_matched_functions(&finish_bp.f); return 0; + default: + ASSERT(0); } - - ep->code[3] = (BeamInstr) em_call_traced_function; - ep->code[4] = (BeamInstr) ep->address; - ep->address = ep->code+3; - ep->match_prog_set = match_prog; - MatchSetRef(ep->match_prog_set); - return 1; + return 0; } -static void setup_bif_trace(int bif_index) { - Export *ep = bif_export[bif_index]; - - ASSERT(ExportIsBuiltIn(ep)); - ASSERT(ep->code[4]); - ep->code[4] = (BeamInstr) bif_table[bif_index].traced; -} +static void +install_exp_breakpoints(BpFunctions* f) +{ + const ErtsCodeIndex code_ix = erts_active_code_ix(); + BpFunction* fp = f->matching; + Uint ne = f->matched; + Uint i; + Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr); -static void set_trace_bif(int bif_index, void* match_prog) { - Export *ep = bif_export[bif_index]; - -#ifdef HARDDEBUG - erts_fprintf(stderr, "set_trace_bif: %T:%T/%bpu\n", - ep->code[0], ep->code[1], ep->code[2]); -#endif - ASSERT(ExportIsBuiltIn(ep)); - MatchSetUnref(ep->match_prog_set); - ep->match_prog_set = match_prog; - MatchSetRef(ep->match_prog_set); -} + for (i = 0; i < ne; i++) { + BeamInstr* pc = fp[i].pc; + Export* ep = (Export *) (((char *)pc)-offset); -/* - * Reset function tracing for the given exported function. - * - * Return Value: 1 if entry refers to a BIF or loaded function, - * 0 if the entry refers to a function not loaded. - */ + ep->addressv[code_ix] = pc; + } +} -static int -reset_func_trace(Export* ep) +static void +uninstall_exp_breakpoints(BpFunctions* f) { - if (ep->address == ep->code+3) { - if (ep->code[3] == (BeamInstr) em_call_error_handler) { - return 0; - } else if (ep->code[3] == (BeamInstr) em_call_traced_function) { - ep->address = (Uint *) ep->code[4]; - MatchSetUnref(ep->match_prog_set); - ep->match_prog_set = NULL; - return 1; - } else { - /* - * We ignore apply/3 and anything else. - */ - return 0; + const ErtsCodeIndex code_ix = erts_active_code_ix(); + BpFunction* fp = f->matching; + Uint ne = f->matched; + Uint i; + Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr); + + for (i = 0; i < ne; i++) { + BeamInstr* pc = fp[i].pc; + Export* ep = (Export *) (((char *)pc)-offset); + + if (ep->addressv[code_ix] != pc) { + continue; } + ASSERT(*pc == (BeamInstr) BeamOp(op_jump_f)); + ep->addressv[code_ix] = (BeamInstr *) ep->code[4]; } - - /* - * Currently no trace support for native code. - */ - if (erts_is_native_break(ep->address)) { - return 0; - } - - /* - * Nothing to do, but the export entry matches. - */ +} + +static void +clean_export_entries(BpFunctions* f) +{ + const ErtsCodeIndex code_ix = erts_active_code_ix(); + BpFunction* fp = f->matching; + Uint ne = f->matched; + Uint i; + Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr); - return 1; + for (i = 0; i < ne; i++) { + BeamInstr* pc = fp[i].pc; + Export* ep = (Export *) (((char *)pc)-offset); + + if (ep->addressv[code_ix] == pc) { + continue; + } + if (*pc == (BeamInstr) BeamOp(op_jump_f)) { + ep->code[3] = (BeamInstr) 0; + ep->code[4] = (BeamInstr) 0; + } + } } -static void reset_bif_trace(int bif_index) { - Export *ep = bif_export[bif_index]; - - ASSERT(ExportIsBuiltIn(ep)); - ASSERT(ep->code[4]); - ASSERT(! ep->match_prog_set); - ASSERT(! erts_is_mtrace_break((BeamInstr *)ep->code+3, NULL, NULL)); - ep->code[4] = (BeamInstr) bif_table[bif_index].f; +static void +setup_bif_trace(void) +{ + int i; + + for (i = 0; i < BIF_SIZE; ++i) { + Export *ep = bif_export[i]; + GenericBp* g = (GenericBp *) ep->fake_op_func_info_for_hipe[1]; + if (g) { + if (ExportIsBuiltIn(ep)) { + ASSERT(ep->code[4]); + ep->code[4] = (BeamInstr) bif_table[i].traced; + } + } + } } -static void clear_trace_bif(int bif_index) { - Export *ep = bif_export[bif_index]; - -#ifdef HARDDEBUG - erts_fprintf(stderr, "clear_trace_bif: %T:%T/%bpu\n", - ep->code[0], ep->code[1], ep->code[2]); -#endif - ASSERT(ExportIsBuiltIn(ep)); - MatchSetUnref(ep->match_prog_set); - ep->match_prog_set = NULL; +static void +reset_bif_trace(void) +{ + int i; + ErtsBpIndex active = erts_active_bp_ix(); + + for (i = 0; i < BIF_SIZE; ++i) { + Export *ep = bif_export[i]; + BeamInstr* pc = ep->code+3; + GenericBp* g = (GenericBp *) pc[-4]; + if (g && g->data[active].flags == 0) { + if (ExportIsBuiltIn(ep)) { + ASSERT(ep->code[4]); + ep->code[4] = (BeamInstr) bif_table[i].f; + } + } + } } /* diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c index 41e71881a0..3f90f34736 100644 --- a/erts/emulator/beam/erl_cpu_topology.c +++ b/erts/emulator/beam/erl_cpu_topology.c @@ -486,7 +486,7 @@ erts_sched_check_cpu_bind_post_suspend(ErtsSchedulerData *esdp) erts_thr_set_main_status(1, (int) esdp->no); /* Make sure we check if we should bind to a cpu or not... */ - ERTS_RUNQ_FLGS_SET(esdp->run_queue, ERTS_RUNQ_FLG_CHK_CPU_BIND); + (void) ERTS_RUNQ_FLGS_SET(esdp->run_queue, ERTS_RUNQ_FLG_CHK_CPU_BIND); } #endif diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 2b8bcb5bf7..5bd8163968 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -2816,7 +2816,6 @@ void init_db(void) { DbTable init_tb; int i; - extern BeamInstr* em_apply_bif; Eterm *hp; unsigned bits; size_t size; @@ -2949,49 +2948,24 @@ void init_db(void) } /* Non visual BIF to trap to. */ - memset(&ets_select_delete_continue_exp, 0, sizeof(Export)); - ets_select_delete_continue_exp.address = - &ets_select_delete_continue_exp.code[3]; - ets_select_delete_continue_exp.code[0] = am_ets; - ets_select_delete_continue_exp.code[1] = am_atom_put("delete_trap",11); - ets_select_delete_continue_exp.code[2] = 1; - ets_select_delete_continue_exp.code[3] = - (BeamInstr) em_apply_bif; - ets_select_delete_continue_exp.code[4] = - (BeamInstr) &ets_select_delete_1; + erts_init_trap_export(&ets_select_delete_continue_exp, + am_ets, am_atom_put("delete_trap",11), 1, + &ets_select_delete_1); /* Non visual BIF to trap to. */ - memset(&ets_select_count_continue_exp, 0, sizeof(Export)); - ets_select_count_continue_exp.address = - &ets_select_count_continue_exp.code[3]; - ets_select_count_continue_exp.code[0] = am_ets; - ets_select_count_continue_exp.code[1] = am_atom_put("count_trap",11); - ets_select_count_continue_exp.code[2] = 1; - ets_select_count_continue_exp.code[3] = - (BeamInstr) em_apply_bif; - ets_select_count_continue_exp.code[4] = - (BeamInstr) &ets_select_count_1; + erts_init_trap_export(&ets_select_count_continue_exp, + am_ets, am_atom_put("count_trap",11), 1, + &ets_select_count_1); /* Non visual BIF to trap to. */ - memset(&ets_select_continue_exp, 0, sizeof(Export)); - ets_select_continue_exp.address = - &ets_select_continue_exp.code[3]; - ets_select_continue_exp.code[0] = am_ets; - ets_select_continue_exp.code[1] = am_atom_put("select_trap",11); - ets_select_continue_exp.code[2] = 1; - ets_select_continue_exp.code[3] = - (BeamInstr) em_apply_bif; - ets_select_continue_exp.code[4] = - (BeamInstr) &ets_select_trap_1; + erts_init_trap_export(&ets_select_continue_exp, + am_ets, am_atom_put("select_trap",11), 1, + &ets_select_trap_1); /* Non visual BIF to trap to. */ - memset(&ets_delete_continue_exp, 0, sizeof(Export)); - ets_delete_continue_exp.address = &ets_delete_continue_exp.code[3]; - ets_delete_continue_exp.code[0] = am_ets; - ets_delete_continue_exp.code[1] = am_atom_put("delete_trap",11); - ets_delete_continue_exp.code[2] = 1; - ets_delete_continue_exp.code[3] = (BeamInstr) em_apply_bif; - ets_delete_continue_exp.code[4] = (BeamInstr) &ets_delete_trap; + erts_init_trap_export(&ets_delete_continue_exp, + am_ets, am_atom_put("delete_trap",11), 1, + &ets_delete_trap); hp = ms_delete_all_buff; ms_delete_all = CONS(hp, am_true, NIL); diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index c726be5fb4..2fea4671e1 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -105,7 +105,20 @@ #define NSEG_2 256 /* Size of second segment table */ #define NSEG_INC 128 /* Number of segments to grow after that */ -#define SEGTAB(tb) ((struct segment**)erts_smp_atomic_read_ddrb(&(tb)->segtab)) +#ifdef ERTS_SMP +# define DB_USING_FINE_LOCKING(TB) (((TB))->common.type & DB_FINE_LOCKED) +#else +# define DB_USING_FINE_LOCKING(TB) 0 +#endif + +#ifdef ETHR_ORDERED_READ_DEPEND +#define SEGTAB(tb) ((struct segment**) erts_smp_atomic_read_nob(&(tb)->segtab)) +#else +#define SEGTAB(tb) \ + (DB_USING_FINE_LOCKING(tb) \ + ? ((struct segment**) erts_smp_atomic_read_ddrb(&(tb)->segtab)) \ + : ((struct segment**) erts_smp_atomic_read_nob(&(tb)->segtab))) +#endif #define NACTIVE(tb) ((int)erts_smp_atomic_read_nob(&(tb)->nactive)) #define NITEMS(tb) ((int)erts_smp_atomic_read_nob(&(tb)->common.nitems)) @@ -122,7 +135,9 @@ */ static ERTS_INLINE Uint hash_to_ix(DbTableHash* tb, HashValue hval) { - Uint mask = erts_smp_atomic_read_acqb(&tb->szm); + Uint mask = (DB_USING_FINE_LOCKING(tb) + ? erts_smp_atomic_read_acqb(&tb->szm) + : erts_smp_atomic_read_nob(&tb->szm)); Uint ix = hval & mask; if (ix >= erts_smp_atomic_read_nob(&tb->nactive)) { ix &= mask>>1; @@ -319,7 +334,10 @@ struct ext_segment { static ERTS_INLINE void SET_SEGTAB(DbTableHash* tb, struct segment** segtab) { - erts_smp_atomic_set_wb(&tb->segtab, (erts_aint_t) segtab); + if (DB_USING_FINE_LOCKING(tb)) + erts_smp_atomic_set_wb(&tb->segtab, (erts_aint_t) segtab); + else + erts_smp_atomic_set_nob(&tb->segtab, (erts_aint_t) segtab); #ifdef VALGRIND tb->top_ptr_to_segment_with_active_segtab = EXTSEG(segtab); #endif @@ -2501,6 +2519,28 @@ static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2, return list; } +static ERTS_INLINE int +begin_resizing(DbTableHash* tb) +{ + if (DB_USING_FINE_LOCKING(tb)) + return !erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1); + else { + if (erts_smp_atomic_read_nob(&tb->is_resizing)) + return 0; + erts_smp_atomic_set_nob(&tb->is_resizing, 1); + return 1; + } +} + +static ERTS_INLINE void +done_resizing(DbTableHash* tb) +{ + if (DB_USING_FINE_LOCKING(tb)) + erts_smp_atomic_set_relb(&tb->is_resizing, 0); + else + erts_smp_atomic_set_nob(&tb->is_resizing, 0); +} + /* Grow table with one new bucket. ** Allocate new segment if needed. */ @@ -2513,9 +2553,8 @@ static void grow(DbTableHash* tb, int nactive) int from_ix; int szm; - if (erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1)) { + if (!begin_resizing(tb)) return; /* already in progress */ - } if (NACTIVE(tb) != nactive) { goto abort; /* already done (race) */ } @@ -2547,9 +2586,12 @@ static void grow(DbTableHash* tb, int nactive) } erts_smp_atomic_inc_nob(&tb->nactive); if (from_ix == 0) { - erts_smp_atomic_set_relb(&tb->szm, szm); + if (DB_USING_FINE_LOCKING(tb)) + erts_smp_atomic_set_relb(&tb->szm, szm); + else + erts_smp_atomic_set_nob(&tb->szm, szm); } - erts_smp_atomic_set_relb(&tb->is_resizing, 0); + done_resizing(tb); /* Finally, let's split the bucket. We try to do it in a smart way to keep link order and avoid unnecessary updates of next-pointers */ @@ -2581,7 +2623,7 @@ static void grow(DbTableHash* tb, int nactive) return; abort: - erts_smp_atomic_set_relb(&tb->is_resizing, 0); + done_resizing(tb); } @@ -2590,9 +2632,8 @@ abort: */ static void shrink(DbTableHash* tb, int nactive) { - if (erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1)) { + if (!begin_resizing(tb)) return; /* already in progress */ - } if (NACTIVE(tb) == nactive) { erts_smp_rwmtx_t* lck; int src_ix = nactive - 1; @@ -2639,7 +2680,7 @@ static void shrink(DbTableHash* tb, int nactive) } /*else already done */ - erts_smp_atomic_set_relb(&tb->is_resizing, 0); + done_resizing(tb); } diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 312050b931..faa7f31d99 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -452,16 +452,8 @@ DbTableMethod db_tree = void db_initialize_tree(void) { - memset(&ets_select_reverse_exp, 0, sizeof(Export)); - ets_select_reverse_exp.address = - &ets_select_reverse_exp.code[3]; - ets_select_reverse_exp.code[0] = am_ets; - ets_select_reverse_exp.code[1] = am_reverse; - ets_select_reverse_exp.code[2] = 3; - ets_select_reverse_exp.code[3] = - (BeamInstr) em_apply_bif; - ets_select_reverse_exp.code[4] = - (BeamInstr) &ets_select_reverse; + erts_init_trap_export(&ets_select_reverse_exp, am_ets, am_reverse, 3, + &ets_select_reverse); return; }; diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index d7d6fcf0a2..2121f72fd2 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -25,7 +25,6 @@ #include "erl_vm.h" #include "global.h" #include "erl_process.h" -#include "erl_nmgc.h" #include "big.h" #include "bif.h" #include "beam_catches.h" @@ -33,34 +32,9 @@ #define WITHIN(ptr, x, y) ((x) <= (ptr) && (ptr) < (y)) -#if defined(HYBRID) -#if defined(INCREMENTAL) -/* Hybrid + Incremental */ -#define IN_HEAP(p, ptr) \ - (WITHIN((ptr), p->heap, p->hend) || \ - (OLD_HEAP(p) && WITHIN((ptr), OLD_HEAP(p), OLD_HEND(p))) || \ - WITHIN((ptr), global_heap, global_hend) || \ - (inc_fromspc && WITHIN((ptr), inc_fromspc, inc_fromend)) || \ - WITHIN((ptr), global_old_heap, global_old_hend)) - -#define IN_MA(ptr) \ - (WITHIN((ptr), global_heap, global_hend) || \ - (inc_fromspc && WITHIN((ptr), inc_fromspc, inc_fromend)) || \ - WITHIN((ptr), global_old_heap, global_old_hend)) -#else -/* Hybrid */ -#define IN_HEAP(p, ptr) \ - (WITHIN((ptr), p->heap, p->hend) || \ - (OLD_HEAP(p) && WITHIN((ptr), OLD_HEAP(p), OLD_HEND(p))) || \ - WITHIN((ptr), global_heap, global_hend) || \ - (global_old_heap && WITHIN((ptr),global_old_heap,global_old_hend))) -#endif -#else -/* Private */ #define IN_HEAP(p, ptr) \ (WITHIN((ptr), p->heap, p->hend) || \ (OLD_HEAP(p) && WITHIN((ptr), OLD_HEAP(p), OLD_HEND(p)))) -#endif #ifdef __GNUC__ @@ -266,13 +240,6 @@ static int verify_eterm(Process *p,Eterm element) } } } -#ifdef INCREMENTAL - else { - if (IN_MA(ptr)) - return 1; - } -#endif - return 0; } @@ -447,51 +414,12 @@ void verify_process(Process *p) VERIFY_ETERM("fvalue",p->fvalue); VERIFY_ETERM("ftrace",p->ftrace); -#ifdef HYBRID - VERIFY_AREA("rrma",p->rrma,p->nrr); -#endif - VERBOSE(DEBUG_MEMORY,("...done\n")); #undef VERIFY_AREA #undef VERIFY_ETERM } -void verify_everything() -{ -#ifdef HYBRID - Uint i; - Uint n = erts_num_active_procs; - -#ifdef INCREMENTAL_FREE_SIZES_NEEDS_TO_BE_TAGGED_AS_HEADERS_WITH_ARITY - INC_Page *page = inc_used_mem; -#endif - - for (i = 0; i < n; i++) { - verify_process(erts_active_procs[i]); - } - - erts_check_memory(NULL,global_heap,global_htop); - -#ifdef INCREMENTAL_FREE_SIZES_NEEDS_TO_BE_TAGGED_AS_HEADERS_WITH_ARITY - while (page) - { - Eterm *end = page + INC_PAGE_SIZE; - Eterm *pos = page->start; - - while( pos < end) { - Eterm val = *pos++; - if(is_header(val)) - pos += thing_arityval(val); - else - verify_eterm(NULL,val); - } - page = page->next; - } -#endif -#endif /* HYBRID */ -} - /* * print_untagged_memory will print the contents of given memory area. */ @@ -582,83 +510,6 @@ void print_tagged_memory(Eterm *pos, Eterm *end) erts_printf("+-%s-+-%s-+\n",dashes,dashes); } -#ifdef HYBRID -void print_ma_info(void) -{ - erts_printf("Message Area (start - top - end): " - "0x%0*lx - 0x%0*lx - 0x%0*lx\n", - PTR_SIZE, (unsigned long)global_heap, - PTR_SIZE, (unsigned long)global_htop, - PTR_SIZE, (unsigned long)global_hend); -#ifndef INCREMENTAL - erts_printf(" High water: 0x%0*lx " - "Old gen: 0x%0*lx - 0x%0*lx - 0x%0*lx\n", - PTR_SIZE, (unsigned long)global_high_water, - PTR_SIZE, (unsigned long)global_old_heap, - PTR_SIZE, (unsigned long)global_old_htop, - PTR_SIZE, (unsigned long)global_old_hend); -#endif -} - -void print_message_area(void) -{ - Eterm *pos = global_heap; - Eterm *end = global_htop; - - erts_printf("From: 0x%0*lx to 0x%0*lx\n", - PTR_SIZE,(unsigned long)pos,PTR_SIZE,(unsigned long)end); - erts_printf("(Old generation: 0x%0*lx to 0x%0*lx\n", - PTR_SIZE, (unsigned long)global_old_heap, - PTR_SIZE, (unsigned long)global_old_hend); - erts_printf("| %-*s | %-*s |\n",PTR_SIZE,"Address",PTR_SIZE,"Contents"); - erts_printf("|-%s-|-%s-|\n",dashes,dashes); - while( pos < end ) { - Eterm val = pos[0]; - erts_printf("| 0x%0*lx | 0x%0*lx | ", - PTR_SIZE,(unsigned long)pos,PTR_SIZE,(unsigned long)val); - ++pos; - if( is_arity_value(val) ) { - erts_printf("Arity(%lu)", arityval(val)); - } else if( is_thing(val) ) { - unsigned int ari = thing_arityval(val); - erts_printf("Thing Arity(%u) Tag(%lu)", ari, thing_subtag(val)); - while( ari ) { - erts_printf("\n| 0x%0*lx | 0x%0*lx | THING", - PTR_SIZE, (unsigned long)pos, - PTR_SIZE, (unsigned long)*pos); - ++pos; - --ari; - } - } else - erts_printf("%.30T", val); - erts_printf("\n"); - } - erts_printf("+-%s-+-%s-+\n",dashes,dashes); -} - -void check_message_area() -{ - Eterm *pos = global_heap; - Eterm *end = global_htop; - - while( pos < end ) { - Eterm val = *pos++; - if(is_header(val)) - pos += thing_arityval(val); - else if(!is_immed(val)) - if ((ptr_val(val) < global_heap || ptr_val(val) >= global_htop) && - (ptr_val(val) < global_old_heap || - ptr_val(val) >= global_old_hend)) - { - erts_printf("check_message_area: Stray pointer found\n"); - print_message_area(); - erts_printf("Crashing to make it look real...\n"); - pos = 0; - } - } -} -#endif /* HYBRID */ - static void print_process_memory(Process *p); static void print_process_memory(Process *p) { @@ -703,19 +554,6 @@ static void print_process_memory(Process *p) erts_printf(" Fvalue: 0x%0*lx\n",PTR_SIZE,p->fvalue); erts_printf(" Ftrace: 0x%0*lx\n",PTR_SIZE,p->ftrace); -#ifdef HYBRID - if (p->nrr > 0) { - int i; - erts_printf(" Remembered Roots:\n"); - for (i = 0; i < p->nrr; i++) - if (p->rrsrc[i] != NULL) - erts_printf("0x%0*lx -> 0x%0*lx\n", - PTR_SIZE, (unsigned long)p->rrsrc[i], - PTR_SIZE, (unsigned long)p->rrma[i]); - erts_printf("\n"); - } -#endif - erts_printf("+- %-*s -+ 0x%0*lx 0x%0*lx %s-%s-+\n", PTR_SIZE, "Stack", PTR_SIZE, (unsigned long)STACK_TOP(p), @@ -757,92 +595,6 @@ void print_memory(Process *p) if (p != NULL) { print_process_memory(p); } -#ifdef HYBRID - else { - Uint i; - Uint n = erts_num_active_procs; - - for (i = 0; i < n; i++) { - Process *p = erts_active_procs[i]; - print_process_memory(p); - } - - erts_printf("==================\n"); - erts_printf("|| Message area ||\n"); - erts_printf("==================\n"); - erts_printf("+-%s-+-%s-%s-%s-%s-+\n", - dashes,dashes,dashes,dashes,dashes); - erts_printf("| %-*s | 0x%0*lx - 0x%0*lx - 0x%0*lx%*s|\n", - PTR_SIZE, "Young", - PTR_SIZE, (unsigned long)global_heap, - PTR_SIZE, (unsigned long)global_htop, - PTR_SIZE, (unsigned long)global_hend, - PTR_SIZE, ""); - erts_printf("+-%s-+-%s-%s-%s-%s-+\n", - dashes,dashes,dashes,dashes,dashes); - - print_untagged_memory(global_heap,global_htop); - - - erts_printf("+-%s-+-%s-%s-%s-%s-+\n", - dashes,dashes,dashes,dashes,dashes); - erts_printf("| %-*s | 0x%0*lx - 0x%0*lx %*s |\n", - PTR_SIZE, "Old", - PTR_SIZE, (unsigned long)global_old_heap, - PTR_SIZE, (unsigned long)global_old_hend, - 2 * PTR_SIZE, ""); - erts_printf("+-%s-+-%s-%s-%s-%s-+\n", - dashes,dashes,dashes,dashes,dashes); - -#ifdef INCREMENTAL - { - INC_Page *page = inc_used_mem; - /* Genom att g� igenom fri-listan f�rst kan vi markera de - omr�den som inte �r allokerade och bara skriva ut de som - lever. - char markarea[INC_PAGESIZE]; - */ - - while (page) { - Eterm *ptr = (Eterm*)page->start; - Eterm *end = (Eterm*)page->start + INC_PAGESIZE; - - erts_printf("| %*s | This: 0x%0*lx Next: 0x%0*lx %*s|\n", - PTR_SIZE, "", - PTR_SIZE, (unsigned long)page, - PTR_SIZE, (unsigned long)page->next, - 2 * PTR_SIZE - 8, ""); - print_untagged_memory(ptr,end); - page = page->next; - } - } - - { - INC_MemBlock *this = inc_free_list; - - erts_printf("-- %-*s --%s-%s-%s-%s-\n",PTR_SIZE+2,"Free list", - dashes,dashes,dashes,dashes); - while (this) { - erts_printf("Block @ 0x%0*lx sz: %8d prev: 0x%0*lx next: 0x%0*lx\n", - PTR_SIZE, (unsigned long)this,this->size, - PTR_SIZE, (unsigned long)this->prev, - PTR_SIZE, (unsigned long)this->next); - this = this->next; - } - erts_printf("--%s---%s-%s-%s-%s--\n", - dashes,dashes,dashes,dashes,dashes); - } - - if (inc_fromspc != NULL) { - erts_printf("-- fromspace - 0x%0*lx 0x%0*lx " - "------------------------------\n", - PTR_SIZE, (unsigned long)inc_fromspc, - PTR_SIZE, (unsigned long)inc_fromend); - print_untagged_memory(inc_fromspc,inc_fromend); - } -#endif /* INCREMENTAL */ - } -#endif /* HYBRID */ } void print_memory_info(Process *p) @@ -869,26 +621,6 @@ void print_memory_info(Process *p) erts_printf("|| Memory info ||\n"); erts_printf("=================\n"); } -#ifdef HYBRID - erts_printf("|- message area --%s-%s-%s-%s-|\n", - dashes,dashes,dashes,dashes); - erts_printf("| Young | 0x%0*lx - 0x%0*lx - 0x%0*lx %*s |\n", - PTR_SIZE, (unsigned long)global_heap, - PTR_SIZE, (unsigned long)global_htop, - PTR_SIZE, (unsigned long)global_hend, - PTR_SIZE, ""); - erts_printf("| Old | 0x%0*lx - 0x%0*lx %*s |\n", - PTR_SIZE, (unsigned long)global_old_heap, - PTR_SIZE, (unsigned long)global_old_hend, - 2 * PTR_SIZE, ""); -#endif -#ifdef INCREMENTAL - if (inc_fromspc != NULL) - erts_printf("| Frmsp | 0x%0*lx - 0x%0*lx %*s |\n", - PTR_SIZE, (unsigned long)inc_fromspc, - PTR_SIZE, (unsigned long)inc_fromend, - 2 * PTR_SIZE, ""); -#endif erts_printf("+-----------------%s-%s-%s-%s-+\n",dashes,dashes,dashes,dashes); } #if !HEAP_ON_C_STACK && defined(DEBUG) diff --git a/erts/emulator/beam/erl_debug.h b/erts/emulator/beam/erl_debug.h index c49354a2b3..a028a95fef 100644 --- a/erts/emulator/beam/erl_debug.h +++ b/erts/emulator/beam/erl_debug.h @@ -42,12 +42,11 @@ #define DEBUG_DEFAULT 0x0000 /* No flags are set per default */ #define DEBUG_SYSTEM 0x0001 /* Misc system info at startup and end */ #define DEBUG_PRIVATE_GC 0x0002 /* GC of private heaps */ -#define DEBUG_HYBRID_GC 0x0004 /* GC of the message area */ -#define DEBUG_ALLOCATION 0x0008 /* HAlloc. To find holes in the heap */ -#define DEBUG_MESSAGES 0x0010 /* Message passing */ -#define DEBUG_THREADS 0x0020 /* Thread-related stuff */ -#define DEBUG_PROCESSES 0x0040 /* Process creation and removal */ -#define DEBUG_MEMORY 0x0080 /* Display results of memory checks */ +#define DEBUG_ALLOCATION 0x0004 /* HAlloc. To find holes in the heap */ +#define DEBUG_MESSAGES 0x0008 /* Message passing */ +#define DEBUG_THREADS 0x0010 /* Thread-related stuff */ +#define DEBUG_PROCESSES 0x0020 /* Process creation and removal */ +#define DEBUG_MEMORY 0x0040 /* Display results of memory checks */ extern Uint32 verbose; @@ -88,7 +87,6 @@ extern void erts_check_stack(Process *p); extern void erts_check_heap(Process *p); extern void erts_check_memory(Process *p, Eterm *start, Eterm *end); extern void verify_process(Process *p); -extern void verify_everything(void); extern void print_tagged_memory(Eterm *start, Eterm *end); extern void print_untagged_memory(Eterm *start, Eterm *end); extern void print_memory(Process *p); @@ -99,10 +97,4 @@ extern void erts_debug_use_tmp_heap(int, Process *); extern void erts_debug_unuse_tmp_heap(int, Process *); #endif -#ifdef HYBRID -extern void print_ma_info(void); -extern void print_message_area(void); -extern void check_message_area(void); -#endif - #endif /* _ERL_DEBUG_H_ */ diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 1ae9a211d7..771ee46d2b 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -87,10 +87,7 @@ #include <stdlib.h> #include <string.h> /* ssize_t on Mac OS X */ -#if defined(VXWORKS) -# include <ioLib.h> -typedef struct iovec SysIOVec; -#elif defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_) +#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_) #ifndef STATIC_ERLANG_DRIVER /* Windows dynamic drivers, everything is different... */ #define ERL_DRIVER_TYPES_ONLY @@ -370,11 +367,7 @@ typedef struct erl_drv_entry { /* For windows dynamic drivers */ #ifndef ERL_DRIVER_TYPES_ONLY -#if defined(VXWORKS) -# define DRIVER_INIT(DRIVER_NAME) \ - ErlDrvEntry* DRIVER_NAME ## _init(void); \ - ErlDrvEntry* DRIVER_NAME ## _init(void) -#elif defined(__WIN32__) +#if defined(__WIN32__) # define DRIVER_INIT(DRIVER_NAME) \ __declspec(dllexport) ErlDrvEntry* driver_init(void); \ __declspec(dllexport) ErlDrvEntry* driver_init(void) diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h index 2f165afa06..217066ab11 100644 --- a/erts/emulator/beam/erl_fun.h +++ b/erts/emulator/beam/erl_fun.h @@ -54,9 +54,7 @@ typedef struct erl_fun_entry { typedef struct erl_fun_thing { Eterm thing_word; /* Subtag FUN_SUBTAG. */ ErlFunEntry* fe; /* Pointer to fun entry. */ -#ifndef HYBRID /* FIND ME! */ struct erl_off_heap_header* next; -#endif #ifdef HIPE UWord* native_address; /* Native code for the fun. */ #endif @@ -79,13 +77,9 @@ ErlFunEntry* erts_get_fun_entry(Eterm mod, int uniq, int index); ErlFunEntry* erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index, byte* uniq, int index, int arity); -ErlFunEntry* erts_get_fun_entry2(Eterm mod, int old_uniq, int old_index, - byte* uniq, int index, int arity); void erts_erase_fun_entry(ErlFunEntry* fe); -#ifndef HYBRID /* FIND ME! */ void erts_cleanup_funs(ErlFunThing* funp); -#endif void erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end); void erts_dump_fun_entries(int, void *); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 945cc2565c..d382e421e6 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -34,7 +34,6 @@ #include "erl_binary.h" #include "dist.h" #include "erl_mseg.h" -#include "erl_nmgc.h" #include "erl_threads.h" #include "erl_bif_timer.h" #include "erl_instrument.h" @@ -209,28 +208,6 @@ Export *erts_delay_trap = NULL; int erts_use_r9_pids_ports; -#ifdef HYBRID -Eterm *global_heap; -Eterm *global_hend; -Eterm *global_htop; -Eterm *global_saved_htop; -Eterm *global_old_heap; -Eterm *global_old_hend; -ErlOffHeap erts_global_offheap; -Uint global_heap_sz = SH_DEFAULT_SIZE; - -#ifndef INCREMENTAL -Eterm *global_high_water; -Eterm *global_old_htop; -#endif - -Uint16 global_gen_gcs; -Uint16 global_max_gen_gcs; -Uint global_gc_flags; - -Uint global_heap_min_sz = SH_DEFAULT_SIZE; -#endif - int ignore_break; int replace_intr; @@ -319,6 +296,7 @@ erl_init(int ncpu) erts_init_trace(); erts_init_binary(); erts_init_bits(); + erts_code_ix_init(); erts_init_fun_table(); init_atom_table(); init_export_table(); @@ -336,7 +314,6 @@ erl_init(int ncpu) erl_drv_thr_init(); erts_init_async(); init_io(); - init_copy(); init_load(); erts_init_bif(); erts_init_bif_chksum(); @@ -357,45 +334,6 @@ erl_init(int ncpu) } static void -init_shared_memory(int argc, char **argv) -{ -#ifdef HYBRID - int arg_size = 0; - - global_heap_sz = erts_next_heap_size(global_heap_sz,0); - - /* Make sure arguments will fit on the heap, no one else will check! */ - while (argc--) - arg_size += 2 + strlen(argv[argc]); - if (global_heap_sz < arg_size) - global_heap_sz = erts_next_heap_size(arg_size,1); - -#ifndef INCREMENTAL - global_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, - sizeof(Eterm) * global_heap_sz); - global_hend = global_heap + global_heap_sz; - global_htop = global_heap; - global_high_water = global_heap; - global_old_hend = global_old_htop = global_old_heap = NULL; -#endif - - global_gen_gcs = 0; - global_max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs); - global_gc_flags = erts_default_process_flags; - - erts_global_offheap.mso = NULL; -#ifndef HYBRID /* FIND ME! */ - erts_global_offheap.funs = NULL; -#endif - erts_global_offheap.overhead = 0; -#endif - -#ifdef INCREMENTAL - erts_init_incgc(); -#endif -} - -static void erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** argv) { int i; @@ -407,7 +345,8 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** Eterm env; start_mod = am_atom_put(modname, sys_strlen(modname)); - if (erts_find_function(start_mod, am_start, 2) == NULL) { + if (erts_find_function(start_mod, am_start, 2, + erts_active_code_ix()) == NULL) { erl_exit(5, "No function %s:start/2\n", modname); } @@ -506,7 +445,7 @@ load_preloaded(void) if ((code = sys_preload_begin(&preload_p[i])) == 0) erl_exit(1, "Failed to find preloaded code for module %s\n", name); - res = erts_load_module(NULL, 0, NIL, &module_name, code, length); + res = erts_preload_module(NULL, 0, NIL, &module_name, code, length); sys_preload_end(&preload_p[i]); if (res != NIL) erl_exit(1,"Failed loading preloaded module %s (%T)\n", @@ -566,10 +505,14 @@ void erts_usage(void) erts_fprintf(stderr, "-rg amount set reader groups limit\n"); erts_fprintf(stderr, "-sbt type set scheduler bind type, valid types are:\n"); erts_fprintf(stderr, " u|ns|ts|ps|s|nnts|nnps|tnnps|db\n"); + erts_fprintf(stderr, "-sbwt val set scheduler busy wait threshold, valid values are:\n"); + erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n"); erts_fprintf(stderr, "-scl bool enable/disable compaction of scheduler load,\n"); erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); erts_fprintf(stderr, "-sct cput set cpu topology,\n"); erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); + erts_fprintf(stderr, "-sws val set scheduler wakeup strategy, valid values are:\n"); + erts_fprintf(stderr, " default|legacy|proposal.\n"); erts_fprintf(stderr, "-swt val set scheduler wakeup threshold, valid values are:\n"); erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n"); erts_fprintf(stderr, "-sss size suggested stack size in kilo words for scheduler threads,\n"); @@ -846,6 +789,10 @@ early_init(int *argc, char **argv) /* } } +#ifndef USE_THREADS + erts_async_max_threads = 0; +#endif + #ifdef ERTS_SMP no_schedulers = schdlrs; no_schedulers_online = schdlrs_onln; @@ -1047,7 +994,6 @@ erl_start(int argc, char **argv) switch (*ch) { case 's': verbose |= DEBUG_SYSTEM; break; case 'g': verbose |= DEBUG_PRIVATE_GC; break; - case 'h': verbose |= DEBUG_HYBRID_GC; break; case 'M': verbose |= DEBUG_MEMORY; break; case 'a': verbose |= DEBUG_ALLOCATION; break; case 't': verbose |= DEBUG_THREADS; break; @@ -1060,7 +1006,6 @@ erl_start(int argc, char **argv) erts_printf("Verbose level: "); if (verbose & DEBUG_SYSTEM) erts_printf("SYSTEM "); if (verbose & DEBUG_PRIVATE_GC) erts_printf("PRIVATE_GC "); - if (verbose & DEBUG_HYBRID_GC) erts_printf("HYBRID_GC "); if (verbose & DEBUG_MEMORY) erts_printf("PARANOID_MEMORY "); if (verbose & DEBUG_ALLOCATION) erts_printf("ALLOCATION "); if (verbose & DEBUG_THREADS) erts_printf("THREADS "); @@ -1088,12 +1033,6 @@ erl_start(int argc, char **argv) #ifdef HIPE strcat(tmp, ",HIPE"); #endif -#ifdef INCREMENTAL - strcat(tmp, ",INCREMENTAL_GC"); -#endif -#ifdef HYBRID - strcat(tmp, ",HYBRID"); -#endif erts_fprintf(stderr, "Erlang "); if (tmp[1]) { erts_fprintf(stderr, "(%s) ", tmp+1); @@ -1254,6 +1193,16 @@ erl_start(int argc, char **argv) erts_usage(); } } + else if (has_prefix("bwt", sub_param)) { + arg = get_arg(sub_param+3, argv[i+1], &i); + if (erts_sched_set_busy_wait_threshold(arg) != 0) { + erts_fprintf(stderr, "bad scheduler busy wait threshold: %s\n", + arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("scheduler wakup threshold: %s\n", arg)); + } else if (has_prefix("cl", sub_param)) { arg = get_arg(sub_param+2, argv[i+1], &i); if (sys_strcmp("true", arg) == 0) @@ -1314,13 +1263,23 @@ erl_start(int argc, char **argv) erts_use_sender_punish = 0; else if (sys_strcmp("wt", sub_param) == 0) { arg = get_arg(sub_param+2, argv[i+1], &i); - if (erts_sched_set_wakeup_limit(arg) != 0) { + if (erts_sched_set_wakeup_other_thresold(arg) != 0) { erts_fprintf(stderr, "scheduler wakeup threshold: %s\n", arg); erts_usage(); } VERBOSE(DEBUG_SYSTEM, - ("scheduler wakup threshold: %s\n", arg)); + ("scheduler wakeup threshold: %s\n", arg)); + } + else if (sys_strcmp("ws", sub_param) == 0) { + arg = get_arg(sub_param+2, argv[i+1], &i); + if (erts_sched_set_wakeup_other_type(arg) != 0) { + erts_fprintf(stderr, "scheduler wakeup strategy: %s\n", + arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("scheduler wakeup threshold: %s\n", arg)); } else if (has_prefix("ss", sub_param)) { /* suggested stack size (Kilo Words) for scheduler threads */ @@ -1525,8 +1484,9 @@ erl_start(int argc, char **argv) erl_init(ncpu); - init_shared_memory(boot_argc, boot_argv); load_preloaded(); + erts_end_staging_code_ix(); + erts_commit_staging_code_ix(); erts_initialized = 1; @@ -1612,32 +1572,6 @@ system_cleanup(int flush_async) #endif #endif -#ifdef HYBRID - if (ma_src_stack) erts_free(ERTS_ALC_T_OBJECT_STACK, - (void *)ma_src_stack); - if (ma_dst_stack) erts_free(ERTS_ALC_T_OBJECT_STACK, - (void *)ma_dst_stack); - if (ma_offset_stack) erts_free(ERTS_ALC_T_OBJECT_STACK, - (void *)ma_offset_stack); - ma_src_stack = NULL; - ma_dst_stack = NULL; - ma_offset_stack = NULL; - erts_cleanup_offheap(&erts_global_offheap); -#endif - -#if defined(HYBRID) && !defined(INCREMENTAL) - if (global_heap) { - ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, - (void*) global_heap, - sizeof(Eterm) * global_heap_sz); - } - global_heap = NULL; -#endif - -#ifdef INCREMENTAL - erts_cleanup_incgc(); -#endif - erts_exit_flush_async(); } diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 175695e856..11fed4079d 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -83,6 +83,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "bif_timers", NULL }, { "reg_tab", NULL }, { "proc_main", "pid" }, + { "old_code", "address" }, #ifdef HIPE { "hipe_mfait_lock", NULL }, #endif @@ -92,6 +93,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "proc_msgq", "pid" }, { "dist_entry", "address" }, { "dist_entry_links", "address" }, + { "code_ix_queue", NULL }, { "proc_status", "pid" }, { "proc_tab", NULL }, { "ports_snapshot", NULL }, @@ -175,9 +177,6 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "sched_stat", NULL }, #endif { "async_init_mtx", NULL }, -#ifdef ERTS_SMP - { "proc_lck_qs_alloc", NULL }, -#endif #ifdef __WIN32__ #ifdef DEBUG { "save_ops_lock", NULL }, diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index a36c53560e..741c0cb08e 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -49,7 +49,7 @@ const char *str_undefined = "undefined"; static ethr_tsd_key lcnt_thr_data_key; static int lcnt_n_thr; -static erts_lcnt_thread_data_t *lcnt_thread_data[1024]; +static erts_lcnt_thread_data_t *lcnt_thread_data[4096]; /* local functions */ @@ -240,7 +240,7 @@ void erts_lcnt_init() { lcnt_lock(); - erts_lcnt_rt_options = ERTS_LCNT_OPT_PROCLOCK; + erts_lcnt_rt_options = ERTS_LCNT_OPT_PROCLOCK | ERTS_LCNT_OPT_LOCATION; eltd = lcnt_thread_data_alloc(); @@ -312,7 +312,7 @@ void erts_lcnt_list_insert(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) } void erts_lcnt_list_delete(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock) { - + if (lock->next) lock->next->prev = lock->prev; if (lock->prev) lock->prev->next = lock->next; if (list->head == lock) list->head = lock->next; @@ -334,6 +334,10 @@ void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag ) { } void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id) { int i; + if (!name) { + lock->flag = 0; + return; + } lcnt_lock(); lock->next = NULL; @@ -363,6 +367,8 @@ void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eter void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) { erts_lcnt_lock_t *deleted_lock; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; + lcnt_lock(); if (erts_lcnt_rt_options & ERTS_LCNT_OPT_COPYSAVE) { @@ -378,6 +384,7 @@ void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) { } /* delete original */ erts_lcnt_list_delete(erts_lcnt_data->current_locks, lock); + lock->flag = 0; lcnt_unlock(); } @@ -389,6 +396,7 @@ void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option) { erts_lcnt_thread_data_t *eltd; if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; eltd = lcnt_get_thread_data(); @@ -422,6 +430,7 @@ void erts_lcnt_lock(erts_lcnt_lock_t *lock) { erts_lcnt_thread_data_t *eltd; if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; w_state = ethr_atomic_read(&lock->w_state); ethr_atomic_inc( &lock->w_state); @@ -452,6 +461,7 @@ void erts_lcnt_lock_unaquire(erts_lcnt_lock_t *lock) { /* should check if this thread was "waiting" */ if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; ethr_atomic_dec( &lock->w_state); } @@ -475,6 +485,7 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; #ifdef DEBUG if (!(lock->flag & (ERTS_LCNT_LT_RWMUTEX | ERTS_LCNT_LT_RWSPINLOCK))) { @@ -489,9 +500,13 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line ASSERT(eltd); /* if lock was in conflict, time it */ - - stats = lcnt_get_lock_stats(lock, file, line); + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_LOCATION) { + stats = lcnt_get_lock_stats(lock, file, line); + } else { + stats = &lock->stats[0]; + } + if (eltd->timer_set) { lcnt_time(&timer); @@ -510,6 +525,7 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line void erts_lcnt_unlock_opt(erts_lcnt_lock_t *lock, Uint16 option) { if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_dec(&lock->w_state); if (option & ERTS_LCNT_LO_READ ) ethr_atomic_dec(&lock->r_state); } @@ -520,6 +536,7 @@ void erts_lcnt_unlock(erts_lcnt_lock_t *lock) { erts_aint_t flowstate; #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; #ifdef DEBUG /* flowstate */ flowstate = ethr_atomic_read(&lock->flowstate); @@ -537,6 +554,7 @@ void erts_lcnt_unlock(erts_lcnt_lock_t *lock) { void erts_lcnt_trylock_opt(erts_lcnt_lock_t *lock, int res, Uint16 option) { if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; /* Determine lock_state via res instead of state */ if (res != EBUSY) { if (option & ERTS_LCNT_LO_WRITE) ethr_atomic_inc(&lock->w_state); @@ -555,6 +573,7 @@ void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res) { erts_aint_t flowstate; #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; + if (!ERTS_LCNT_LOCK_TYPE(lock)) return; if (res != EBUSY) { #ifdef DEBUG diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h index 6306580ae4..690551c71f 100644 --- a/erts/emulator/beam/erl_lock_count.h +++ b/erts/emulator/beam/erl_lock_count.h @@ -89,6 +89,7 @@ #define ERTS_LCNT_OPT_LOCATION (((Uint16) 1) << 1) #define ERTS_LCNT_OPT_PROCLOCK (((Uint16) 1) << 2) #define ERTS_LCNT_OPT_COPYSAVE (((Uint16) 1) << 3) +#define ERTS_LCNT_OPT_PORTLOCK (((Uint16) 1) << 4) typedef struct { unsigned long s; @@ -201,5 +202,7 @@ void erts_lcnt_clear_counters(void); char *erts_lcnt_lock_type(Uint16 type); erts_lcnt_data_t *erts_lcnt_get_data(void); +#define ERTS_LCNT_LOCK_TYPE(lockp) ((lockp)->flag & ERTS_LCNT_LT_ALL) + #endif /* ifdef ERTS_ENABLE_LOCK_COUNT */ #endif /* ifndef ERTS_LOCK_COUNT_H__ */ diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 64065bafb8..531144bc4f 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -29,7 +29,6 @@ #include "global.h" #include "erl_message.h" #include "erl_process.h" -#include "erl_nmgc.h" #include "erl_binary.h" #include "dtrace-wrapper.h" @@ -602,9 +601,7 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg) #endif #ifdef HARD_DEBUG - ProcBin *dbg_mso_start = off_heap->mso; - ErlFunThing *dbg_fun_start = off_heap->funs; - ExternalThing *dbg_external_start = off_heap->externals; + struct erl_off_heap_header* dbg_oh_start = off_heap->first; Eterm dbg_term, dbg_token; ErlHeapFragment *dbg_bp; Uint *dbg_hp, *dbg_thp_start; @@ -778,48 +775,16 @@ copy_done: int i, j; ErlHeapFragment* frag; { - ProcBin *mso = off_heap->mso; + struct erl_off_heap_header* dbg_oh = off_heap->first; i = j = 0; - while (mso != dbg_mso_start) { - mso = mso->next; + while (dbg_oh != dbg_oh_start) { + dbg_oh = dbg_oh->next; i++; } for (frag=bp; frag; frag=frag->next) { - mso = frag->off_heap.mso; - while (mso) { - mso = mso->next; - j++; - } - } - ASSERT(i == j); - } - { - ErlFunThing *fun = off_heap->funs; - i = j = 0; - while (fun != dbg_fun_start) { - fun = fun->next; - i++; - } - for (frag=bp; frag; frag=frag->next) { - fun = frag->off_heap.funs; - while (fun) { - fun = fun->next; - j++; - } - } - ASSERT(i == j); - } - { - ExternalThing *external = off_heap->externals; - i = j = 0; - while (external != dbg_external_start) { - external = external->next; - i++; - } - for (frag=bp; frag; frag=frag->next) { - external = frag->off_heap.externals; - while (external) { - external = external->next; + dbg_oh = frag->off_heap.first; + while (dbg_oh) { + dbg_oh = dbg_oh->next; j++; } } @@ -929,8 +894,8 @@ erts_send_message(Process* sender, #ifdef USE_VM_PROBES *sender_name = *receiver_name = '\0'; if (DTRACE_ENABLED(message_send)) { - erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id); - erts_snprintf(receiver_name, sizeof(receiver_name), "%T", receiver->id); + erts_snprintf(sender_name, sizeof(DTRACE_CHARBUF_NAME(sender_name)), "%T", sender->id); + erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(receiver_name)), "%T", receiver->id); } #endif if (SEQ_TRACE_TOKEN(sender) != NIL && !(flags & ERTS_SND_FLG_NO_SEQ_TRACE)) { @@ -1013,54 +978,6 @@ erts_send_message(Process* sender, #endif ); BM_SWAP_TIMER(send,system); -#ifdef HYBRID - } else { - ErlMessage* mp = message_alloc(); - BM_SWAP_TIMER(send,copy); -#ifdef INCREMENTAL - /* TODO: During GC activate processes if the message relies in - * the fromspace and the sender is active. During major - * collections add the message to the gray stack if it relies - * in the old generation and the sender is active and the - * receiver is inactive. - - if (!IS_CONST(message) && (ma_gc_flags & GC_CYCLE) && - (ptr_val(message) >= inc_fromspc && - ptr_val(message) < inc_fromend) && INC_IS_ACTIVE(sender)) - INC_ACTIVATE(receiver); - else if (!IS_CONST(message) && (ma_gc_flags & GC_CYCLE) && - (ptr_val(message) >= global_old_heap && - ptr_val(message) < global_old_hend) && - INC_IS_ACTIVE(sender) && !INC_IS_ACTIVE(receiver)) - Mark message in blackmap and add it to the gray stack - */ - - if (!IS_CONST(message)) - INC_ACTIVATE(receiver); -#endif - LAZY_COPY(sender,message); - BM_SWAP_TIMER(copy,send); - DTRACE6(message_send, sender_name, receiver_name, - size_object(message)msize, tok_label, tok_lastcnt, tok_serial); - ERL_MESSAGE_TERM(mp) = message; - ERL_MESSAGE_TOKEN(mp) = NIL; -#ifdef USE_VM_PROBES - ERL_MESSAGE_DT_UTAG(mp) = NIL; -#endif - mp->next = NULL; - LINK_MESSAGE(receiver, mp); - ACTIVATE(receiver); - - res = receiver->msg.len; - - erts_proc_notify_new_message(receiver); - - if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) { - trace_receive(receiver, message); - } - - BM_SWAP_TIMER(send,system); -#else } else if (sender == receiver) { /* Drop message if receiver has a pending exit ... */ #ifdef ERTS_SMP @@ -1179,7 +1096,7 @@ erts_send_message(Process* sender, } BM_SWAP_TIMER(send,system); #endif /* #ifndef ERTS_SMP */ -#endif /* HYBRID */ + return; } return res; } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 6d0975f41f..594c51a5db 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1524,6 +1524,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) if (len < 0) { BIF_ERROR(BIF_P, BADARG); } + lib_name = (char *) erts_alloc(ERTS_ALC_T_TMP, len + 1); if (intlist_to_buf(BIF_ARG_1, lib_name, len) != len) { @@ -1532,6 +1533,12 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) } lib_name[len] = '\0'; + if (!erts_try_seize_code_write_permission(BIF_P)) { + erts_free(ERTS_ALC_T_TMP, lib_name); + ERTS_BIF_YIELD2(bif_export[BIF_load_nif_2], + BIF_P, BIF_ARG_1, BIF_ARG_2); + } + /* Block system (is this the right place to do it?) */ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_smp_thr_progress_block(); @@ -1545,11 +1552,11 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ASSERT(caller != NULL); mod_atom = caller[0]; ASSERT(is_atom(mod_atom)); - mod=erts_get_module(mod_atom); + mod=erts_get_module(mod_atom, erts_active_code_ix()); ASSERT(mod != NULL); - if (!in_area(caller, mod->code, mod->code_length)) { - ASSERT(in_area(caller, mod->old_code, mod->old_code_length)); + if (!in_area(caller, mod->curr.code, mod->curr.code_length)) { + ASSERT(in_area(caller, mod->old.code, mod->old.code_length)); ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old " "module '%T' not allowed", mod_atom); @@ -1595,7 +1602,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) BeamInstr** code_pp; ErlNifFunc* f = &entry->funcs[i]; if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom) - || (code_pp = get_func_pp(mod->code, f_atom, f->arity))==NULL) { + || (code_pp = get_func_pp(mod->curr.code, f_atom, f->arity))==NULL) { ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u", mod_atom, f->name, f->arity); } @@ -1624,18 +1631,18 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) erts_refc_init(&lib->rt_dtor_cnt, 0); lib->mod = mod; env.mod_nif = lib; - if (mod->nif != NULL) { /* Reload */ + if (mod->curr.nif != NULL) { /* Reload */ int k; - lib->priv_data = mod->nif->priv_data; + lib->priv_data = mod->curr.nif->priv_data; - ASSERT(mod->nif->entry != NULL); + ASSERT(mod->curr.nif->entry != NULL); if (entry->reload == NULL) { ret = load_nif_error(BIF_P,reload,"Reload not supported by this NIF library."); goto error; } /* Check that no NIF is removed */ - for (k=0; k < mod->nif->entry->num_of_funcs; k++) { - ErlNifFunc* old_func = &mod->nif->entry->funcs[k]; + for (k=0; k < mod->curr.nif->entry->num_of_funcs; k++) { + ErlNifFunc* old_func = &mod->curr.nif->entry->funcs[k]; for (i=0; i < entry->num_of_funcs; i++) { if (old_func->arity == entry->funcs[i].arity && sys_strcmp(old_func->name, entry->funcs[i].name) == 0) { @@ -1656,24 +1663,24 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful."); } else { - mod->nif->entry = NULL; /* to prevent 'unload' callback */ - erts_unload_nif(mod->nif); + mod->curr.nif->entry = NULL; /* to prevent 'unload' callback */ + erts_unload_nif(mod->curr.nif); reload_warning = 1; } } else { lib->priv_data = NULL; - if (mod->old_nif != NULL) { /* Upgrade */ - void* prev_old_data = mod->old_nif->priv_data; + if (mod->old.nif != NULL) { /* Upgrade */ + void* prev_old_data = mod->old.nif->priv_data; if (entry->upgrade == NULL) { ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library."); goto error; } erts_pre_nif(&env, BIF_P, lib); - veto = entry->upgrade(&env, &lib->priv_data, &mod->old_nif->priv_data, BIF_ARG_2); + veto = entry->upgrade(&env, &lib->priv_data, &mod->old.nif->priv_data, BIF_ARG_2); erts_post_nif(&env); if (veto) { - mod->old_nif->priv_data = prev_old_data; + mod->old.nif->priv_data = prev_old_data; ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful."); } /*else if (mod->old_nif->priv_data != prev_old_data) { @@ -1693,20 +1700,21 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) /* ** Everything ok, patch the beam code with op_call_nif */ - mod->nif = lib; + mod->curr.nif = lib; for (i=0; i < entry->num_of_funcs; i++) { BeamInstr* code_ptr; erts_atom_get(entry->funcs[i].name, sys_strlen(entry->funcs[i].name), &f_atom); - code_ptr = *get_func_pp(mod->code, f_atom, entry->funcs[i].arity); + code_ptr = *get_func_pp(mod->curr.code, f_atom, entry->funcs[i].arity); if (code_ptr[1] == 0) { code_ptr[5+0] = (BeamInstr) BeamOp(op_call_nif); } else { /* Function traced, patch the original instruction word */ - BpData** bps = (BpData**) code_ptr[1]; - BpData* bp = (BpData*) bps[erts_bp_sched2ix()]; - bp->orig_instr = (BeamInstr) BeamOp(op_call_nif); + GenericBp* g = (GenericBp *) code_ptr[1]; + ASSERT(code_ptr[5+0] == + (BeamInstr) BeamOp(op_i_generic_breakpoint)); + g->orig_instr = (BeamInstr) BeamOp(op_call_nif); } code_ptr[5+1] = (BeamInstr) entry->funcs[i].fptr; code_ptr[5+2] = (BeamInstr) lib; @@ -1726,6 +1734,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) erts_smp_thr_progress_unblock(); erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_release_code_write_permission(); erts_free(ERTS_ALC_T_TMP, lib_name); if (reload_warning) { diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index e5d99dc4f1..3fc7b2c9c2 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -187,11 +187,7 @@ extern TWinDynNifCallbacks WinDynNifCallbacks; #else # define ERL_NIF_INIT_GLOB # define ERL_NIF_INIT_BODY -# if defined(VXWORKS) -# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _init(void) -# else -# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void) -# endif +# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* nif_init(void) #endif diff --git a/erts/emulator/beam/erl_nmgc.c b/erts/emulator/beam/erl_nmgc.c deleted file mode 100644 index 2a8c819360..0000000000 --- a/erts/emulator/beam/erl_nmgc.c +++ /dev/null @@ -1,1401 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2004-2011. All Rights Reserved. - * - * The contents of this file are subject to the Erlang Public License, - * Version 1.1, (the "License"); you may not use this file except in - * compliance with the License. You should have received a copy of the - * Erlang Public License along with this software. If not, it can be - * retrieved online at http://www.erlang.org/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and limitations - * under the License. - * - * %CopyrightEnd% - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include "global.h" -#include "erl_gc.h" -#include "erl_binary.h" -#include "erl_nmgc.h" -#include "erl_debug.h" -#if HIPE -#include "hipe_stack.h" -#endif - - -#ifdef INCREMENTAL -/*************************************************************************** - * * - * Incremental Garbage Collector for the Message Area * - * * - ***************************************************************************/ - -/* - * The heap pointers are declared in erl_init.c - * global_heap is the nursery - * global_old_heap is the old generation - */ -unsigned char *blackmap = NULL; -INC_Page *inc_used_mem = NULL; -INC_MemBlock *inc_free_list = NULL; -Eterm *inc_fromspc; -Eterm *inc_fromend; -Eterm *inc_nursery_scn_ptr; -Eterm **fwdptrs; -Eterm *inc_alloc_limit; -Process *inc_active_proc; -Process *inc_active_last; -int inc_words_to_go; - -static Eterm *inc_last_nursery; -static int inc_pages = INC_NoPAGES; -static INC_Page *inc_bibop = NULL; -static int inc_used_pages; - -/* Used when growing the old generation */ -/* -#define INC_ROOTSAVE 16384 -static Eterm *root_save[INC_ROOTSAVE]; -static int roots_saved = 0; -*/ - -INC_STORAGE_DECLARATION(,gray); - -static void inc_minor_gc(Process *p, int need, Eterm* objv, int nobj); -static void inc_major_gc(Process *p, int need, Eterm* objv, int nobj); - -#ifdef INC_TIME_BASED -#if USE_PERFCTR - -/* - * This uses the Linux perfctr extension to virtualise the - * time-stamp counter. - */ -#include "libperfctr.h" -static struct vperfctr *vperfctr; -static double cpu_khz; -static double tsc_to_cpu_mult; - -static void inc_start_hrvtime(void) -{ - struct perfctr_info info; - struct vperfctr_control control; - - if( vperfctr != NULL ) - return; - vperfctr = vperfctr_open(); - if( vperfctr == NULL ) - return; - if( vperfctr_info(vperfctr, &info) >= 0 ) { - cpu_khz = (double)info.cpu_khz; - tsc_to_cpu_mult = (double)(info.tsc_to_cpu_mult ? : 1); - if( info.cpu_features & PERFCTR_FEATURE_RDTSC ) { - memset(&control, 0, sizeof control); - control.cpu_control.tsc_on = 1; - if( vperfctr_control(vperfctr, &control) >= 0 ) - return; - } - } - vperfctr_close(vperfctr); - vperfctr = NULL; -} - -#define inc_get_hrvtime() (((double)vperfctr_read_tsc(vperfctr) * tsc_to_cpu_mult) / cpu_khz) - -#endif /* USE_PERFCTR */ -#endif /* INC_TIME_BASED */ - -#ifdef INC_TIME_BASED -# define timeslice 1 /* milli seconds */ -# define WORK_MORE (inc_get_hrvtime() < start_time + timeslice) -#else -//# define inc_min_work 100 /* words */ -# define inc_min_work global_heap_sz + inc_pages * INC_FULLPAGE /* words */ -# define WORK_MORE (inc_words_to_go > 0) -#endif - -void erts_init_incgc(void) -{ - int i; - int size = inc_pages * INC_FULLPAGE; - - /* Young generation */ - global_heap = (Eterm *)erts_alloc(ERTS_ALC_T_MESSAGE_AREA, - sizeof(Eterm) * global_heap_sz); - global_hend = global_heap + global_heap_sz; - global_htop = global_heap; - inc_alloc_limit = global_hend; - - /* Fromspace */ - inc_last_nursery = (Eterm *) erts_alloc(ERTS_ALC_T_MESSAGE_AREA, - global_heap_sz * sizeof(Eterm)); - inc_fromspc = inc_fromend = NULL; - - /* Forward-pointers */ - fwdptrs = erts_alloc(ERTS_ALC_T_MESSAGE_AREA, - global_heap_sz * sizeof(Eterm*)); - /* Old generation */ - global_old_heap = (Eterm *)erts_alloc(ERTS_ALC_T_MESSAGE_AREA, - size * sizeof(Eterm)); - global_old_hend = global_old_heap + size; - - /* Pages i BiBOP */ - for (i = 0; i < inc_pages; i++) - { - INC_Page *this = (INC_Page*)(global_old_heap + i * INC_FULLPAGE); - this->next = (INC_Page*)((Eterm*)this + INC_FULLPAGE); - } - - inc_bibop = (INC_Page*)global_old_heap; - ((INC_Page*)(global_old_heap + (inc_pages - 1) * INC_FULLPAGE))->next = - NULL; - - inc_used_mem = inc_bibop; - inc_bibop = inc_bibop->next; - inc_used_mem->next = NULL; - inc_used_pages = 1; - - /* Free-list */ - inc_free_list = (INC_MemBlock*)inc_used_mem->start; - inc_free_list->size = INC_PAGESIZE; - inc_free_list->prev = NULL; - inc_free_list->next = NULL; - - /* Blackmap */ - blackmap = (unsigned char*)erts_alloc(ERTS_ALC_T_MESSAGE_AREA, - INC_FULLPAGE * inc_pages); - /* Gray stack */ - INC_STORAGE_INIT(gray); - - inc_active_proc = NULL; - inc_active_last = NULL; - -#ifdef INC_TIME_BASED - inc_start_hrvtime(); -#endif -} - -void erts_cleanup_incgc(void) -{ - INC_STORAGE_ERASE(gray); - - if (inc_fromspc) - inc_last_nursery = inc_fromspc; - - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)global_heap); - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)inc_last_nursery); - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)global_old_heap); - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)blackmap); - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)fwdptrs); -} - -void erts_incremental_gc(Process* p, int need, Eterm* objv, int nobj) -{ - int repeat_minor; -#ifdef INC_TIME_BASED - double start_time = inc_get_hrvtime(); - int work_left_before = inc_words_to_go; -#endif - /* Used when growing the fromspace */ - static char inc_growing_nurs = 0; - - BM_STOP_TIMER(system); - //BM_MMU_READ(); - BM_RESET_TIMER(gc); - BM_START_TIMER(gc); - - VERBOSE(DEBUG_HYBRID_GC, - ("INCGC: Incremental GC START Caused by: %T Need: %d\n", - p->id,need)); - - ma_gc_flags |= GC_GLOBAL; - ma_gc_flags &= ~GC_CYCLE_START; - -#ifndef INC_TIME_BASED - /* Decide how much work to do this GC stage. The work is meassured - * in number of words copied from the young generation to the old - * plus number of work marked in the old generation. - */ - if (ma_gc_flags & GC_MAJOR) { - int wm = (need > inc_min_work) ? need : inc_min_work; - inc_words_to_go = (int)((wm * (((inc_used_pages * INC_PAGESIZE) / - (double)global_heap_sz) + 1)) + 0.5); - } - else - inc_words_to_go = (need > inc_min_work) ? need : inc_min_work; -#endif - - do { - if (ma_gc_flags & GC_MAJOR) { - /* This is a major collection cycle. */ - inc_major_gc(p,need,objv,nobj); - } else if (ma_gc_flags & GC_CYCLE) { - /* This is a minor collection cycle. */ - inc_minor_gc(p,need,objv,nobj); - } else { - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Collection cycle START\n")); - ma_gc_flags |= (GC_CYCLE | GC_CYCLE_START); - inc_fromspc = global_heap; - inc_fromend = global_htop; - global_heap = global_htop = inc_last_nursery; - global_hend = global_heap + global_heap_sz; - inc_nursery_scn_ptr = global_heap; -#ifdef INC_TIME_BASED - work_left_before = inc_words_to_go = global_heap_sz; -#endif -#ifdef DEBUG - inc_last_nursery = NULL; -#endif - memset(fwdptrs,0,global_heap_sz * sizeof(Eterm)); - - { - /* TODO: Alla processer ska v�l egentligen inte aktiveras h�r... */ - int i; - for (i = 0; i < erts_num_active_procs; i++) { - Process *cp = erts_active_procs[i]; - INC_ACTIVATE(cp); - cp->scan_top = cp->high_water; - } - } - - if (ma_gc_flags & GC_NEED_MAJOR) { - /* The previous collection cycle caused the old generation to - * overflow. This collection cycle will therefore be a major - * one. - */ - BM_COUNT(major_gc_cycles); - VERBOSE(DEBUG_HYBRID_GC,("INCGC: MAJOR cycle\n")); - inc_major_gc(p,need,objv,nobj); - } else { - BM_COUNT(minor_gc_cycles); - VERBOSE(DEBUG_HYBRID_GC,("INCGC: MINOR cycle\n")); - inc_minor_gc(p,need,objv,nobj); - } - } - - repeat_minor = 0; - if (!(ma_gc_flags & GC_CYCLE)) { - inc_alloc_limit = global_hend; - inc_last_nursery = inc_fromspc; - inc_fromspc = inc_fromend = NULL; - ASSERT(INC_STORAGE_EMPTY(gray)); - - if (inc_growing_nurs) { - /* - * The previous collection cycle caused the nursery to - * grow, now we have to grow the from-space as well. - */ - inc_last_nursery = - (Eterm*) erts_realloc(ERTS_ALC_T_MESSAGE_AREA, - (void*)inc_last_nursery, - sizeof(Eterm) * global_heap_sz); - inc_growing_nurs = 0; - } - - if (global_hend - global_htop <= need) { - /* - * Initiate a new GC cycle immediately and, if necessary, - * enlarge the nursery. - */ - if (global_heap_sz <= need) { - VERBOSE(DEBUG_HYBRID_GC, - ("INCGC: Allocating a larger nursery\n")); - global_heap_sz = erts_next_heap_size(need * 1.5,0); - inc_last_nursery = - (Eterm*) erts_realloc(ERTS_ALC_T_MESSAGE_AREA, - (void*)inc_last_nursery, - sizeof(Eterm) * global_heap_sz); - fwdptrs = erts_realloc(ERTS_ALC_T_MESSAGE_AREA,fwdptrs, - global_heap_sz * sizeof(Eterm*)); - inc_growing_nurs = 1; - } - repeat_minor = 1; - } - -#ifdef DEBUG - /* Fill the from-space with bad things */ - memset(inc_last_nursery,DEBUG_BAD_BYTE, - global_heap_sz * sizeof(Eterm)); -#endif - } - } while (repeat_minor); - - - /* Clean up after garbage collection ********************************/ - - if (inc_alloc_limit != global_hend) { - -#ifdef INC_TIME_BASED - if ((work_left_before - inc_words_to_go) == 0) { - inc_alloc_limit = global_htop + need; - } else { - inc_alloc_limit = (global_hend - global_htop) / - (inc_words_to_go / (work_left_before - inc_words_to_go)) + - global_htop; - if (inc_alloc_limit > global_hend) - inc_alloc_limit = global_hend; - } -#else - inc_alloc_limit = (Eterm*)(global_htop + (need > inc_min_work) ? - need : inc_min_work); - if (inc_alloc_limit > global_hend) - inc_alloc_limit = global_hend; -#endif - } - - ma_gc_flags &= ~GC_GLOBAL; - - /* INC_TIME_BASED: If this fails we have to increase the timeslice! */ - ASSERT(inc_alloc_limit - global_htop > need); - - BM_STOP_TIMER(gc); -#ifdef BM_TIMERS - minor_global_gc_time += gc_time; - if (gc_time > max_global_minor_time) - max_global_minor_time = gc_time; - - pause_times[(((gc_time * 1000) < MAX_PAUSE_TIME) ? - (int)(gc_time * 1000) : - MAX_PAUSE_TIME - 1)]++; -#endif - //BM_MMU_INIT(); - { static long long verif = 0; - //erts_printf("innan verify: %d\n",++verif); - if (verif==168) print_memory(NULL); - verify_everything(); - //erts_printf("efter verify: %d\n",verif); - } - BM_START_TIMER(system); - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Incremental GC END\n")); -} - - -/*************************************************************************** - * * - * Minor collection - Copy live data from young generation to old * - * * - ***************************************************************************/ - -#define MINOR_SCAN(PTR,END) do { \ - ASSERT(PTR <= END); \ - while (WORK_MORE && PTR < END) { \ - Eterm val = *PTR; \ - Eterm *obj_ptr = ptr_val(val); \ - switch (primary_tag(val)) { \ - case TAG_PRIMARY_LIST: \ - if (ptr_within(obj_ptr,inc_fromspc,inc_fromend)) { \ - if (INC_IS_FORWARDED(obj_ptr)) { \ - *PTR = make_list(INC_FORWARD_VALUE(obj_ptr)); \ - } \ - else { \ - Eterm *hp = erts_inc_alloc(2); \ - INC_STORE(gray,hp,2); \ - INC_COPY_CONS(obj_ptr,hp,PTR); \ - } \ - } \ - break; \ - case TAG_PRIMARY_BOXED: \ - if (ptr_within(obj_ptr,inc_fromspc,inc_fromend)) { \ - if (INC_IS_FORWARDED(obj_ptr)) { \ - *PTR = make_boxed(INC_FORWARD_VALUE(obj_ptr)); \ - } \ - else { \ - Eterm *hp = erts_inc_alloc(BOXED_NEED(obj_ptr,*obj_ptr)); \ - INC_STORE(gray,hp,BOXED_NEED(obj_ptr,*obj_ptr)); \ - INC_COPY_BOXED(obj_ptr,hp,PTR); \ - } \ - } \ - break; \ - case TAG_PRIMARY_HEADER: \ - switch (val & _TAG_HEADER_MASK) { \ - case ARITYVAL_SUBTAG: break; \ - default: PTR += thing_arityval(val); break; \ - } \ - break; \ - } \ - PTR++; \ - } \ -} while(0) - - -/* Returns: TRUE (1) if the need is greater than the available space - * and the garbage collector needs to be restarted immediately. FALSE - * (0) otherwise. - */ -static void inc_minor_gc(Process* p, int need, Eterm* objv, int nobj) -{ - BM_COUNT(minor_gc_stages); - - /* Start with looking at gray objects found in earlier collection - * stages. - */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Rescue gray found from nursery\n")); - { - INC_Object *obj = NULL; - Eterm *ptr; - - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - MINOR_SCAN(ptr,obj->this + obj->size); - } - /* TODO: Se f�reg�ende uppdatering av gr� objekt */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Scan root-set\n")); - while (WORK_MORE && inc_active_proc) { - Rootset rootset; - Process *cp = inc_active_proc; - - ASSERT(INC_IS_ACTIVE(cp)); - - /* TODO: Hur dyrt �r det att bygga nytt rootset varje g�ng? */ - - /* TODO: Fundera p� ordningen! Rootset, Heap, Old heap... */ - - /* TODO: Scanna stacken fr�n p->send till p->stop! [Brooks84] */ - /* Notera: Vi GC:ar inte de yngsta objekten - de som allokeras - under GC-cykeln. Detta ger ynglingarna en chans att d� innan - GC:n b�rjar kopiera dem. [StefanovicMcKinleyMoss@OOPSLA99] */ - - /* TODO: N�r rootset �r scannat borde processen inte vara - aktiv mer. Den b�r aktiveras i schedule, endast om en - process har k�rt beh�ver vi scanna rootset igen. */ - - /* MT: In a multithreaded system the process cp needs to be - * locked here. - */ - - if (cp == p) - rootset.n = setup_rootset(cp, objv, nobj, &rootset); - else - rootset.n = setup_rootset(cp, cp->arg_reg, cp->arity, &rootset); - - //MA_GENSWEEP_NSTACK(cp, old_htop, n_htop, objv, nobj); - - while (WORK_MORE && rootset.n--) { - Eterm *g_ptr = rootset.v[rootset.n]; - Uint g_sz = rootset.sz[rootset.n]; - - while (WORK_MORE && g_sz--) { - Eterm gval = *g_ptr; - switch (primary_tag(gval)) { - case TAG_PRIMARY_LIST: { - Eterm *ptr = list_val(gval); - if (ptr_within(ptr,inc_fromspc,inc_fromend)) { - if (INC_IS_FORWARDED(ptr)) { - *g_ptr++ = make_list(INC_FORWARD_VALUE(ptr)); - } - else { - Eterm *hp = erts_inc_alloc(2); - INC_STORE(gray,hp,2); - INC_COPY_CONS(ptr,hp,g_ptr++); - } - } - else - ++g_ptr; - continue; - } - - case TAG_PRIMARY_BOXED: { - Eterm *ptr = boxed_val(gval); - if (ptr_within(ptr,inc_fromspc,inc_fromend)) { - if (INC_IS_FORWARDED(ptr)) { - *g_ptr++ = make_boxed(INC_FORWARD_VALUE(ptr)); - } - else { - Eterm *hp = erts_inc_alloc(BOXED_NEED(ptr,*ptr)); - INC_STORE(gray,hp,BOXED_NEED(ptr,*ptr)); - INC_COPY_BOXED(ptr,hp,g_ptr++); - } - } - else - ++g_ptr; - continue; - } - - default: - g_ptr++; - continue; - } - } - } - - restore_one_rootset(cp, &rootset); - - /* MT: cp can be unlocked now. */ - - /* VERBOSE(DEBUG_HYBRID_GC,("INCGC: Scan private nursery\n")); */ - if (cp->scan_top != HEAP_TOP(cp)) { - Eterm *ptr = cp->scan_top; - MINOR_SCAN(ptr,HEAP_TOP(cp)); - /* TODO: F�r att spara scan_top h�r m�ste alla ma-pekare - * som hittas l�ggas till i cp->rrma. - */ - //cp->scan_top = ptr; - } - - /* VERBOSE(DEBUG_HYBRID_GC,("INCGC: Scan heap fragments\n")); */ - { - ErlHeapFragment* bp = MBUF(cp); - - while (WORK_MORE && bp) { - Eterm *ptr = bp->mem; - if ((ARITH_HEAP(cp) >= bp->mem) && - (ARITH_HEAP(cp) < bp->mem + bp->size)) { - MINOR_SCAN(ptr,ARITH_HEAP(cp)); - } else { - MINOR_SCAN(ptr,bp->mem + bp->size); - } - bp = bp->next; - } - } - - /* VERBOSE(DEBUG_HYBRID_GC,("INCGC: Scan gray\n")); */ - { - INC_Object *obj = NULL; - Eterm *ptr; - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - MINOR_SCAN(ptr,obj->this + obj->size); - } - /* TODO: INC_STORE(gray,ptr,obj->size-(ptr-obj->this)); Typ.. */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - if (WORK_MORE) { - //printf("Rootset after:\r\n"); - //print_one_rootset(&rootset); - INC_DEACTIVATE(cp); - } - } - - /* Update new pointers in the nursery to new copies in old generation. */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Update nursery\n")); - { - Eterm *ptr = inc_nursery_scn_ptr; - MINOR_SCAN(ptr,global_htop); - inc_nursery_scn_ptr = ptr; - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Rescue gray found from nursery\n")); - { - INC_Object *obj = NULL; - Eterm *ptr; - - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - MINOR_SCAN(ptr,obj->this + obj->size); - } - /* TODO: Se f�reg�ende uppdatering av gr� objekt */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Update copy stack\n")); - { - Uint i; - for (i = 0; i < ma_dst_top; i++) { - if (ptr_within(ma_dst_stack[i],inc_fromspc,inc_fromend)) { - if (INC_IS_FORWARDED(ma_dst_stack[i])) - ma_dst_stack[i] = INC_FORWARD_VALUE(ma_dst_stack[i]); - } - } - } - - if (WORK_MORE) { - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Update offheap-lists\n")); - { - ExternalThing **prev = &erts_global_offheap.externals; - ExternalThing *ptr = erts_global_offheap.externals; - - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep proc externals\n")); - while (ptr) { - Eterm *ppt = (Eterm*) ptr; - - if (ptr_within(ppt,global_old_heap,global_old_hend)) { - prev = &ptr->next; - ptr = ptr->next; - } else if (ptr_within(ppt, inc_fromspc, inc_fromend) && - INC_IS_FORWARDED(ppt)) { - ExternalThing *ro = (ExternalThing*)INC_FORWARD_VALUE(ppt); - *prev = ro; /* Patch to moved pos */ - prev = &ro->next; - ptr = ro->next; - } else { - erts_deref_node_entry(ptr->node); - *prev = ptr = ptr->next; - } - } - ASSERT(*prev == NULL); - } - - { - ProcBin **prev = &erts_global_offheap.mso; - ProcBin *ptr = erts_global_offheap.mso; - - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep proc bins\n")); - while (ptr) { - Eterm *ppt = (Eterm*)ptr; - - if (ptr_within(ppt,global_old_heap,global_old_hend)) { - prev = &ptr->next; - ptr = ptr->next; - } else if (ptr_within(ppt, inc_fromspc, inc_fromend) && - INC_IS_FORWARDED(ppt)) { - ProcBin *ro = (ProcBin*)INC_FORWARD_VALUE(ppt); - *prev = ro; /* Patch to moved pos */ - prev = &ro->next; - ptr = ro->next; - } else { - Binary *bptr; - *prev = ptr->next; - bptr = ptr->val; - if (erts_refc_dectest(&bptr->refc, 0) == 0) - erts_bin_free(bptr); - ptr = *prev; - } - } - ASSERT(*prev == NULL); - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Minor collection cycle END\n")); - ma_gc_flags &= ~GC_CYCLE; - } -} - - - - -/*************************************************************************** - * * - * Major collection - CopyMark - Copy young to old, Mark-Sweep old * - * * - ***************************************************************************/ - -#define COPYMARK(PTR,END) do { \ - ASSERT(PTR <= END); \ - while (WORK_MORE && PTR < END) { \ - Eterm val = *PTR; \ - Eterm *obj_ptr = ptr_val(val); \ - switch (primary_tag(val)) { \ - case TAG_PRIMARY_LIST: \ - COPYMARK_CONS(obj_ptr,aging_htop,PTR,aging_end); break; \ - case TAG_PRIMARY_BOXED: \ - COPYMARK_BOXED(obj_ptr,aging_htop,PTR,aging_end); break; \ - case TAG_PRIMARY_HEADER: \ - switch (val & _TAG_HEADER_MASK) { \ - case ARITYVAL_SUBTAG: break; \ - default: \ - PTR += thing_arityval(val); \ - break; \ - } \ - break; \ - default: break; \ - } \ - PTR++; \ - } \ -} while(0); -/* TODO: - if (aging_htop + 10 > aging + INC_FULLPAGE) { - aging->next = inc_used_mem; - inc_used_mem = aging; - } -*/ - -static void inc_major_gc(Process *p, int need, Eterm* objv, int nobj) -{ - Eterm *free_start = NULL; - Uint live = 0; - Uint old_gen_sz = 0; - static INC_Page *aging; - static Eterm *aging_htop; - static Eterm *aging_end; - BM_NEW_TIMER(old_gc); - - BM_SWAP_TIMER(gc,old_gc); - BM_COUNT(major_gc_stages); - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Major collection START\n")); - - ma_gc_flags |= GC_INCLUDE_ALL; - - if (ma_gc_flags & GC_NEED_MAJOR) - { - INC_Page *page = inc_used_mem; - - ma_gc_flags |= GC_MAJOR; - ma_gc_flags &= ~GC_NEED_MAJOR; - - while (page) - { - memset(blackmap + - ((void*)page - (void*)global_old_heap) / sizeof(void*), - 0, INC_FULLPAGE); - page = page->next; - } - - if (inc_bibop) { - aging = inc_bibop; - inc_bibop = inc_bibop->next; - aging->next = NULL; - memset(blackmap + - ((void*)aging - (void*)global_old_heap) / sizeof(void*), - 1, INC_FULLPAGE); - aging_htop = aging->start; - aging_end = aging->start + INC_PAGESIZE; - } - else { - /* There are no free pages.. Either fragmentation is a - * problem or we are simply out of memory. Allocation in - * the old generation will be done through the free-list - * this GC cycle. - */ - aging = NULL; - aging_htop = aging_end = NULL; - } - } - - /* Start with looking at gray objects found in earlier collection - * stages. - */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark gray\n")); - { - INC_Object *obj = NULL; - - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - Eterm *ptr; - - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - COPYMARK(ptr,obj->this + obj->size); - } - /* TODO: Titta p� motsvarande i minor. */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark roots\n")); - while (WORK_MORE && inc_active_proc) - { - /* For each process: Scan all areas containing pointers to the - * message area. When a process is done here, all it's - * message-pointers should be to the old generation. - */ - Rootset rootset; - Process *cp = inc_active_proc; - - ASSERT(INC_IS_ACTIVE(cp)); - - /* MT: In a multithreaded system the process cp needs to be - * locked here. - */ - if (cp == p) - rootset.n = setup_rootset(cp, objv, nobj, &rootset); - else - rootset.n = setup_rootset(cp, cp->arg_reg, cp->arity, &rootset); - - while (WORK_MORE && rootset.n--) - { - Eterm *ptr = rootset.v[rootset.n]; - Eterm *end = ptr + rootset.sz[rootset.n]; - - while (WORK_MORE && ptr < end) { - Eterm val = *ptr; - Eterm *obj_ptr = ptr_val(val); - - switch (primary_tag(val)) { - case TAG_PRIMARY_LIST: - { - COPYMARK_CONS(obj_ptr,aging_htop,ptr,aging_end); - break; - } - - case TAG_PRIMARY_BOXED: - { - COPYMARK_BOXED(obj_ptr,aging_htop,ptr,aging_end); - break; - } - } - ptr++; - } - } - -#ifdef HIPE - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Native stack scan: %T\n",cp->id)); - aging_htop = ma_fullsweep_nstack(cp,aging_htop,aging_end); -#endif - restore_one_rootset(cp, &rootset); - - /* MT: cp can be unlocked now. But beware!! The message queue - * might be updated with new pointers to the fromspace while - * we work below. The send operation can not assume that all - * active processes will look through their message queue - * before deactivating as is the case in non-MT incremental - * collection. - */ - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark process heap\n")); - { - Eterm *ptr = cp->scan_top; - COPYMARK(ptr,cp->htop); - //cp->scan_top = ptr; - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark heap fragments\n")); - { - ErlHeapFragment* bp = MBUF(cp); - - while (WORK_MORE && bp) { - Eterm *ptr = bp->mem; - Eterm *end; - - if ((ARITH_HEAP(cp) >= bp->mem) && - (ARITH_HEAP(cp) < bp->mem + bp->size)) { - end = ARITH_HEAP(cp); - } else { - end = bp->mem + bp->size; - } - - COPYMARK(ptr,end); - bp = bp->next; - } - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark gray stack\n")); - { - INC_Object *obj = NULL; - - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - Eterm *ptr; - - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - COPYMARK(ptr,obj->this + obj->size); - } - /* TODO: Titta p� motsvarande i minor. */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - if (WORK_MORE) { - INC_DEACTIVATE(cp); - } - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark nursery\n")); - { - Eterm *ptr = inc_nursery_scn_ptr; - COPYMARK(ptr,global_htop); - inc_nursery_scn_ptr = ptr; - } - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Copy-Mark gray found in nursery\n")); - { - INC_Object *obj = NULL; - - while (WORK_MORE && !INC_STORAGE_EMPTY(gray)) { - Eterm *ptr; - - obj = INC_STORAGE_GET(gray); - if ((*obj->this & _TAG_HEADER_MASK) == FUN_SUBTAG) { - ptr = obj->this + thing_arityval(*obj->this) + 1; - } else { - ptr = obj->this; - } - COPYMARK(ptr,obj->this + obj->size); - } - /* TODO: Titta p� motsvarande i minor. */ - if (!WORK_MORE && obj != NULL) - INC_STORE(gray,obj->this,obj->size); - } - - - /**********************************************************************/ - if (WORK_MORE) { - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep phase\n")); - - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep externals in old generation\n")); - { - ExternalThing** prev = &erts_global_offheap.externals; - ExternalThing* ptr = erts_global_offheap.externals; - - while (ptr) { - Eterm* ppt = (Eterm *) ptr; - - if ((ptr_within(ppt, global_old_heap, global_old_hend) && - blackmap[ppt - global_old_heap] == 0) || - (ptr_within(ppt, inc_fromspc, inc_fromend) && - !INC_IS_FORWARDED(ppt))) - { - erts_deref_node_entry(ptr->node); - *prev = ptr = ptr->next; - } else if (ptr_within(ppt, inc_fromspc, inc_fromend)) { - ExternalThing* ro = (ExternalThing*)INC_FORWARD_VALUE(ppt); - *prev = ro; /* Patch to moved pos */ - prev = &ro->next; - ptr = ro->next; - } else { - prev = &ptr->next; - ptr = ptr->next; - } - } - ASSERT(*prev == NULL); - } - - /* Atomic phase */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep refc bins in old generation\n")); - { - ProcBin** prev = &erts_global_offheap.mso; - ProcBin* ptr = erts_global_offheap.mso; - - while (ptr) { - Eterm *ppt = (Eterm*)ptr; - - if ((ptr_within(ppt, global_old_heap, global_old_hend) && - blackmap[ppt - global_old_heap] == 0) || - (ptr_within(ppt, inc_fromspc, inc_fromend) && - !INC_IS_FORWARDED(ppt))) - { - Binary* bptr; - *prev = ptr->next; - bptr = ptr->val; - if (erts_refc_dectest(&bptr->refc, 0) == 0) - erts_bin_free(bptr); - ptr = *prev; - } else if (ptr_within(ppt, inc_fromspc, inc_fromend)) { - ProcBin* ro = (ProcBin*)INC_FORWARD_VALUE(ppt); - *prev = ro; /* Patch to moved pos */ - prev = &ro->next; - ptr = ro->next; - } else { - prev = &ptr->next; - ptr = ptr->next; - } - } - ASSERT(*prev == NULL); - } - - /* TODO: Currently atomic phase - Can not be later of course. */ - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Sweep old generation\n")); - { - INC_Page *page = inc_used_mem; - INC_Page *prev = NULL; - inc_free_list = NULL; - - while (page) { - int scavenging = 0; - int n = page->start - global_old_heap; - int stop = n + INC_PAGESIZE; - - old_gen_sz += INC_PAGESIZE; - while (n < stop) { - if (blackmap[n] != 0) { - if (scavenging) { - Eterm *ptr = global_old_heap + n; - scavenging = 0; - if ((ptr - free_start) * sizeof(Eterm) >= - sizeof(INC_MemBlock)) - { - INC_MemBlock *new = (INC_MemBlock*)free_start; - new->size = ptr - free_start; - new->prev = NULL; - new->next = inc_free_list; - if (inc_free_list) - inc_free_list->prev = new; - inc_free_list = new; - } - } - if (blackmap[n] == 255) { - unsigned int size = - *(unsigned int*)(((long)&blackmap[n]+4) & ~3); - live += size; - n += size; - } - else { - live += blackmap[n]; - n += blackmap[n]; - } - } - else if (!scavenging) { - free_start = global_old_heap + n; - scavenging = 1; - n++; - } - else { - n++; - } - } - - if (scavenging) { - if ((global_old_heap + n - free_start) * sizeof(Eterm) > - sizeof(INC_MemBlock)) - { - INC_MemBlock *new = (INC_MemBlock*)free_start; - new->size = global_old_heap + n - free_start; - new->prev = NULL; - new->next = inc_free_list; - if (inc_free_list) - inc_free_list->prev = new; - inc_free_list = new; - } - else if (free_start == page->start) { - INC_Page *next = page->next; - - if (prev) - prev->next = page->next; - else - inc_used_mem = page->next; - - page->next = inc_bibop; - inc_bibop = page; - inc_used_pages--; - page = next; - continue; - } - } - prev = page; - page = page->next; - } - } - } - - ASSERT(inc_bibop); - /* - This code is not expected to work right now. - if (!inc_bibop) { - int i; - int new_pages = inc_pages * 2; - int size = sizeof(Eterm) * new_pages * INC_FULLPAGE; - Eterm *new_heap = erts_alloc(ERTS_ALC_T_MESSAGE_AREA,size); - Eterm *new_hend = new_heap + size; - Eterm *new_htop; - Eterm *last_page_end; - INC_Page *new_used_mem; - INC_Page *page; - - erts_printf("The last page has been allocated..\n"); - erts_printf("We need to copy things!\n"); - - / * Create new, bigger bag of pages * / - for (i = 0; i < new_pages; i++) - { - INC_Page *this = - (INC_Page*)(new_heap + i * INC_FULLPAGE); - this->next = (INC_Page*)((Eterm*)this + INC_FULLPAGE); - } - inc_bibop = (INC_Page*)new_heap; - ((INC_Page*)(new_heap + (new_pages - 1) * - INC_FULLPAGE))->next = NULL; - - new_used_mem = inc_bibop; - inc_bibop = inc_bibop->next; - new_used_mem->next = NULL; - - / * Move stuff from old bag to new * / - inc_free_list = NULL; - new_htop = new_used_mem->start; - last_page_end = new_htop + INC_PAGESIZE; - page = inc_used_mem; - while (page) - { - Eterm *ptr = page->start; - Eterm *page_end = ptr + INC_PAGESIZE; - int n = offsetof(INC_Page,start) / sizeof(void*) + - ((Eterm*)page - global_old_heap); - while (ptr < page_end) - { - if (blackmap[n] > 0) - { - if (last_page_end - new_htop < blackmap[n]) - { - INC_Page *new_page = inc_bibop; - inc_bibop = inc_bibop->next; - new_page->next = new_used_mem; - new_used_mem = new_page; - new_htop = new_page->start; - last_page_end = new_htop + INC_PAGESIZE; - } - - memcpy(new_htop,ptr,blackmap[n] * sizeof(Eterm)); - for (i = 0; i < blackmap[n]; i++) - { - *ptr++ = (Eterm)new_htop++; - } - //new_htop += blackmap[n]; - //ptr += blackmap[n]; - / * - if (blackmap[n] == 255) Do the right thing... - * / - n += blackmap[n]; - } - else - { - n++; ptr++; - } - } - page = page->next; - } - - page = inc_used_mem; - while (page) - { - Eterm *ptr = page->start; - Eterm *page_end = ptr + INC_PAGESIZE; - - / * TODO: If inc_used_mem is sorted in address order, this - * pass can be done at the same time as copying. * / - while (ptr < page_end) - { - if (ptr_within(ptr_val(*ptr),global_old_heap,global_old_hend)) - { - *ptr = *((Eterm*)ptr_val(*ptr)); - } - ptr++; - } - page = page->next; - } - - printf("Restore rootset after heap move. Roots: %d\r\n",roots_saved); - while (roots_saved--) - { - Eterm *ptr = root_save[roots_saved]; - *ptr = *((Eterm*)ptr_val(*ptr)); - } - - erts_free(ERTS_ALC_T_MESSAGE_AREA,(void*)global_old_heap); - - global_old_heap = new_heap; - global_old_hend = new_hend; - inc_used_mem = new_used_mem; - inc_pages = new_pages; - - if ((last_page_end - new_htop) * sizeof(Eterm) >= - sizeof(INC_MemBlock)) - { - inc_free_list = (INC_MemBlock*)(new_htop); - inc_free_list->size = last_page_end - new_htop; - inc_free_list->prev = NULL; - inc_free_list->next = NULL; - } - } - */ - - /* I vilka l�gen kan vi vilja sl�nga p� en extra sida.. ( < 25% kvar?) - if () - { - INC_Page *new_page = inc_bibop; - INC_MemBlock *new_free = - (INC_MemBlock*)new_page->start; - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Fetching new page\n")); - inc_bibop = inc_bibop->next; - - new_page->next = inc_used_mem; - if (inc_used_mem) - inc_used_mem->prev = new_page; - inc_used_mem = new_page; - - // kolla detta med normal sidstorlek! old_gen_sz += INC_PAGESIZE; - //BM_SWAP_TIMER(gc,misc1); - memset(blackmap + - ((void*)new_page - (void*)global_old_heap) / sizeof(void*), - 0, INC_FULLPAGE); - //BM_SWAP_TIMER(misc1,gc); - - new_free->prev = NULL; - new_free->next = inc_free_list; - new_free->size = INC_PAGESIZE; - if (inc_free_list) - inc_free_list->prev = new_free; - inc_free_list = new_free; - //printf("Snatched a new page @ 0x%08x\r\n",(int)new_page); - //print_free_list(); - found = new_free; - } - */ - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Update copy stack\n")); - { - Uint i; - for (i = 0; i < ma_dst_top; i++) { - if (ptr_within(ma_dst_stack[i],inc_fromspc,inc_fromend)) { - if (INC_IS_FORWARDED(ma_dst_stack[i])) - ma_dst_stack[i] = INC_FORWARD_VALUE(ma_dst_stack[i]); - } - } - } - - if (WORK_MORE) - { - int size_left = INC_PAGESIZE - (aging_htop - aging->start); - - if (size_left > sizeof(INC_MemBlock)) - { - ((INC_MemBlock*)aging_htop)->size = size_left; - ((INC_MemBlock*)aging_htop)->prev = NULL; - ((INC_MemBlock*)aging_htop)->next = inc_free_list; - if (inc_free_list) - inc_free_list->prev = (INC_MemBlock*)aging_htop; - inc_free_list = (INC_MemBlock*)aging_htop; - } - aging->next = inc_used_mem; - inc_used_mem = aging; - inc_used_pages++; - - ma_gc_flags &= ~GC_MAJOR; - ma_gc_flags &= ~GC_CYCLE; - - VERBOSE(DEBUG_HYBRID_GC,("INCGC: Major collection cycle END\n")); - } - - ma_gc_flags &= ~GC_INCLUDE_ALL; - - BM_STOP_TIMER(old_gc); -#ifdef BM_TIMER - major_global_gc_time += old_gc_time; - if (old_gc_time > max_global_major_time) - max_global_major_time = old_gc_time; - - if ((old_gc_time * 1000) < MAX_PAUSE_TIME) - pause_times_old[(int)(old_gc_time * 1000)]++; - else - pause_times_old[MAX_PAUSE_TIME - 1]++; -#endif - BM_START_TIMER(gc); -} - - - -/*************************************************************************** - * * - * Allocation in the old generation. Used in minor colection and when * - * copying the rest of a message after a GC. * - * * - ***************************************************************************/ - - -Eterm *erts_inc_alloc(int need) -{ - INC_MemBlock *this = inc_free_list; - - ASSERT(need < INC_PAGESIZE); - while (this && (this->size) < need) - { - this = this->next; - } - - if (!this) - { - /* If a free block large enough is not found, a new page is - * allocated. GC_NEED_MAJOR is set so that the next garbage - * collection cycle will be a major one, that is, both - * generations will be garbage collected. - */ - INC_Page *new_page = inc_bibop; - INC_MemBlock *new_free = (INC_MemBlock*)new_page->start; - - if (new_page) - { - VERBOSE(DEBUG_HYBRID_GC, - ("INCGC: Allocation grabs a new page\n")); - inc_bibop = inc_bibop->next; - new_page->next = inc_used_mem; - inc_used_mem = new_page; - inc_used_pages++; - - new_free->prev = NULL; - new_free->next = inc_free_list; - new_free->size = INC_PAGESIZE; - if (inc_free_list) - inc_free_list->prev = new_free; - inc_free_list = new_free; - - this = new_free; - if (!(ma_gc_flags & GC_MAJOR)) - ma_gc_flags |= GC_NEED_MAJOR; - } - else - { - erl_exit(-1, "inc_alloc ran out of pages!\n"); - } - } - - if (((this->size) - need) * sizeof(Eterm) >= sizeof(INC_MemBlock)) - { - INC_MemBlock *rest = (INC_MemBlock*)((Eterm*)this + need); - - /* The order here IS important! */ - rest->next = this->next; - - if (rest->next) - rest->next->prev = rest; - - rest->prev = this->prev; - - if (rest->prev) - rest->prev->next = rest; - else - inc_free_list = rest; - - rest->size = this->size - need; - } - else - { - if (this->prev) - this->prev->next = this->next; - else - inc_free_list = this->next; - - if (this->next) - this->next->prev = this->prev; - } - - if (ma_gc_flags & GC_MAJOR) { - if (need > 254) { - blackmap[(Eterm*)this - global_old_heap] = 255; - *(int*)((UWord)(&blackmap[(Eterm*)this - global_old_heap]+4) & ~3) = - need; - } else - blackmap[(Eterm*)this - global_old_heap] = need; - } - return (Eterm*)this; -} -#endif /* INCREMENTAL */ diff --git a/erts/emulator/beam/erl_nmgc.h b/erts/emulator/beam/erl_nmgc.h deleted file mode 100644 index b207dd37fa..0000000000 --- a/erts/emulator/beam/erl_nmgc.h +++ /dev/null @@ -1,364 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2004-2009. All Rights Reserved. - * - * The contents of this file are subject to the Erlang Public License, - * Version 1.1, (the "License"); you may not use this file except in - * compliance with the License. You should have received a copy of the - * Erlang Public License along with this software. If not, it can be - * retrieved online at http://www.erlang.org/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and limitations - * under the License. - * - * %CopyrightEnd% - */ - -#ifndef __ERL_NMGC_H__ -#define __ERL_NMGC_H__ - -#ifdef INCREMENTAL -#include <stddef.h> /* offsetof() */ -#include "erl_process.h" - -#define INC_FULLPAGE (INC_PAGESIZE + offsetof(INC_Page,start) / sizeof(void*)) - -#define BOXED_NEED(PTR,HDR) \ - (((HDR) & _HEADER_SUBTAG_MASK) == SUB_BINARY_SUBTAG ? \ - header_arity(HDR) + 2 : \ - ((HDR) & _HEADER_SUBTAG_MASK) == FUN_SUBTAG ? \ - header_arity(HDR) + ((ErlFunThing*)(PTR))->num_free + 2 : \ - header_arity(HDR) + 1) - - -#define INC_DECREASE_WORK(n) inc_words_to_go -= (n); - -#define INC_COPY_CONS(FROM,TO,PTR) \ -do { \ - TO[0] = FROM[0]; \ - TO[1] = FROM[1]; \ - INC_MARK_FORWARD(FROM,TO); \ - *(PTR) = make_list(TO); \ - INC_DECREASE_WORK(2); \ - (TO) += 2; \ -} while(0) - -#define INC_COPY_BOXED(FROM,TO,PTR) \ -do { \ - Sint nelts; \ - Eterm hdr = *(FROM); \ - \ - ASSERT(is_header(hdr)); \ - INC_MARK_FORWARD(FROM,TO); \ - *(PTR) = make_boxed(TO); \ - *(TO)++ = *(FROM)++; \ - nelts = header_arity(hdr); \ - switch ((hdr) & _HEADER_SUBTAG_MASK) { \ - case SUB_BINARY_SUBTAG: nelts++; break; \ - case FUN_SUBTAG: nelts+=((ErlFunThing*)(FROM-1))->num_free+1; break;\ - } \ - INC_DECREASE_WORK(nelts + 1); \ - while (nelts--) \ - *(TO)++ = *(FROM)++; \ -} while(0) - - -/* Things copied to the old generation are not marked in the blackmap. - * This is ok since the page they are copied to (aging) is not part of - * the sweep. - */ -#define COPYMARK_CONS(FROM,TO,PTR,LIMIT) \ -do { \ - if (ptr_within(FROM,inc_fromspc,inc_fromend)) { \ - if (INC_IS_FORWARDED(FROM)) { \ - *PTR = make_list(INC_FORWARD_VALUE(FROM)); \ - } else if (TO + 2 <= LIMIT) { \ - INC_STORE(gray,TO,2); \ - INC_COPY_CONS(FROM,TO,PTR); \ - } else { \ - Eterm *hp = erts_inc_alloc(2); \ - INC_STORE(gray,hp,2); \ - INC_COPY_CONS(FROM,hp,PTR); \ - } \ - } else if (ptr_within(FROM,global_old_heap,global_old_hend) && \ - (blackmap[FROM - global_old_heap] == 0)) { \ - blackmap[FROM - global_old_heap] = 2; \ - INC_DECREASE_WORK(2); \ - INC_STORE(gray,FROM,2); \ - } \ -} while(0) - -#define COPYMARK_BOXED(FROM,TO,PTR,LIMIT) \ -do { \ - if (ptr_within(FROM,inc_fromspc,inc_fromend)) { \ - int size = BOXED_NEED(FROM,*FROM); \ - if (INC_IS_FORWARDED(FROM)) { \ - *PTR = make_boxed(INC_FORWARD_VALUE(FROM)); \ - } else if (TO + size <= LIMIT) { \ - INC_STORE(gray,TO,size); \ - INC_COPY_BOXED(FROM,TO,PTR); \ - } else { \ - Eterm *hp = erts_inc_alloc(size); \ - INC_STORE(gray,hp,size); \ - INC_COPY_BOXED(FROM,hp,PTR); \ - } \ - } else if (ptr_within(FROM,global_old_heap,global_old_hend) && \ - (blackmap[FROM - global_old_heap] == 0)) { \ - int size = BOXED_NEED(FROM,*FROM); \ - if (size > 254) { \ - blackmap[FROM - global_old_heap] = 255; \ - *(int*)((long)(&blackmap[FROM - \ - global_old_heap] + 4) & ~3) = size; \ - } else \ - blackmap[FROM - global_old_heap] = size; \ - INC_DECREASE_WORK(size); \ - INC_STORE(gray,FROM,size); \ - } \ -} while(0) - -#define INC_MARK_FORWARD(ptr,dst) fwdptrs[(ptr) - inc_fromspc] = (dst); -#define INC_IS_FORWARDED(ptr) (fwdptrs[(ptr) - inc_fromspc] != 0) -#define INC_FORWARD_VALUE(ptr) fwdptrs[(ptr) - inc_fromspc] - -/* Note for BM_TIMER: Active timer should always be 'system' when IncAlloc - * is called! - */ -#define IncAlloc(p, sz, objv, nobj) \ - (ASSERT_EXPR((sz) >= 0), \ - (((inc_alloc_limit - global_htop) <= (sz)) ? \ - erts_incremental_gc((p),(sz),(objv),(nobj)) : 0), \ - ASSERT_EXPR(global_hend - global_htop > (sz)), \ - global_htop += (sz), global_htop - (sz)) - - -/************************************************************************ - * INC_STORAGE, a dynamic circular storage for objects (INC_Object). * - * Use INC_STORE to add objects to the storage. The storage can then * - * be used either as a queue, using INC_STORAGE_GET to retreive * - * values, or as a stack, using INC_STORAGE_POP. It is OK to mix calls * - * to GET and POP if that is desired. * - * An iterator can be declared to traverse the storage without removing * - * any elements, and INC_STORAGE_STEP will then return each element in * - * turn, oldest first. * - ***********************************************************************/ - -/* Declare a new storage; must be in the beginning of a block. Give - * the storage a name that is used in all later calls to the storage. - * If this is an external declaration of the storage, pass the keyword - * external as the first argument, otherwise leave it empty. - */ -#define INC_STORAGE_DECLARATION(ext,name) \ - ext INC_Storage *name##head; \ - ext INC_Storage *name##tail; \ - ext INC_Object *name##free; \ - ext INC_Object *name##last_free; \ - ext int name##size; - - -/* Initialize the storage. Note that memory allocation is involved - - * don't forget to erase the storage when you are done. - */ -#define INC_STORAGE_INIT(name) do { \ - name##head = (INC_Storage*)erts_alloc(ERTS_ALC_T_OBJECT_STACK, \ - sizeof(INC_Storage)); \ - name##head->next = name##head; \ - name##head->prev = name##head; \ - name##tail = name##head; \ - name##free = name##head->data; \ - name##last_free = name##free + INC_STORAGE_SIZE - 1; \ - name##size = 0; \ -} while(0) - - -/* -#define INC_STORAGE_SWAP(s1,s2) do { \ - INC_Storage *tmphead = s1##head; \ - INC_Storage *tmptail = s1##tail; \ - INC_Object *tmpfree = s1##free; \ - INC_Object *tmplast = s1##last_free; \ - int tmpsize = s1##size; \ - s1##head = s2##head; \ - s1##tail = s2##tail; \ - s1##free = s2##free; \ - s1##last_free = s2##last_free; \ - s1##size = s2##size; \ - s2##head = tmphead; \ - s2##tail = tmptail; \ - s2##free = tmpfree; \ - s2##last_free = tmplast; \ - s2##size = tmpsize; \ -} while(0) -*/ - - -/* Return and remove the youngest element - treat the storage as a - * stack. Always check that there are elements in the queue before - * using INC_STORAGE_POP! - */ -#define INC_STORAGE_POP(name) (ASSERT_EXPR(name##size != 0), \ - name##size--, \ - (--name##free != name##head->data - 1) ? \ - name##free : (name##head = name##head->prev, \ - name##free = name##head->data + INC_STORAGE_SIZE - 1)) - - -/* Return and remove the oldest element - treat the storage as a - * queue. Always check that there are elements in the queue before - * using INC_STORAGE_GET! - */ -#define INC_STORAGE_GET(name) (ASSERT_EXPR(name##size != 0), \ - name##size--, \ - (++name##last_free != name##tail->data + INC_STORAGE_SIZE) ? \ - name##last_free : (name##tail = name##tail->next, \ - name##last_free = name##tail->data)) - - -/* Advance the head to the next free location. If the storage is full, - * a new storage is allocated and linked into the list. - */ -#define INC_STORAGE_NEXT(name) do { \ - if (name##free == name##last_free) { \ - name##tail = (INC_Storage*)erts_alloc(ERTS_ALC_T_OBJECT_STACK, \ - sizeof(INC_Storage)); \ - memcpy(name##tail->data,name##head->data, \ - INC_STORAGE_SIZE * sizeof(INC_Object)); \ - name##tail->next = name##head->next; \ - name##head->next = name##tail; \ - name##tail->prev = name##tail->next->prev; \ - name##tail->next->prev = name##tail; \ - name##last_free = ((void*)name##tail + \ - ((void*)name##last_free - (void*)name##head)); \ - } \ - name##free++; \ - name##size++; \ - if (name##free == name##head->data + INC_STORAGE_SIZE) { \ - name##head = name##head->next; \ - name##free = name##head->data; \ - } \ -} while(0) - - -/* The head of this storage is the next free location. This is where - * the next element will be stored. - */ -#define INC_STORAGE_HEAD(name) (name##free) - - -/* Return the top - the youngest element in the storage. */ -/* #define INC_STORAGE_TOP(name) (name##free - 1 with some magic..) */ - - -/* True if the storage is empty, false otherwise */ -#define INC_STORAGE_EMPTY(name) (name##size == 0) - - -/* Store a new element in the head of the storage and advance the head - * to the next free location. - */ -#define INC_STORE(name,ptr,sz) do { \ - INC_STORAGE_HEAD(name)->this = ptr; \ - INC_STORAGE_HEAD(name)->size = sz; \ - INC_STORAGE_NEXT(name); \ -} while(0) - - -/* An iterator. Use it together with INC_STORAGE_STEP to browse throuh - * the storage. Please note that it is not possible to remove an entry - * in the middle of the storage, use GET or POP to remove enties. - */ -#define INC_STORAGE_ITERATOR(name) \ - INC_Storage *name##iterator_head = name##tail; \ - INC_Object *name##iterator_current = name##last_free; \ - int name##iterator_left = name##size; - - -/* Return the next element in the storage (sorted by age, oldest - * first) or NULL if the storage is empty or the last element has been - * returned already. - */ -#define INC_STORAGE_STEP(name) (name##iterator_left == 0 ? NULL : \ - (name##iterator_left--, \ - (++name##iterator_current != name##iterator_head->data + \ - INC_STORAGE_SIZE) ? name##iterator_current : \ - (name##iterator_head = name##iterator_head->next, \ - name##iterator_current = name##iterator_head->data))) - - -/* Erase the storage. */ -#define INC_STORAGE_ERASE(name)do { \ - name##head->prev->next = NULL; \ - while (name##head != NULL) { \ - name##tail = name##head; \ - name##head = name##head->next; \ - erts_free(ERTS_ALC_T_OBJECT_STACK,(void*)name##tail); \ - } \ - name##tail = NULL; \ - name##free = NULL; \ - name##last_free = NULL; \ - name##size = 0; \ -} while(0) - -/* - * Structures used by the non-moving memory manager - */ - -typedef struct -{ - Eterm *this; - unsigned long size; -} INC_Object; - -typedef struct inc_storage { - struct inc_storage *next; - struct inc_storage *prev; - INC_Object data[INC_STORAGE_SIZE]; -} INC_Storage; - -typedef struct inc_mem_block -{ - unsigned long size; - struct inc_mem_block *prev; - struct inc_mem_block *next; -} INC_MemBlock; - -typedef struct inc_page -{ - struct inc_page *next; - Eterm start[1]; /* Has to be last in struct, this is where the data start */ -} INC_Page; - - -/* - * Heap pointers for the non-moving memory area. - */ -extern INC_Page *inc_used_mem; -extern INC_MemBlock *inc_free_list; -extern unsigned char *blackmap; - -extern Eterm **fwdptrs; -extern Eterm *inc_fromspc; -extern Eterm *inc_fromend; -extern Process *inc_active_proc; -extern Process *inc_active_last; -extern Eterm *inc_alloc_limit; -extern int inc_words_to_go; - -INC_STORAGE_DECLARATION(extern,gray); -INC_STORAGE_DECLARATION(extern,root); - -void erts_init_incgc(void); -void erts_cleanup_incgc(void); -void erts_incremental_gc(Process *p, int sz, Eterm* objv, int nobj); -Eterm *erts_inc_alloc(int need); - -#else -# define INC_STORE(lst,ptr,sz) -# define INC_MARK_FORWARD(ptr) -# define INC_IS_FORWARDED(ptr) -# define INC_FORWARD_VALUE(ptr) -#endif /* INCREMENTAL */ - -#endif /* _ERL_NMGC_H_ */ diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 16367c305d..649be3f454 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -849,9 +849,6 @@ static Eterm AM_dist_references; static Eterm AM_node_references; static Eterm AM_system; static Eterm AM_timer; -#ifdef HYBRID -static Eterm AM_processes; -#endif static void setup_reference_table(void); static Eterm reference_table_term(Uint **hpp, Uint *szp); @@ -936,9 +933,6 @@ erts_get_node_and_dist_references(struct process *proc) INIT_AM(node_references); INIT_AM(timer); INIT_AM(system); -#ifdef HYBRID - INIT_AM(processes); -#endif references_atoms_need_init = 0; } @@ -1301,12 +1295,6 @@ setup_reference_table(void) SYSTEM_REF, TUPLE2(&heap[0], AM_system, am_undefined)); -#ifdef HYBRID - /* Insert Heap */ - insert_offheap(&erts_global_offheap, - HEAP_REF, - TUPLE2(&heap[0], AM_processes, am_undefined)); -#endif UnUseTmpHeapNoproc(3); /* Insert all processes */ diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 34da9cab84..2320b64295 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -437,7 +437,10 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, } break; case BINARY_DEF: - { + if (header_is_bin_matchstate(*boxed_val(wobj))) { + PRINT_STRING(res, fn, arg, "#MatchState"); + } + else { ProcBin* pb = (ProcBin *) binary_val(wobj); if (pb->size == 1) PRINT_STRING(res, fn, arg, "<<1 byte>>"); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index c74f8f131f..8153cdce1c 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -28,7 +28,6 @@ #include "erl_vm.h" #include "global.h" #include "erl_process.h" -#include "erl_nmgc.h" #include "error.h" #include "bif.h" #include "erl_db.h" @@ -52,21 +51,22 @@ #define ERTS_SCHED_SPIN_UNTIL_YIELD 100 -#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT 10 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_LONG 40 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_LONG 1000 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_LONG 20 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_LONG 1000 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM 10 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM 1000 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_SHORT 10 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_SHORT 0 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_SHORT 5 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_SHORT 0 +#define ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE 0 +#define ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_NONE 0 + #define ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT 1000 -#define ERTS_SCHED_TSE_SLEEP_SPINCOUNT \ - (ERTS_SCHED_SYS_SLEEP_SPINCOUNT*ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT) #define ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT 0 -#define ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH (200*CONTEXT_REDS) -#define ERTS_WAKEUP_OTHER_LIMIT_HIGH (50*CONTEXT_REDS) -#define ERTS_WAKEUP_OTHER_LIMIT_MEDIUM (10*CONTEXT_REDS) -#define ERTS_WAKEUP_OTHER_LIMIT_LOW (CONTEXT_REDS) -#define ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW (CONTEXT_REDS/10) - -#define ERTS_WAKEUP_OTHER_DEC 10 -#define ERTS_WAKEUP_OTHER_FIXED_INC (CONTEXT_REDS/10) - #if 0 || defined(DEBUG) #define ERTS_FAKE_SCHED_BIND_PRINT_SORTED_CPU_DATA #endif @@ -283,14 +283,18 @@ Uint erts_no_schedulers; ErtsProcTab erts_proc erts_align_attribute(ERTS_CACHE_LINE_SIZE); -static int wakeup_other_limit; - int erts_sched_thread_suggested_stack_size = -1; #ifdef ERTS_ENABLE_LOCK_CHECK ErtsLcPSDLocks erts_psd_required_locks[ERTS_PSD_SIZE]; #endif +static struct { + int aux_work; + int tse; + int sys_schedule; +} sched_busy_wait; + #ifdef ERTS_SMP int erts_disable_proc_not_running_opt; @@ -399,11 +403,6 @@ struct erts_system_monitor_flags_t erts_system_monitor_flags; Eterm erts_system_profile; struct erts_system_profile_flags_t erts_system_profile_flags; -#ifdef HYBRID -Uint erts_num_active_procs; -Process** erts_active_procs; -#endif - #if ERTS_MAX_PROCESSES > 0x7fffffff #error "Need to store process_count in another type" #endif @@ -519,6 +518,7 @@ dbg_chk_aux_work_val(erts_aint32_t value) valid |= ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN; #endif #ifdef ERTS_SMP + valid |= ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP; valid |= ERTS_SSI_AUX_WORK_MISC_THR_PRGR; valid |= ERTS_SSI_AUX_WORK_DD; valid |= ERTS_SSI_AUX_WORK_DD_THR_PRGR; @@ -529,6 +529,10 @@ dbg_chk_aux_work_val(erts_aint32_t value) #ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN valid |= ERTS_SSI_AUX_WORK_CHECK_CHILDREN; #endif +#ifdef ERTS_SMP + valid |= ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION; + valid |= ERTS_SSI_AUX_WORK_FINISH_BP; +#endif #ifdef ERTS_SSI_AUX_WORK_REAP_PORTS valid |= ERTS_SSI_AUX_WORK_REAP_PORTS; #endif @@ -689,12 +693,6 @@ erts_init_process(int ncpu) erts_smp_atomic_init_nob(proc_entry, ERTS_AINT_NULL); proc_entry++; } -#ifdef HYBRID - erts_active_procs = (Process**) - erts_alloc(ERTS_ALC_T_ACTIVE_PROCS, - erts_proc.max * sizeof(Process*)); - erts_num_active_procs = 0; -#endif erts_smp_rwmtx_init_opt(&erts_proc_tab_rwmtx, &proc_tab_rwmtx_opts, @@ -1149,13 +1147,13 @@ unset_aux_work_flags(ErtsSchedulerSleepInfo *ssi, erts_aint32_t flgs) #ifdef ERTS_SMP static ERTS_INLINE void -thr_prgr_current_reset(ErtsAuxWorkData *awdp) +haw_thr_prgr_current_reset(ErtsAuxWorkData *awdp) { awdp->current_thr_prgr = ERTS_THR_PRGR_INVALID; } static ERTS_INLINE ErtsThrPrgrVal -thr_prgr_current(ErtsAuxWorkData *awdp) +haw_thr_prgr_current(ErtsAuxWorkData *awdp) { ErtsThrPrgrVal current = awdp->current_thr_prgr; if (current == ERTS_THR_PRGR_INVALID) { @@ -1165,6 +1163,60 @@ thr_prgr_current(ErtsAuxWorkData *awdp) return current; } +static ERTS_INLINE void +haw_thr_prgr_current_check_progress(ErtsAuxWorkData *awdp) +{ + ErtsThrPrgrVal current = awdp->current_thr_prgr; + if (current != ERTS_THR_PRGR_INVALID + && !erts_thr_progress_equal(current, erts_thr_progress_current())) { + /* + * We have used a previouly read current value that isn't the + * latest; need to poke ourselfs in order to guarantee no loss + * of wakeups. + */ + erts_sched_poke(awdp->ssi); + } +} + +static ERTS_INLINE erts_aint32_t +handle_delayed_aux_work_wakeup(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) +{ + int jix, max_jix; + unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP); + + ERTS_THR_MEMORY_BARRIER; + + max_jix = awdp->delayed_wakeup.jix; + awdp->delayed_wakeup.jix = -1; + for (jix = 0; jix <= max_jix; jix++) { + int sched = awdp->delayed_wakeup.job[jix].sched; + erts_aint32_t aux_work = awdp->delayed_wakeup.job[jix].aux_work; + + ASSERT(awdp->delayed_wakeup.sched2jix[sched] == jix); + awdp->delayed_wakeup.sched2jix[sched] = -1; + set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(sched-1), + aux_work); + } + return aux_work & ~ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP; +} + +static ERTS_INLINE void +schedule_aux_work_wakeup(ErtsAuxWorkData *awdp, int sched, erts_aint32_t aux_work) +{ + int jix = awdp->delayed_wakeup.sched2jix[sched]; + if (jix >= 0) { + ASSERT(awdp->delayed_wakeup.job[jix].sched == sched); + awdp->delayed_wakeup.job[jix].aux_work |= aux_work; + } + else { + jix = ++awdp->delayed_wakeup.jix; + awdp->delayed_wakeup.sched2jix[sched] = jix; + awdp->delayed_wakeup.job[jix].sched = sched; + awdp->delayed_wakeup.job[jix].aux_work = aux_work; + } + set_aux_work_flags_wakeup_nob(awdp->ssi, ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP); +} + #endif typedef struct erts_misc_aux_work_t_ erts_misc_aux_work_t; @@ -1263,7 +1315,7 @@ static ERTS_INLINE erts_aint32_t handle_misc_aux_work_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { - if (!erts_thr_progress_has_reached_this(thr_prgr_current(awdp), + if (!erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp), awdp->misc.thr_prgr)) return aux_work & ~ERTS_SSI_AUX_WORK_MISC_THR_PRGR; @@ -1368,7 +1420,7 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp, #ifdef ERTS_SMP if (awdp->async_ready.need_thr_prgr - && !erts_thr_progress_has_reached_this(thr_prgr_current(awdp), + && !erts_thr_progress_has_reached_this(haw_thr_prgr_current(awdp), awdp->async_ready.thr_prgr)) { return aux_work & ~ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN; } @@ -1395,7 +1447,94 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp, } } +#endif /* ERTS_USE_ASYNC_READY_Q */ + +#ifdef ERTS_SMP +void +erts_notify_code_ix_activation(Process* p, ErtsThrPrgrVal later) +{ + ErtsAuxWorkData* awdp = &p->scheduler_data->aux_work_data; + ASSERT(awdp->code_ix_activation.code_stager == NULL); + awdp->code_ix_activation.code_stager = p; + awdp->code_ix_activation.thr_prgr = later; + erts_smp_proc_inc_refc(p); + set_aux_work_flags_wakeup_relb(p->scheduler_data->ssi, + ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION); +} + +static erts_aint32_t +handle_code_ix_activation(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) +{ + Process* p; + if (!erts_thr_progress_has_reached(awdp->code_ix_activation.thr_prgr)) { + return aux_work & ~ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION; + } + unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION); + p = awdp->code_ix_activation.code_stager; + ASSERT(p); +#ifdef DEBUG + awdp->code_ix_activation.code_stager = NULL; #endif + erts_commit_staging_code_ix(); + erts_release_code_write_permission(); + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + if (!ERTS_PROC_IS_EXITING(p)) { + erts_resume(p, ERTS_PROC_LOCK_STATUS); + } + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + erts_smp_proc_dec_refc(p); + return aux_work & ~ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION; +} +#endif /* ERTS_SMP */ + +#ifdef ERTS_SMP +void +erts_notify_finish_breakpointing(Process* p) +{ + ErtsAuxWorkData* awdp = &p->scheduler_data->aux_work_data; + + ASSERT(awdp->bp_ix_activation.stager == NULL); + awdp->bp_ix_activation.stager = p; + awdp->bp_ix_activation.thr_prgr = erts_thr_progress_later(awdp->esdp); + erts_thr_progress_wakeup(awdp->esdp, awdp->bp_ix_activation.thr_prgr); + erts_smp_proc_inc_refc(p); + set_aux_work_flags_wakeup_relb(p->scheduler_data->ssi, + ERTS_SSI_AUX_WORK_FINISH_BP); +} + +static erts_aint32_t +handle_finish_bp(ErtsAuxWorkData* awdp, erts_aint32_t aux_work) +{ + ErtsThrPrgrVal current = haw_thr_prgr_current(awdp); + + if (!erts_thr_progress_has_reached_this(current, + awdp->bp_ix_activation.thr_prgr)) { + return aux_work & ~ERTS_SSI_AUX_WORK_FINISH_BP; + } + if (erts_finish_breakpointing()) { /* Not done */ + /* Arrange for being called again */ + awdp->bp_ix_activation.thr_prgr = + erts_thr_progress_later(awdp->esdp); + erts_thr_progress_wakeup(awdp->esdp, awdp->bp_ix_activation.thr_prgr); + } else { /* Done */ + Process* p; + + unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_FINISH_BP); + p = awdp->bp_ix_activation.stager; +#ifdef DEBUG + awdp->bp_ix_activation.stager = NULL; +#endif + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + if (!ERTS_PROC_IS_EXITING(p)) { + erts_resume(p, ERTS_PROC_LOCK_STATUS); + } + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + erts_smp_proc_dec_refc(p); + erts_release_code_write_permission(); + } + return aux_work & ~ERTS_SSI_AUX_WORK_FINISH_BP; +} +#endif /* ERTS_SMP */ static ERTS_INLINE erts_aint32_t handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) @@ -1421,8 +1560,14 @@ handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) void erts_alloc_notify_delayed_dealloc(int ix) { - set_aux_work_flags_wakeup_nob(ERTS_SCHED_SLEEP_INFO_IX(ix-1), - ERTS_SSI_AUX_WORK_DD); + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + if (esdp) + schedule_aux_work_wakeup(&esdp->aux_work_data, + ix, + ERTS_SSI_AUX_WORK_DD); + else + set_aux_work_flags_wakeup_relb(ERTS_SCHED_SLEEP_INFO_IX(ix-1), + ERTS_SSI_AUX_WORK_DD); } static ERTS_INLINE erts_aint32_t @@ -1449,7 +1594,7 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) if (need_thr_progress) { if (wakeup == ERTS_THR_PRGR_INVALID) - wakeup = erts_thr_progress_later_than(thr_prgr_current(awdp)); + wakeup = erts_thr_progress_later(awdp->esdp); awdp->dd.thr_prgr = wakeup; set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD_THR_PRGR); awdp->dd.thr_prgr = wakeup; @@ -1470,7 +1615,7 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) int need_thr_progress; int more_work; ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID; - ErtsThrPrgrVal current = thr_prgr_current(awdp); + ErtsThrPrgrVal current = haw_thr_prgr_current(awdp); if (!erts_thr_progress_has_reached_this(current, awdp->dd.thr_prgr)) return aux_work & ~ERTS_SSI_AUX_WORK_DD_THR_PRGR; @@ -1492,7 +1637,7 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) if (need_thr_progress) { if (wakeup == ERTS_THR_PRGR_INVALID) - wakeup = erts_thr_progress_later_than(current); + wakeup = erts_thr_progress_later(awdp->esdp); awdp->dd.thr_prgr = wakeup; erts_thr_progress_wakeup(awdp->esdp, wakeup); } @@ -1681,7 +1826,7 @@ handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) } static erts_aint32_t -handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) +handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting) { #undef HANDLE_AUX_WORK #define HANDLE_AUX_WORK(FLG, HNDLR) \ @@ -1699,7 +1844,7 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) erts_aint32_t ignore = 0; #ifdef ERTS_SMP - thr_prgr_current_reset(awdp); + haw_thr_prgr_current_reset(awdp); #endif ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); @@ -1720,6 +1865,8 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) * eachother. Most frequent first. */ #ifdef ERTS_SMP + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP, + handle_delayed_aux_work_wakeup); HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DD, handle_delayed_dealloc); /* DD must be before DD_THR_PRGR */ @@ -1760,11 +1907,26 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) handle_mseg_cache_check); #endif +#ifdef ERTS_SMP + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION, + handle_code_ix_activation); +#endif + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_REAP_PORTS, handle_reap_ports); +#ifdef ERTS_SMP + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_FINISH_BP, + handle_finish_bp); +#endif + ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); +#ifdef ERTS_SMP + if (waiting && !aux_work) + haw_thr_prgr_current_check_progress(awdp); +#endif + return aux_work; #undef HANDLE_AUX_WORK @@ -1916,8 +2078,8 @@ sched_waiting_sys(Uint no, ErtsRunQueue *rq) { ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); ASSERT(rq->waiting >= 0); - ERTS_RUNQ_FLGS_SET(rq, (ERTS_RUNQ_FLG_OUT_OF_WORK - | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK)); + (void) ERTS_RUNQ_FLGS_SET(rq, (ERTS_RUNQ_FLG_OUT_OF_WORK + | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK)); rq->waiting++; rq->waiting *= -1; rq->woken = 0; @@ -1993,8 +2155,8 @@ static ERTS_INLINE void sched_waiting(Uint no, ErtsRunQueue *rq) { ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); - ERTS_RUNQ_FLGS_SET(rq, (ERTS_RUNQ_FLG_OUT_OF_WORK - | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK)); + (void) ERTS_RUNQ_FLGS_SET(rq, (ERTS_RUNQ_FLG_OUT_OF_WORK + | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK)); if (rq->waiting < 0) rq->waiting--; else @@ -2191,7 +2353,7 @@ thr_prgr_fin_wait(void *vssi) | ERTS_SSI_FLG_TSE_SLEEPING)); } -static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp); +static void init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp); static void * aux_thread(void *unused) @@ -2211,7 +2373,7 @@ aux_thread(void *unused) callbacks.finalize_wait = thr_prgr_fin_wait; erts_thr_progress_register_managed_thread(NULL, &callbacks, 1); - init_aux_work_data(awdp, NULL); + init_aux_work_data(awdp, NULL, NULL); awdp->ssi = ssi; sched_prep_spin_wait(ssi); @@ -2223,7 +2385,7 @@ aux_thread(void *unused) if (aux_work) { if (!thr_prgr_active) erts_thr_progress_active(NULL, thr_prgr_active = 1); - aux_work = handle_aux_work(awdp, aux_work); + aux_work = handle_aux_work(awdp, aux_work, 1); if (aux_work && erts_thr_progress_update(NULL)) erts_thr_progress_leader_update(NULL); } @@ -2287,7 +2449,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_smp_runq_unlock(rq); - spincount = ERTS_SCHED_TSE_SLEEP_SPINCOUNT; + spincount = sched_busy_wait.tse; tse_wait: @@ -2302,7 +2464,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_thr_progress_active(esdp, thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } - aux_work = handle_aux_work(&esdp->aux_work_data, aux_work); + aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); } @@ -2338,7 +2500,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } flgs = sched_prep_cont_spin_wait(ssi); - spincount = ERTS_SCHED_TSE_SLEEP_SPINCOUNT; + spincount = sched_busy_wait.aux_work; if (!(flgs & ERTS_SSI_FLG_WAITING)) { ASSERT(!(flgs & ERTS_SSI_FLG_SLEEPING)); @@ -2375,7 +2537,9 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ASSERT(working); sched_wall_time_change(esdp, working = 0); - spincount = ERTS_SCHED_SYS_SLEEP_SPINCOUNT; + spincount = sched_busy_wait.sys_schedule; + if (spincount == 0) + goto sys_aux_work; while (spincount-- > 0) { @@ -2404,7 +2568,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) if (!thr_prgr_active) erts_thr_progress_active(esdp, thr_prgr_active = 1); #endif - aux_work = handle_aux_work(&esdp->aux_work_data, aux_work); + aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); #ifdef ERTS_SMP if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); @@ -2666,7 +2830,7 @@ chk_wake_sched(ErtsRunQueue *crq, int ix, int activate) if (!(flags & (ERTS_RUNQ_FLG_SUSPENDED|ERTS_RUNQ_FLG_NONEMPTY))) { if (activate) { if (try_inc_no_active_runqs(ix+1)) - ERTS_RUNQ_FLGS_UNSET(wrq, ERTS_RUNQ_FLG_INACTIVE); + (void) ERTS_RUNQ_FLGS_UNSET(wrq, ERTS_RUNQ_FLG_INACTIVE); } wake_scheduler(wrq, 0); return 1; @@ -2733,7 +2897,7 @@ erts_sched_notify_check_cpu_bind(void) int ix; for (ix = 0; ix < erts_no_run_queues; ix++) { ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); - ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND); + (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND); wake_scheduler(rq, 0); } #else @@ -2999,7 +3163,7 @@ suspend_run_queue(ErtsRunQueue *rq) { erts_smp_atomic32_read_bor_nob(&rq->scheduler->ssi->flags, ERTS_SSI_FLG_SUSPENDED); - ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_SUSPENDED); + (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_SUSPENDED); wake_scheduler(rq, 0); } @@ -3066,7 +3230,7 @@ evacuate_run_queue(ErtsRunQueue *rq, ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); - ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); + (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); mps = erts_get_migration_paths_managed(); mp = &mps->mpath[rq->ix]; @@ -3529,7 +3693,7 @@ retire_mpaths(ErtsMigrationPaths *mps) if (!mpaths.retired.first) mpaths.retired.last = NULL; - mps->thr_prgr = erts_thr_progress_later_than(current); + mps->thr_prgr = erts_thr_progress_later(NULL); mps->next = NULL; if (mpaths.retired.last) @@ -3571,9 +3735,9 @@ check_balance(ErtsRunQueue *c_rq) ERTS_FOREACH_RUNQ(rq, { if (rq->waiting) - ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK); + (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK); else - ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK); + (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK); rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS; }); @@ -4142,33 +4306,287 @@ erts_debug_nbalance(void) #endif } +/* Wakeup other schedulers */ + +typedef enum { + ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH, + ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH, + ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM, + ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW, + ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW +} ErtsSchedWakeupOtherThreshold; + +typedef enum { + ERTS_SCHED_WAKEUP_OTHER_TYPE_PROPOSAL, + ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY +} ErtsSchedWakeupOtherType; + +/* First proposal */ + +#define ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH (200*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_HIGH (50*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_MEDIUM (10*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_LOW (CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW (CONTEXT_REDS/10) + +#define ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_HIGH 3 +#define ERTS_WAKEUP_OTHER_DEC_SHIFT_HIGH 1 +#define ERTS_WAKEUP_OTHER_DEC_SHIFT_MEDIUM 0 +#define ERTS_WAKEUP_OTHER_DEC_SHIFT_LOW -2 +#define ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_LOW -5 + +#define ERTS_WAKEUP_OTHER_DEC_SHIFT 2 +#define ERTS_WAKEUP_OTHER_FIXED_INC (CONTEXT_REDS/10) + +/* To be legacy */ + +#define ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH_LEGACY (200*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_HIGH_LEGACY (50*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_MEDIUM_LEGACY (10*CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_LOW_LEGACY (CONTEXT_REDS) +#define ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW_LEGACY (CONTEXT_REDS/10) + +#define ERTS_WAKEUP_OTHER_DEC_LEGACY 10 +#define ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY (CONTEXT_REDS/10) + +#ifdef ERTS_SMP + +static struct { + ErtsSchedWakeupOtherThreshold threshold; + ErtsSchedWakeupOtherType type; + int limit; + int dec_shift; + int dec_mask; + void (*check)(ErtsRunQueue *rq, Uint32 flags); +} wakeup_other; + +static void +wakeup_other_check(ErtsRunQueue *rq, Uint32 flags) +{ + int wo_reds = rq->wakeup_other_reds; + if (wo_reds) { + int left_len = rq->len - 1; + if (left_len < 1) { + int wo_reduce = wo_reds << wakeup_other.dec_shift; + wo_reduce &= wakeup_other.dec_mask; + rq->wakeup_other -= wo_reduce; + if (rq->wakeup_other < 0) + rq->wakeup_other = 0; + } + else { + rq->wakeup_other += (left_len*wo_reds + + ERTS_WAKEUP_OTHER_FIXED_INC); + if (rq->wakeup_other > wakeup_other.limit) { + int empty_rqs = + erts_smp_atomic32_read_acqb(&no_empty_run_queues); + if (flags & ERTS_RUNQ_FLG_PROTECTED) + (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); + if (empty_rqs != 0) + wake_scheduler_on_empty_runq(rq); + rq->wakeup_other = 0; + } + } + rq->wakeup_other_reds = 0; + } +} + +static void +wakeup_other_set_limit(void) +{ + switch (wakeup_other.threshold) { + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH; + wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_HIGH; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH; + wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_HIGH; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM; + wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_MEDIUM; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_LOW; + wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_LOW; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW; + wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_LOW; + break; + } + if (wakeup_other.dec_shift < 0) + wakeup_other.dec_mask = (1 << (sizeof(wakeup_other.dec_mask)*8 + + wakeup_other.dec_shift)) - 1; + else { + wakeup_other.dec_mask = 0; + wakeup_other.dec_mask = ~wakeup_other.dec_mask; + } +} + +static void +wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags) +{ + int wo_reds = rq->wakeup_other_reds; + if (wo_reds) { + erts_aint32_t len = rq->len; + if (len < 2) { + rq->wakeup_other -= ERTS_WAKEUP_OTHER_DEC_LEGACY*wo_reds; + if (rq->wakeup_other < 0) + rq->wakeup_other = 0; + } + else if (rq->wakeup_other < wakeup_other.limit) + rq->wakeup_other += len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY; + else { + if (flags & ERTS_RUNQ_FLG_PROTECTED) + (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); + if (erts_smp_atomic32_read_acqb(&no_empty_run_queues) != 0) { + wake_scheduler_on_empty_runq(rq); + rq->wakeup_other = 0; + } + rq->wakeup_other = 0; + } + } + rq->wakeup_other_reds = 0; +} + +static void +wakeup_other_set_limit_legacy(void) +{ + switch (wakeup_other.threshold) { + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH_LEGACY; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH_LEGACY; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM_LEGACY; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_LOW_LEGACY; + break; + case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW: + wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW_LEGACY; + break; + } +} + +static void +set_wakeup_other_data(void) +{ + switch (wakeup_other.type) { + case ERTS_SCHED_WAKEUP_OTHER_TYPE_PROPOSAL: + wakeup_other.check = wakeup_other_check; + wakeup_other_set_limit(); + break; + case ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY: + wakeup_other.check = wakeup_other_check_legacy; + wakeup_other_set_limit_legacy(); + break; + } +} + +#endif + void erts_early_init_scheduling(int no_schedulers) { aux_work_timeout_early_init(no_schedulers); - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM; +#ifdef ERTS_SMP + wakeup_other.threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM; + wakeup_other.type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY; +#endif + sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM; + sched_busy_wait.tse = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM + * ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT); + sched_busy_wait.aux_work = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM + * ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM); } int -erts_sched_set_wakeup_limit(char *str) +erts_sched_set_wakeup_other_thresold(char *str) { + ErtsSchedWakeupOtherThreshold threshold; if (sys_strcmp(str, "very_high") == 0) - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH; + threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH; else if (sys_strcmp(str, "high") == 0) - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH; + threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH; else if (sys_strcmp(str, "medium") == 0) - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM; + threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM; else if (sys_strcmp(str, "low") == 0) - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_LOW; + threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW; else if (sys_strcmp(str, "very_low") == 0) - wakeup_other_limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW; + threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW; else return EINVAL; +#ifdef ERTS_SMP + wakeup_other.threshold = threshold; + set_wakeup_other_data(); +#endif return 0; } +int +erts_sched_set_wakeup_other_type(char *str) +{ + ErtsSchedWakeupOtherType type; + if (sys_strcmp(str, "proposal") == 0) + type = ERTS_SCHED_WAKEUP_OTHER_TYPE_PROPOSAL; + else if (sys_strcmp(str, "default") == 0) + type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY; + else if (sys_strcmp(str, "legacy") == 0) + type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY; + else + return EINVAL; +#ifdef ERTS_SMP + wakeup_other.type = type; +#endif + return 0; +} + +int +erts_sched_set_busy_wait_threshold(char *str) +{ + int sys_sched; + int aux_work_fact; + + if (sys_strcmp(str, "very_long") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_LONG; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_LONG; + } + else if (sys_strcmp(str, "long") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_LONG; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_LONG; + } + else if (sys_strcmp(str, "medium") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM; + } + else if (sys_strcmp(str, "short") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_SHORT; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_SHORT; + } + else if (sys_strcmp(str, "very_short") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_SHORT; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_VERY_SHORT; + } + else if (sys_strcmp(str, "none") == 0) { + sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_NONE; + aux_work_fact = ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_NONE; + } + else { + return EINVAL; + } + + sched_busy_wait.sys_schedule = sys_sched; + sched_busy_wait.tse = sys_sched*ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT; + sched_busy_wait.aux_work = sys_sched*aux_work_fact; + + return 0; +} static void -init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp) +init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp) { awdp->sched_id = esdp ? (int) esdp->no : 0; awdp->esdp = esdp; @@ -4186,15 +4604,39 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp) #endif awdp->async_ready.queue = NULL; #endif +#ifdef ERTS_SMP + if (!dawwp) { + awdp->delayed_wakeup.job = NULL; + awdp->delayed_wakeup.sched2jix = NULL; + awdp->delayed_wakeup.jix = -1; + } + else { + int i; + awdp->delayed_wakeup.job = (ErtsDelayedAuxWorkWakeupJob *) dawwp; + dawwp += sizeof(ErtsDelayedAuxWorkWakeupJob)*(erts_no_schedulers+1); + awdp->delayed_wakeup.sched2jix = (int *) dawwp; + awdp->delayed_wakeup.jix = -1; + for (i = 0; i <= erts_no_schedulers; i++) + awdp->delayed_wakeup.sched2jix[i] = -1; + } +#endif } void erts_init_scheduling(int no_schedulers, int no_schedulers_online) { int ix, n, no_ssi; + char *daww_ptr; +#ifdef ERTS_SMP + size_t daww_sz; +#endif init_misc_op_list_alloc(); +#ifdef ERTS_SMP + set_wakeup_other_data(); +#endif + ASSERT(no_schedulers_online <= no_schedulers); ASSERT(no_schedulers_online >= 1); ASSERT(no_schedulers >= 1); @@ -4310,6 +4752,15 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) /* Create and initialize scheduler specific data */ +#ifdef ERTS_SMP + daww_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE((sizeof(ErtsDelayedAuxWorkWakeupJob) + + sizeof(int))*(n+1)); + daww_ptr = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA, + daww_sz*n); +#else + daww_ptr = NULL; +#endif + erts_aligned_scheduler_data = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_SCHDLR_DATA, n*sizeof(ErtsAlignedSchedulerData)); @@ -4344,7 +4795,10 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) esdp->run_queue = ERTS_RUNQ_IX(ix); esdp->run_queue->scheduler = esdp; - init_aux_work_data(&esdp->aux_work_data, esdp); + init_aux_work_data(&esdp->aux_work_data, esdp, daww_ptr); +#ifdef ERTS_SMP + daww_ptr += daww_sz; +#endif init_sched_wall_time(&esdp->sched_wall_time); } @@ -4857,7 +5311,9 @@ suspend_scheduler(ErtsSchedulerData *esdp) sched_wall_time_change(esdp, 1); } if (aux_work) - aux_work = handle_aux_work(&esdp->aux_work_data, aux_work); + aux_work = handle_aux_work(&esdp->aux_work_data, + aux_work, + 1); if (aux_work && erts_thr_progress_update(esdp)) erts_thr_progress_leader_update(esdp); if (qmask) { @@ -6529,7 +6985,7 @@ Process *schedule(Process *p, int calls) if (leader_update) erts_thr_progress_leader_update(esdp); if (aux_work) - handle_aux_work(&esdp->aux_work_data, aux_work); + handle_aux_work(&esdp->aux_work_data, aux_work, 0); erts_smp_runq_lock(rq); } } @@ -6542,7 +6998,7 @@ Process *schedule(Process *p, int calls) erts_aint32_t aux_work; aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work); if (aux_work) - handle_aux_work(&esdp->aux_work_data, aux_work); + handle_aux_work(&esdp->aux_work_data, aux_work, 0); } #endif /* ERTS_SMP */ @@ -6570,7 +7026,7 @@ Process *schedule(Process *p, int calls) goto continue_check_activities_to_run; } - ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); + (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); /* * Check for ERTS_RUNQ_FLG_SUSPENDED has to be done @@ -6624,29 +7080,7 @@ Process *schedule(Process *p, int calls) exec_misc_ops(rq); #ifdef ERTS_SMP - { - int wo_reds = rq->wakeup_other_reds; - if (wo_reds) { - erts_aint32_t len = rq->len; - if (len < 2) { - rq->wakeup_other -= ERTS_WAKEUP_OTHER_DEC*wo_reds; - if (rq->wakeup_other < 0) - rq->wakeup_other = 0; - } - else if (rq->wakeup_other < wakeup_other_limit) - rq->wakeup_other += len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC; - else { - if (flags & ERTS_RUNQ_FLG_PROTECTED) - ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); - if (erts_smp_atomic32_read_acqb(&no_empty_run_queues) != 0) { - wake_scheduler_on_empty_runq(rq); - rq->wakeup_other = 0; - } - rq->wakeup_other = 0; - } - } - rq->wakeup_other_reds = 0; - } + wakeup_other.check(rq, flags); #endif /* @@ -6741,7 +7175,7 @@ Process *schedule(Process *p, int calls) erts_smp_runq_unlock(rq); if (flags & ERTS_RUNQ_FLG_PROTECTED) - ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); + (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED); ERTS_SMP_CHK_NO_PROC_LOCKS; @@ -6778,7 +7212,6 @@ Process *schedule(Process *p, int calls) /* Never run a suspended process */ ASSERT(!(ERTS_PSFLG_SUSPENDED & erts_smp_atomic32_read_nob(&p->state))); - ACTIVATE(p); reds = context_reds; if (IS_TRACED(p)) { @@ -6814,7 +7247,6 @@ Process *schedule(Process *p, int calls) } p->fcalls = reds; - ASSERT(IS_ACTIVE(p)); ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); return p; } @@ -7215,9 +7647,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). ErtsRunQueue *rq = NULL; Process *p; Sint arity; /* Number of arguments. */ -#ifndef HYBRID Uint arg_size; /* Size of arguments. */ -#endif Uint sz; /* Needed words on heap. */ Uint heap_need; /* Size needed on heap. */ Eterm res = THE_NON_VALUE; @@ -7228,17 +7658,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). erts_smp_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR); #endif -#ifdef HYBRID - /* - * Copy the arguments to the global heap - * Since global GC might occur we want to do this before adding the - * new process to the erts_proc.tab. - */ - BM_SWAP_TIMER(system,copy); - LAZY_COPY(parent,args); - BM_SWAP_TIMER(copy,system); - heap_need = 0; -#endif /* HYBRID */ /* * Check for errors. */ @@ -7277,12 +7696,10 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). #endif BM_COUNT(processes_spawned); -#ifndef HYBRID BM_SWAP_TIMER(system,size); arg_size = size_object(args); BM_SWAP_TIMER(size,system); heap_need = arg_size; -#endif p->flags = erts_default_process_flags; @@ -7327,9 +7744,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*sz); p->old_hend = p->old_htop = p->old_heap = NULL; p->high_water = p->heap; -#ifdef INCREMENTAL - p->scan_top = p->high_water; -#endif p->gen_gcs = 0; p->stop = p->hend = p->heap + sz; p->htop = p->heap; @@ -7355,19 +7769,10 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). BM_STOP_TIMER(system); BM_MESSAGE(args,p,parent); BM_START_TIMER(system); -#ifdef HYBRID - p->arg_reg[2] = args; -#ifdef INCREMENTAL - p->active = 0; - if (ptr_val(args) >= inc_fromspc && ptr_val(args) < inc_fromend) - INC_ACTIVATE(p); -#endif -#else BM_SWAP_TIMER(system,copy); p->arg_reg[2] = copy_struct(args, arg_size, &p->htop, &p->off_heap); BM_MESSAGE_COPIED(arg_size); BM_SWAP_TIMER(copy,system); -#endif p->arity = 3; p->fvalue = NIL; @@ -7424,13 +7829,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). #endif p->parent = parent->id == ERTS_INVALID_PID ? NIL : parent->id; -#ifdef HYBRID - p->rrma = NULL; - p->rrsrc = NULL; - p->nrr = 0; - p->rrsz = 0; -#endif - INIT_HOLE_CHECK(p); #ifdef DEBUG p->last_old_htop = NULL; @@ -7499,15 +7897,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). so->mref = mref; } -#ifdef HYBRID - /* - * Add process to the array of active processes. - */ - ACTIVATE(p); - p->active_index = erts_num_active_procs++; - erts_active_procs[p->active_index] = p; -#endif - #ifdef ERTS_SMP p->scheduler_data = NULL; p->suspendee = NIL; @@ -7591,9 +7980,6 @@ void erts_init_empty_process(Process *p) p->reg = NULL; p->heap_sz = 0; p->high_water = NULL; -#ifdef INCREMENTAL - p->scan_top = NULL; -#endif p->old_hend = NULL; p->old_htop = NULL; p->old_heap = NULL; @@ -7645,14 +8031,6 @@ void erts_init_empty_process(Process *p) #endif #endif - ACTIVATE(p); - -#ifdef HYBRID - p->rrma = NULL; - p->rrsrc = NULL; - p->nrr = 0; - p->rrsz = 0; -#endif INIT_HOLE_CHECK(p); #ifdef DEBUG p->last_old_htop = NULL; @@ -7698,9 +8076,6 @@ erts_debug_verify_clean_empty_process(Process* p) ASSERT(p->reg == NULL); ASSERT(p->heap_sz == 0); ASSERT(p->high_water == NULL); -#ifdef INCREMENTAL - ASSERT(p->scan_top == NULL); -#endif ASSERT(p->old_hend == NULL); ASSERT(p->old_htop == NULL); ASSERT(p->old_heap == NULL); @@ -7848,22 +8223,6 @@ delete_process(Process* p) ASSERT(!p->suspend_monitors); p->fvalue = NIL; - -#ifdef HYBRID - erts_active_procs[p->active_index] = - erts_active_procs[--erts_num_active_procs]; - erts_active_procs[p->active_index]->active_index = p->active_index; -#ifdef INCREMENTAL - if (INC_IS_ACTIVE(p)) - INC_DEACTIVATE(p); -#endif - - if (p->rrma != NULL) { - erts_free(ERTS_ALC_T_ROOTSET,p->rrma); - erts_free(ERTS_ALC_T_ROOTSET,p->rrsrc); - } -#endif - } static ERTS_INLINE erts_aint32_t @@ -8252,7 +8611,6 @@ send_exit_signal(Process *c_p, /* current process if and only ? rsn : copy_object(rsn, rp)), NULL); - ACTIVATE(rp); } #endif return -1; /* Receiver will exit */ @@ -9861,13 +10219,8 @@ init_processes_bif(void) + 1); /* processes_trap/2 is a hidden BIF that the processes/0 BIF traps to. */ - sys_memset((void *) &processes_trap_export, 0, sizeof(Export)); - processes_trap_export.address = &processes_trap_export.code[3]; - processes_trap_export.code[0] = am_erlang; - processes_trap_export.code[1] = am_processes_trap; - processes_trap_export.code[2] = 2; - processes_trap_export.code[3] = (BeamInstr) em_apply_bif; - processes_trap_export.code[4] = (BeamInstr) &processes_trap; + erts_init_trap_export(&processes_trap_export, am_erlang, am_processes_trap, 2, + &processes_trap); } diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 249f29e7e6..b8b137f413 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -270,18 +270,21 @@ typedef enum { * eachother. Most frequent - lowest bit number. */ -#define ERTS_SSI_AUX_WORK_DD (((erts_aint32_t) 1) << 0) -#define ERTS_SSI_AUX_WORK_DD_THR_PRGR (((erts_aint32_t) 1) << 1) -#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC (((erts_aint32_t) 1) << 2) -#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM (((erts_aint32_t) 1) << 3) -#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 4) -#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 5) -#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 6) -#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 7) -#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 8) -#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 9) -#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 10) -#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 11) +#define ERTS_SSI_AUX_WORK_DELAYED_AW_WAKEUP (((erts_aint32_t) 1) << 0) +#define ERTS_SSI_AUX_WORK_DD (((erts_aint32_t) 1) << 1) +#define ERTS_SSI_AUX_WORK_DD_THR_PRGR (((erts_aint32_t) 1) << 2) +#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC (((erts_aint32_t) 1) << 3) +#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM (((erts_aint32_t) 1) << 4) +#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 5) +#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 6) +#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 7) +#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 8) +#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 9) +#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 10) +#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 11) +#define ERTS_SSI_AUX_WORK_CODE_IX_ACTIVATION (((erts_aint32_t) 1) << 12) +#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 13) +#define ERTS_SSI_AUX_WORK_FINISH_BP (((erts_aint32_t) 1) << 14) typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; @@ -437,6 +440,11 @@ typedef struct { } ErtsSchedWallTime; typedef struct { + int sched; + erts_aint32_t aux_work; +} ErtsDelayedAuxWorkWakeupJob; + +typedef struct { int sched_id; ErtsSchedulerData *esdp; ErtsSchedulerSleepInfo *ssi; @@ -465,6 +473,21 @@ typedef struct { void *queue; } async_ready; #endif +#ifdef ERTS_SMP + struct { + Process* code_stager; + ErtsThrPrgrVal thr_prgr; + } code_ix_activation; + struct { + Process* stager; + ErtsThrPrgrVal thr_prgr; + } bp_ix_activation; + struct { + int *sched2jix; + int jix; + ErtsDelayedAuxWorkWakeupJob *job; + } delayed_wakeup; +#endif } ErtsAuxWorkData; struct ErtsSchedulerData_ { @@ -498,7 +521,6 @@ struct ErtsSchedulerData_ { int virtual_reds; int cpu_id; /* >= 0 when bound */ ErtsAuxWorkData aux_work_data; - ErtsAtomCacheMap atom_cache_map; ErtsSchedAllocData alloc_data; @@ -861,24 +883,6 @@ struct process { #endif #endif -#ifdef HYBRID - Eterm *rrma; /* Remembered roots to Message Area */ - Eterm **rrsrc; /* The source of the root */ - Uint nrr; /* Number of remembered roots */ - Uint rrsz; /* Size of root array */ -#endif - -#ifdef HYBRID - Uint active; /* Active since last major collection? */ - Uint active_index; /* Index in the active process array */ -#endif - -#ifdef INCREMENTAL - Process *active_next; /* Active processes to scan for roots */ - Process *active_prev; /* in collection of the message area */ - Eterm *scan_top; -#endif - #ifdef CHECK_FOR_HOLES Eterm* last_htop; /* No need to scan the heap below this point. */ ErlHeapFragment* last_mbuf; /* No need to scan beyond this mbuf. */ @@ -1024,10 +1028,6 @@ Eterm* erts_set_hole_marker(Eterm* ptr, Uint sz); extern erts_smp_rwmtx_t erts_proc_tab_rwmtx; extern erts_smp_atomic_t *erts_proc_tab; -#ifdef HYBRID -extern Uint erts_num_active_procs; -extern Process** erts_active_procs; -#endif extern Uint erts_default_process_flags; extern erts_smp_rwmtx_t erts_cpu_bind_rwmtx; /* If any of the erts_system_monitor_* variables are set (enabled), @@ -1171,7 +1171,9 @@ ErtsProcList *erts_proclist_create(Process *); void erts_proclist_destroy(ErtsProcList *); int erts_proclist_same(ErtsProcList *, Process *); -int erts_sched_set_wakeup_limit(char *str); +int erts_sched_set_wakeup_other_thresold(char *str); +int erts_sched_set_wakeup_other_type(char *str); +int erts_sched_set_busy_wait_threshold(char *str); #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) int erts_dbg_check_halloc_lock(Process *p); @@ -1199,6 +1201,10 @@ void erts_smp_notify_check_children_needed(void); #if ERTS_USE_ASYNC_READY_Q void erts_notify_check_async_ready_queue(void *); #endif +#ifdef ERTS_SMP +void erts_notify_code_ix_activation(Process* p, ErtsThrPrgrVal later); +void erts_notify_finish_breakpointing(Process* p); +#endif void erts_schedule_misc_aux_work(int sched_id, void (*func)(void *), void *arg); @@ -1557,10 +1563,14 @@ ERTS_GLB_INLINE Eterm erts_get_current_pid(void); ERTS_GLB_INLINE Uint erts_get_scheduler_id(void); ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_proc(Process *p); ERTS_GLB_INLINE ErtsRunQueue *erts_get_runq_current(ErtsSchedulerData *esdp); +#ifndef ERTS_ENABLE_LOCK_COUNT ERTS_GLB_INLINE void erts_smp_runq_lock(ErtsRunQueue *rq); +#endif ERTS_GLB_INLINE int erts_smp_runq_trylock(ErtsRunQueue *rq); ERTS_GLB_INLINE void erts_smp_runq_unlock(ErtsRunQueue *rq); +#ifndef ERTS_ENABLE_LOCK_COUNT ERTS_GLB_INLINE void erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq); +#endif ERTS_GLB_INLINE void erts_smp_xrunq_unlock(ErtsRunQueue *rq, ErtsRunQueue *xrq); ERTS_GLB_INLINE void erts_smp_runqs_lock(ErtsRunQueue *rq1, ErtsRunQueue *rq2); ERTS_GLB_INLINE void erts_smp_runqs_unlock(ErtsRunQueue *rq1, ErtsRunQueue *rq2); @@ -1625,6 +1635,12 @@ erts_get_runq_current(ErtsSchedulerData *esdp) #endif } +#ifdef ERTS_ENABLE_LOCK_COUNT + +#define erts_smp_runq_lock(rq) erts_smp_mtx_lock_x(&(rq)->mtx, __FILE__, __LINE__) + +#else + ERTS_GLB_INLINE void erts_smp_runq_lock(ErtsRunQueue *rq) { @@ -1633,6 +1649,8 @@ erts_smp_runq_lock(ErtsRunQueue *rq) #endif } +#endif + ERTS_GLB_INLINE int erts_smp_runq_trylock(ErtsRunQueue *rq) { @@ -1651,6 +1669,31 @@ erts_smp_runq_unlock(ErtsRunQueue *rq) #endif } +#ifdef ERTS_ENABLE_LOCK_COUNT + +#define erts_smp_xrunq_lock(rq, xrq) erts_smp_xrunq_lock_x((rq), (xrq), __FILE__, __LINE__) + +ERTS_GLB_INLINE void +erts_smp_xrunq_lock_x(ErtsRunQueue *rq, ErtsRunQueue *xrq, char* file, int line) +{ +#ifdef ERTS_SMP + ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&rq->mtx)); + if (xrq != rq) { + if (erts_smp_mtx_trylock(&xrq->mtx) == EBUSY) { + if (rq < xrq) + erts_smp_mtx_lock_x(&xrq->mtx, file, line); + else { + erts_smp_mtx_unlock(&rq->mtx); + erts_smp_mtx_lock_x(&xrq->mtx, file, line); + erts_smp_mtx_lock_x(&rq->mtx, file, line); + } + } + } +#endif +} + +#else + ERTS_GLB_INLINE void erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq) { @@ -1670,6 +1713,8 @@ erts_smp_xrunq_lock(ErtsRunQueue *rq, ErtsRunQueue *xrq) #endif } +#endif + ERTS_GLB_INLINE void erts_smp_xrunq_unlock(ErtsRunQueue *rq, ErtsRunQueue *xrq) { diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index b864340782..aabeeca87a 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -92,16 +92,6 @@ static void check_queue(erts_proc_lock_t *lck); #error "The size of the 'uflgs' field of the erts_tse_t type is too small" #endif -struct erts_proc_lock_queues_t_ { - erts_proc_lock_queues_t *next; - erts_tse_t *queue[ERTS_PROC_LOCK_MAX_BIT+1]; -}; - -static erts_proc_lock_queues_t zeroqs = {0}; - -static erts_smp_spinlock_t qs_lock; -static erts_proc_lock_queues_t *queue_free_list; - static int proc_lock_spin_count; static int aux_thr_proc_lock_spin_count; @@ -134,8 +124,6 @@ erts_init_proc_lock(int cpus) #endif } #if ERTS_PROC_LOCK_OWN_IMPL - erts_smp_spinlock_init(&qs_lock, "proc_lck_qs_alloc"); - queue_free_list = NULL; erts_thr_install_exit_handler(cleanup_tse); if (cpus > 1) { proc_lock_spin_count = ERTS_PROC_LOCK_SPIN_COUNT_BASE; @@ -165,16 +153,7 @@ erts_init_proc_lock(int cpus) #if ERTS_PROC_LOCK_OWN_IMPL #ifdef ERTS_ENABLE_LOCK_CHECK -static void -check_unused_tse(erts_tse_t *wtr) -{ - int i; - erts_proc_lock_queues_t *queues = wtr->udata; - ERTS_LC_ASSERT(wtr->uflgs == 0); - for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) - ERTS_LC_ASSERT(!queues->queue[i]); -} -#define CHECK_UNUSED_TSE(W) check_unused_tse((W)) +#define CHECK_UNUSED_TSE(W) ERTS_LC_ASSERT((W)->uflgs == 0) #else #define CHECK_UNUSED_TSE(W) #endif @@ -183,49 +162,14 @@ static ERTS_INLINE erts_tse_t * tse_fetch(erts_pix_lock_t *pix_lock) { erts_tse_t *tse = erts_tse_fetch(); - if (!tse->udata) { - erts_proc_lock_queues_t *qs; -#if ERTS_PROC_LOCK_SPINLOCK_IMPL && !ERTS_PROC_LOCK_ATOMIC_IMPL - if (pix_lock) - erts_pix_unlock(pix_lock); -#endif - erts_smp_spin_lock(&qs_lock); - qs = queue_free_list; - if (qs) { - queue_free_list = queue_free_list->next; - erts_smp_spin_unlock(&qs_lock); - } - else { - erts_smp_spin_unlock(&qs_lock); - qs = erts_alloc(ERTS_ALC_T_PROC_LCK_QS, - sizeof(erts_proc_lock_queues_t)); - sys_memcpy((void *) qs, - (void *) &zeroqs, - sizeof(erts_proc_lock_queues_t)); - } - tse->udata = qs; -#if ERTS_PROC_LOCK_SPINLOCK_IMPL && !ERTS_PROC_LOCK_ATOMIC_IMPL - if (pix_lock) - erts_pix_lock(pix_lock); -#endif - } tse->uflgs = 0; return tse; } static ERTS_INLINE void -tse_return(erts_tse_t *tse, int force_free_q) +tse_return(erts_tse_t *tse) { CHECK_UNUSED_TSE(tse); - if (force_free_q || erts_tse_is_tmp(tse)) { - erts_proc_lock_queues_t *qs = tse->udata; - ASSERT(qs); - erts_smp_spin_lock(&qs_lock); - qs->next = queue_free_list; - queue_free_list = qs; - erts_smp_spin_unlock(&qs_lock); - tse->udata = NULL; - } erts_tse_return(tse); } @@ -233,55 +177,49 @@ static void cleanup_tse(void) { erts_tse_t *tse = erts_tse_fetch(); - if (tse) { - if (tse->udata) - tse_return(tse, 1); - else - erts_tse_return(tse); - } + if (tse) + erts_tse_return(tse); } /* * Waiters are queued in a circular double linked list; - * where qs->queue[lock_ix] is the first waiter in queue, and - * qs->queue[lock_ix]->prev is the last waiter in queue. + * where lck->queue[lock_ix] is the first waiter in queue, and + * lck->queue[lock_ix]->prev is the last waiter in queue. */ static ERTS_INLINE void -enqueue_waiter(erts_proc_lock_queues_t *qs, - int ix, - erts_tse_t *wtr) +enqueue_waiter(erts_proc_lock_t *lck, int ix, erts_tse_t *wtr) { - if (!qs->queue[ix]) { - qs->queue[ix] = wtr; + if (!lck->queue[ix]) { + lck->queue[ix] = wtr; wtr->next = wtr; wtr->prev = wtr; } else { - ERTS_LC_ASSERT(qs->queue[ix]->next && qs->queue[ix]->prev); - wtr->next = qs->queue[ix]; - wtr->prev = qs->queue[ix]->prev; + ERTS_LC_ASSERT(lck->queue[ix]->next && lck->queue[ix]->prev); + wtr->next = lck->queue[ix]; + wtr->prev = lck->queue[ix]->prev; wtr->prev->next = wtr; - qs->queue[ix]->prev = wtr; + lck->queue[ix]->prev = wtr; } } static erts_tse_t * -dequeue_waiter(erts_proc_lock_queues_t *qs, int ix) +dequeue_waiter(erts_proc_lock_t *lck, int ix) { - erts_tse_t *wtr = qs->queue[ix]; - ERTS_LC_ASSERT(qs->queue[ix]); + erts_tse_t *wtr = lck->queue[ix]; + ERTS_LC_ASSERT(lck->queue[ix]); if (wtr->next == wtr) { - ERTS_LC_ASSERT(qs->queue[ix]->prev == wtr); - qs->queue[ix] = NULL; + ERTS_LC_ASSERT(lck->queue[ix]->prev == wtr); + lck->queue[ix] = NULL; } else { ERTS_LC_ASSERT(wtr->next != wtr); ERTS_LC_ASSERT(wtr->prev != wtr); wtr->next->prev = wtr->prev; wtr->prev->next = wtr->next; - qs->queue[ix] = wtr->next; + lck->queue[ix] = wtr->next; } return wtr; } @@ -302,19 +240,18 @@ try_aquire(erts_proc_lock_t *lck, erts_tse_t *wtr) ErtsProcLocks locks = wtr->uflgs; int lock_no; - ERTS_LC_ASSERT(lck->queues); ERTS_LC_ASSERT(got_locks != locks); for (lock_no = 0; lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) { ErtsProcLocks lock = ((ErtsProcLocks) 1) << lock_no; if (locks & lock) { ErtsProcLocks wflg, old_lflgs; - if (lck->queues->queue[lock_no]) { + if (lck->queue[lock_no]) { /* Others already waiting */ enqueue: ERTS_LC_ASSERT(ERTS_PROC_LOCK_FLGS_READ_(lck) & (lock << ERTS_PROC_LOCK_WAITER_SHIFT)); - enqueue_waiter(lck->queues, lock_no, wtr); + enqueue_waiter(lck, lock_no, wtr); break; } wflg = lock << ERTS_PROC_LOCK_WAITER_SHIFT; @@ -366,7 +303,6 @@ transfer_locks(Process *p, for (lock_no = 0; tlocks && lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) { ErtsProcLocks lock = ((ErtsProcLocks) 1) << lock_no; if (tlocks & lock) { - erts_proc_lock_queues_t *qs = p->lock.queues; /* Transfer lock */ #ifdef ERTS_ENABLE_LOCK_CHECK tlocks &= ~lock; @@ -374,9 +310,9 @@ transfer_locks(Process *p, ERTS_LC_ASSERT(ERTS_PROC_LOCK_FLGS_READ_(&p->lock) & (lock << ERTS_PROC_LOCK_WAITER_SHIFT)); transferred++; - wtr = dequeue_waiter(qs, lock_no); + wtr = dequeue_waiter(&p->lock, lock_no); ERTS_LC_ASSERT(wtr); - if (!qs->queue[lock_no]) + if (!p->lock.queue[lock_no]) unset_waiter |= lock; ERTS_LC_ASSERT(wtr->uflgs & lock); wtr->uflgs &= ~lock; @@ -465,7 +401,6 @@ wait_for_locks(Process *p, { erts_pix_lock_t *pix_lock = pixlck ? pixlck : ERTS_PID2PIXLOCK(p->id); erts_tse_t *wtr; - erts_proc_lock_queues_t *qs; /* Acquire a waiter object on which this thread can wait. */ wtr = tse_fetch(pix_lock); @@ -481,18 +416,6 @@ wait_for_locks(Process *p, ERTS_LC_ASSERT(erts_lc_pix_lock_is_locked(pix_lock)); - qs = wtr->udata; - ASSERT(qs); - /* Provide the process with waiter queues, if it doesn't have one. */ - if (!p->lock.queues) { - qs->next = NULL; - p->lock.queues = qs; - } - else { - qs->next = p->lock.queues->next; - p->lock.queues->next = qs; - } - #ifdef ERTS_PROC_LOCK_HARD_DEBUG check_queue(&p->lock); #endif @@ -506,7 +429,9 @@ wait_for_locks(Process *p, check_queue(&p->lock); #endif - if (wtr->uflgs) { + if (wtr->uflgs == 0) + erts_pix_unlock(pix_lock); + else { /* We didn't get them all; need to wait... */ ASSERT((wtr->uflgs & ~ERTS_PROC_LOCKS_ALL) == 0); @@ -531,28 +456,12 @@ wait_for_locks(Process *p, } while (res != 0); } - erts_pix_lock(pix_lock); - ASSERT(wtr->uflgs == 0); } - /* Recover some queues to store in the waiter. */ - ERTS_LC_ASSERT(p->lock.queues); - if (p->lock.queues->next) { - qs = p->lock.queues->next; - p->lock.queues->next = qs->next; - } - else { - qs = p->lock.queues; - p->lock.queues = NULL; - } - wtr->udata = qs; - - erts_pix_unlock(pix_lock); - ERTS_LC_ASSERT(locks == (ERTS_PROC_LOCK_FLGS_READ_(&p->lock) & locks)); - tse_return(wtr, 0); + tse_return(wtr); } /* @@ -659,7 +568,7 @@ void erts_proc_lock_prepare_proc_lock_waiter(void) { #if ERTS_PROC_LOCK_OWN_IMPL - tse_return(tse_fetch(NULL), 0); + tse_return(tse_fetch(NULL)); #endif } @@ -1089,6 +998,7 @@ void erts_proc_lock_init(Process *p) { #if ERTS_PROC_LOCK_OWN_IMPL + int i; /* We always start with all locks locked */ #if ERTS_PROC_LOCK_ATOMIC_IMPL erts_smp_atomic32_init_nob(&p->lock.flags, @@ -1096,7 +1006,8 @@ erts_proc_lock_init(Process *p) #else p->lock.flags = ERTS_PROC_LOCKS_ALL; #endif - p->lock.queues = NULL; + for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) + p->lock.queue[i] = NULL; #ifdef ERTS_ENABLE_LOCK_CHECK erts_proc_lc_trylock(p, ERTS_PROC_LOCKS_ALL, 1); #endif @@ -1124,11 +1035,8 @@ erts_proc_lock_init(Process *p) #endif erts_atomic32_init_nob(&p->lock.refc, 1); #ifdef ERTS_PROC_LOCK_DEBUG - { - int i; - for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) - erts_smp_atomic32_init_nob(&p->lock.locked[i], (erts_aint32_t) 1); - } + for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) + erts_smp_atomic32_init_nob(&p->lock.locked[i], (erts_aint32_t) 1); #endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_proc_lock_init(p); @@ -1155,7 +1063,7 @@ erts_proc_lock_fin(Process *p) #if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT) void erts_lcnt_proc_lock_init(Process *p) { - + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { if (p->id != ERTS_INVALID_PID) { erts_lcnt_init_lock_x(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK, p->id); erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK, p->id); @@ -1167,6 +1075,12 @@ void erts_lcnt_proc_lock_init(Process *p) { erts_lcnt_init_lock(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK); erts_lcnt_init_lock(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK); } + } else { + sys_memzero(&(p->lock.lcnt_main), sizeof(p->lock.lcnt_main)); + sys_memzero(&(p->lock.lcnt_msgq), sizeof(p->lock.lcnt_msgq)); + sys_memzero(&(p->lock.lcnt_link), sizeof(p->lock.lcnt_link)); + sys_memzero(&(p->lock.lcnt_status), sizeof(p->lock.lcnt_status)); + } } @@ -1261,6 +1175,26 @@ void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res } } + +void erts_lcnt_enable_proc_lock_count(int enable) { + int i; + + for (i = 0; i < erts_max_processes; ++i) { + Process* p = erts_pix2proc(i); + if (p) { + if (enable) { + if (!ERTS_LCNT_LOCK_TYPE(&(p->lock.lcnt_main))) { + erts_lcnt_proc_lock_init(p); + } + } else { + if (ERTS_LCNT_LOCK_TYPE(&(p->lock.lcnt_main))) { + erts_lcnt_proc_lock_destroy(p); + } + } + } + } +} + #endif /* ifdef ERTS_ENABLE_LOCK_COUNT */ @@ -1675,21 +1609,21 @@ check_queue(erts_proc_lock_t *lck) if (lflgs & wtr) { int n; erts_tse_t *wtr; - ERTS_LC_ASSERT(lck->queues && lck->queues->queue[lock_no]); - wtr = lck->queues->queue[lock_no]; + ERTS_LC_ASSERT(lck->queue[lock_no]); + wtr = lck->queue[lock_no]; n = 0; do { wtr = wtr->next; n++; - } while (wtr != lck->queues->queue[lock_no]); + } while (wtr != lck->queue[lock_no]); do { wtr = wtr->prev; n--; - } while (wtr != lck->queues->queue[lock_no]); + } while (wtr != lck->queue[lock_no]); ERTS_LC_ASSERT(n == 0); } else { - ERTS_LC_ASSERT(!lck->queues || !lck->queues->queue[lock_no]); + ERTS_LC_ASSERT(!lck->queue[lock_no]); } } } diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 015fda583e..ca853bf347 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -69,8 +69,6 @@ typedef erts_aint32_t ErtsProcLocks; -typedef struct erts_proc_lock_queues_t_ erts_proc_lock_queues_t; - typedef struct erts_proc_lock_t_ { #if ERTS_PROC_LOCK_OWN_IMPL #if ERTS_PROC_LOCK_ATOMIC_IMPL @@ -78,7 +76,7 @@ typedef struct erts_proc_lock_t_ { #else ErtsProcLocks flags; #endif - erts_proc_lock_queues_t *queues; + erts_tse_t *queue[ERTS_PROC_LOCK_MAX_BIT+1]; #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock_t lcnt_main; erts_lcnt_lock_t lcnt_link; @@ -232,6 +230,8 @@ void erts_lcnt_proc_lock_unaquire(erts_proc_lock_t *lock, ErtsProcLocks locks); void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks); void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res); +void erts_lcnt_enable_proc_lock_count(int enable); + #endif /* ERTS_ENABLE_LOCK_COUNT*/ diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c index bff9d246a3..a490aec734 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c @@ -116,54 +116,84 @@ erts_sspa_create(size_t blk_sz, int pa_size) return data; } -static ERTS_INLINE erts_aint_t +static ERTS_INLINE void enqueue_remote_managed_thread(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *this, - int want_last) + int cinit) { - erts_aint_t ilast, itmp; + erts_aint_t itmp; + erts_sspa_blk_t *enq; erts_atomic_init_nob(&this->next_atmc, ERTS_AINT_NULL); - /* Enqueue at end of list... */ - ilast = erts_atomic_read_nob(&chdr->tail.data.last); - while (1) { - erts_sspa_blk_t *last = (erts_sspa_blk_t *) ilast; - itmp = erts_atomic_cmpxchg_mb(&last->next_atmc, - (erts_aint_t) this, - ERTS_AINT_NULL); - if (itmp == ERTS_AINT_NULL) - break; - ilast = itmp; + enq = (erts_sspa_blk_t *) erts_atomic_read_nob(&chdr->tail.data.last); + itmp = erts_atomic_cmpxchg_relb(&enq->next_atmc, + (erts_aint_t) this, + ERTS_AINT_NULL); + if (itmp == ERTS_AINT_NULL) { + /* We are required to move last pointer */ +#ifdef DEBUG + ASSERT(ERTS_AINT_NULL == erts_atomic_read_nob(&this->next_atmc)); + ASSERT(((erts_aint_t) enq) + == erts_atomic_xchg_relb(&chdr->tail.data.last, + (erts_aint_t) this)); +#else + erts_atomic_set_relb(&chdr->tail.data.last, (erts_aint_t) this); +#endif } + else { + /* + * We *need* to insert element somewhere in between the + * last element we read earlier and the actual last element. + */ + int i = cinit; - /* Move last pointer forward... */ - while (1) { - erts_aint_t itmp; - if (want_last) { - if (erts_atomic_read_rb(&this->next_atmc) != ERTS_AINT_NULL) { - /* Someone else will move it forward */ - return erts_atomic_read_nob(&chdr->tail.data.last); + while (1) { + erts_aint_t itmp2; + erts_atomic_set_nob(&this->next_atmc, itmp); + itmp2 = erts_atomic_cmpxchg_relb(&enq->next_atmc, + (erts_aint_t) this, + itmp); + if (itmp == itmp2) + break; /* inserted this */ + if ((i & 1) == 0) + itmp = itmp2; + else { + enq = (erts_sspa_blk_t *) itmp; + itmp = erts_atomic_read_acqb(&enq->next_atmc); + ASSERT(itmp != ERTS_AINT_NULL); } + i++; } - else { - if (erts_atomic_read_nob(&this->next_atmc) != ERTS_AINT_NULL) { - /* Someone else will move it forward */ - return ERTS_AINT_NULL; - } + } +} + +static ERTS_INLINE erts_aint_t +check_insert_marker(erts_sspa_chunk_header_t *chdr, erts_aint_t ilast) +{ + if (!chdr->head.used_marker + && chdr->head.unref_end == (erts_sspa_blk_t *) ilast) { + erts_aint_t itmp; + erts_sspa_blk_t *last = (erts_sspa_blk_t *) ilast; + + erts_atomic_init_nob(&chdr->tail.data.marker.next_atmc, ERTS_AINT_NULL); + itmp = erts_atomic_cmpxchg_relb(&last->next_atmc, + (erts_aint_t) &chdr->tail.data.marker, + ERTS_AINT_NULL); + if (itmp == ERTS_AINT_NULL) { + ilast = (erts_aint_t) &chdr->tail.data.marker; + chdr->head.used_marker = !0; + erts_atomic_set_relb(&chdr->tail.data.last, ilast); } - itmp = erts_atomic_cmpxchg_mb(&chdr->tail.data.last, - (erts_aint_t) this, - ilast); - if (ilast == itmp) - return want_last ? (erts_aint_t) this : ERTS_AINT_NULL; - ilast = itmp; } + return ilast; } void -erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *blk) +erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, + erts_sspa_blk_t *blk, + int cinit) { int um_refc_ix = 0; int managed_thread = erts_thr_progress_is_managed_thread(); @@ -180,7 +210,7 @@ erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *blk) } } - (void) enqueue_remote_managed_thread(chdr, blk, 0); + enqueue_remote_managed_thread(chdr, blk, cinit); if (!managed_thread) erts_atomic_dec_relb(&chdr->tail.data.um_refc[um_refc_ix]); @@ -208,26 +238,19 @@ fetch_remote(erts_sspa_chunk_header_t *chdr, int max) int um_refc_ix; chdr->head.next.thr_progress_reached = 1; um_refc_ix = chdr->head.next.um_refc_ix; - if (erts_atomic_read_acqb(&chdr->tail.data.um_refc[um_refc_ix]) == 0) { + if (erts_atomic_read_nob(&chdr->tail.data.um_refc[um_refc_ix]) == 0) { + + ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore); /* Move unreferenced end pointer forward... */ chdr->head.unref_end = chdr->head.next.unref_end; - if (!chdr->head.used_marker - && chdr->head.unref_end == (erts_sspa_blk_t *) ilast) { - /* Need to equeue marker */ - chdr->head.used_marker = 1; - ilast = enqueue_remote_managed_thread(chdr, - &chdr->tail.data.marker, - 1); - } + ilast = check_insert_marker(chdr, ilast); - if (chdr->head.unref_end == (erts_sspa_blk_t *) ilast) - ERTS_THR_MEMORY_BARRIER; - else { + if (chdr->head.unref_end != (erts_sspa_blk_t *) ilast) { chdr->head.next.unref_end = (erts_sspa_blk_t *) ilast; - chdr->head.next.thr_progress = erts_thr_progress_later(); + chdr->head.next.thr_progress = erts_thr_progress_later(NULL); erts_atomic32_set_relb(&chdr->tail.data.um_refc_ix, um_refc_ix); chdr->head.next.um_refc_ix = um_refc_ix == 0 ? 1 : 0; diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h index d36066c399..bccb1aba7a 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.h +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.h @@ -142,7 +142,8 @@ check_local_list(erts_sspa_chunk_header_t *chdr) erts_sspa_data_t *erts_sspa_create(size_t blk_sz, int pa_size); void erts_sspa_remote_free(erts_sspa_chunk_header_t *chdr, - erts_sspa_blk_t *blk); + erts_sspa_blk_t *blk, + int cinit); erts_sspa_blk_t *erts_sspa_process_remote_frees(erts_sspa_chunk_header_t *chdr, erts_sspa_blk_t *old_res); @@ -216,7 +217,7 @@ erts_sspa_free(erts_sspa_data_t *data, int cix, char *cblk) chdr = &chnk->aligned.header; if (chnk_cix != cix) { /* Remote chunk */ - erts_sspa_remote_free(chdr, blk); + erts_sspa_remote_free(chdr, blk, chnk_cix - cix); } else { /* Local chunk */ diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 9ef83746c5..88524bdd4c 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -891,16 +891,16 @@ has_reached_wakeup(ErtsThrPrgrVal wakeup) ErtsThrPrgrVal limit; /* * erts_thr_progress_later() returns values which are - * equal to 'current + 2'. That is, users should never - * get a hold of values larger than that. + * equal to 'current + 2', or 'current + 3'. That is, users + * should never get a hold of values larger than that. * - * That is, valid values are values less than 'current + 3'. + * That is, valid values are values less than 'current + 4'. * * Values larger than this won't work with the wakeup * algorithm. */ - limit = current + 3; + limit = current + 4; if (limit == ERTS_THR_PRGR_VAL_WAITING) limit = 0; else if (limit < current) /* Wrapped */ diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h index a71724b813..89486b065b 100644 --- a/erts/emulator/beam/erl_thr_progress.h +++ b/erts/emulator/beam/erl_thr_progress.h @@ -139,11 +139,12 @@ ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_mb__(ERTS_THR_PRGR_ATOMIC *atm ERTS_GLB_INLINE int erts_thr_progress_is_managed_thread(void); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_current_to_later__(ErtsThrPrgrVal val); -ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_later_than(ErtsThrPrgrVal val); -ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_later(void); +ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_later(ErtsSchedulerData *); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_current(void); ERTS_GLB_INLINE int erts_thr_progress_has_passed__(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2); ERTS_GLB_INLINE int erts_thr_progress_has_reached_this(ErtsThrPrgrVal this, ErtsThrPrgrVal val); +ERTS_GLB_INLINE int erts_thr_progress_equal(ErtsThrPrgrVal val1, + ErtsThrPrgrVal val2); ERTS_GLB_INLINE int erts_thr_progress_cmp(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2); ERTS_GLB_INLINE int erts_thr_progress_has_reached(ErtsThrPrgrVal val); @@ -230,16 +231,23 @@ erts_thr_progress_current_to_later__(ErtsThrPrgrVal val) } ERTS_GLB_INLINE ErtsThrPrgrVal -erts_thr_progress_later_than(ErtsThrPrgrVal val) +erts_thr_progress_later(ErtsSchedulerData *esdp) { - ERTS_THR_MEMORY_BARRIER; - return erts_thr_progress_current_to_later__(val); -} - -ERTS_GLB_INLINE ErtsThrPrgrVal -erts_thr_progress_later(void) -{ - ErtsThrPrgrVal val = erts_thr_prgr_read_mb__(&erts_thr_prgr__.current); + ErtsThrPrgrData *tpd; + ErtsThrPrgrVal val; + if (esdp) { + tpd = &esdp->thr_progress_data; + managed_thread: + val = tpd->previous.local; + ERTS_THR_MEMORY_BARRIER; + } + else { + tpd = erts_tsd_get(erts_thr_prgr_data_key__); + if (tpd && tpd->is_managed) + goto managed_thread; + val = erts_thr_prgr_read_mb__(&erts_thr_prgr__.current); + } + ASSERT(val != ERTS_THR_PRGR_VAL_WAITING); return erts_thr_progress_current_to_later__(val); } @@ -279,6 +287,12 @@ erts_thr_progress_has_reached_this(ErtsThrPrgrVal this, ErtsThrPrgrVal val) } ERTS_GLB_INLINE int +erts_thr_progress_equal(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2) +{ + return val1 == val2 && val1 != ERTS_THR_PRGR_INVALID; +} + +ERTS_GLB_INLINE int erts_thr_progress_cmp(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2) { if (val1 == val2) diff --git a/erts/emulator/beam/erl_thr_queue.c b/erts/emulator/beam/erl_thr_queue.c index 70949ece76..f07964a265 100644 --- a/erts/emulator/beam/erl_thr_queue.c +++ b/erts/emulator/beam/erl_thr_queue.c @@ -422,7 +422,7 @@ clean(ErtsThrQ_t *q, int max_ops, int do_notify) else { q->head.next.unref_end = (ErtsThrQElement_t *) ilast; #ifdef ERTS_SMP - q->head.next.thr_progress = erts_thr_progress_later(); + q->head.next.thr_progress = erts_thr_progress_later(NULL); #endif erts_atomic32_set_relb(&q->tail.data.um_refc_ix, um_refc_ix); diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index bc988cd61b..d04a91f18c 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -2134,187 +2134,6 @@ void save_calls(Process *p, Export *e) } } -/* - * Entry point called by the trace wrap functions in erl_bif_wrap.c - * - * The trace wrap functions are themselves called through the export - * entries instead of the original BIF functions. - */ -Eterm -erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I) -{ - Eterm result; - int meta = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_META); - - ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); - - if (!ARE_TRACE_FLAGS_ON(p, F_TRACE_CALLS) && (! meta)) { - /* Warning! This is an Optimization. - * - * If neither meta trace is active nor process trace flags then - * no tracing will occur. Doing the whole else branch will - * also do nothing, only slower. - */ - Eterm (*func)(Process*, Eterm*, BeamInstr*) = bif_table[bif_index].f; - result = func(p, args, I); - } else { - Eterm (*func)(Process*, Eterm*, BeamInstr*); - Export* ep = bif_export[bif_index]; - Uint32 flags = 0, flags_meta = 0; - int global = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_GLOBAL); - int local = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_LOCAL); - int time = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_CALL_TIME); - Eterm meta_tracer_pid = NIL; - int applying = (I == &(ep->code[3])); /* Yup, the apply code for a bif - * is actually in the - * export entry */ - BeamInstr *cp = p->cp; - - /* - * Make continuation pointer OK, it is not during direct BIF calls, - * but it is correct during apply of bif. - */ - if (!applying) { - p->cp = I; - } - if (global || local) { - flags = erts_call_trace(p, ep->code, ep->match_prog_set, args, - local, &p->tracer_proc); - } - if (meta) { - flags_meta = erts_bif_mtrace(p, ep->code+3, args, local, - &meta_tracer_pid); - } - if (time) { - BpDataTime *bdt = NULL; - BeamInstr *pc = (BeamInstr *)ep->code+3; - - bdt = (BpDataTime *) erts_get_time_break(p, pc); - ASSERT(bdt); - - if (!bdt->pause) { - erts_trace_time_break(p, pc, bdt, ERTS_BP_CALL_TIME_CALL); - } - } - /* Restore original continuation pointer (if changed). */ - p->cp = cp; - - func = bif_table[bif_index].f; - - result = func(p, args, I); - - if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) { - BeamInstr i_return_trace = beam_return_trace[0]; - BeamInstr i_return_to_trace = beam_return_to_trace[0]; - BeamInstr i_return_time_trace = beam_return_time_trace[0]; - Eterm *cpp; - /* Maybe advance cp to skip trace stack frames */ - for (cpp = p->stop; ; cp = cp_val(*cpp++)) { - if (*cp == i_return_trace) { - /* Skip stack frame variables */ - while (is_not_CP(*cpp)) cpp++; - cpp += 2; /* Skip return_trace parameters */ - } else if (*cp == i_return_time_trace) { - /* Skip stack frame variables */ - while (is_not_CP(*cpp)) cpp++; - cpp += 1; /* Skip return_time_trace parameters */ - } else if (*cp == i_return_to_trace) { - /* A return_to trace message is going to be generated - * by normal means, so we do not have to. - */ - cp = NULL; - break; - } else break; - } - } - - /* Try to get these in the order - * they usually appear in normal code... */ - if (is_non_value(result)) { - Uint reason = p->freason; - if (reason != TRAP) { - Eterm class; - Eterm value = p->fvalue; - DeclareTmpHeapNoproc(nocatch,3); - UseTmpHeapNoproc(3); - /* Expand error value like in handle_error() */ - if (reason & EXF_ARGLIST) { - Eterm *tp; - ASSERT(is_tuple(value)); - tp = tuple_val(value); - value = tp[1]; - } - if ((reason & EXF_THROWN) && (p->catches <= 0)) { - value = TUPLE2(nocatch, am_nocatch, value); - reason = EXC_ERROR; - } - /* Note: expand_error_value() could theoretically - * allocate on the heap, but not for any error - * returned by a BIF, and it would do no harm, - * just be annoying. - */ - value = expand_error_value(p, reason, value); - class = exception_tag[GET_EXC_CLASS(reason)]; - - if (flags_meta & MATCH_SET_EXCEPTION_TRACE) { - erts_trace_exception(p, ep->code, class, value, - &meta_tracer_pid); - } - if (flags & MATCH_SET_EXCEPTION_TRACE) { - erts_trace_exception(p, ep->code, class, value, - &p->tracer_proc); - } - if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) { - /* can only happen if(local)*/ - Eterm *ptr = p->stop; - ASSERT(is_CP(*ptr)); - ASSERT(ptr <= STACK_START(p)); - /* Search the nearest stack frame for a catch */ - while (++ptr < STACK_START(p)) { - if (is_CP(*ptr)) break; - if (is_catch(*ptr)) { - if (applying) { - /* Apply of BIF, cp is in calling function */ - if (cp) erts_trace_return_to(p, cp); - } else { - /* Direct bif call, I points into - * calling function */ - erts_trace_return_to(p, I); - } - } - } - } - UnUseTmpHeapNoproc(3); - if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) { - erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); - p->trace_flags |= F_EXCEPTION_TRACE; - erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR); - } - } - } else { - if (flags_meta & MATCH_SET_RX_TRACE) { - erts_trace_return(p, ep->code, result, &meta_tracer_pid); - } - /* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */ - if (flags & MATCH_SET_RX_TRACE) { - erts_trace_return(p, ep->code, result, &p->tracer_proc); - } - if (flags & MATCH_SET_RETURN_TO_TRACE) { - /* can only happen if(local)*/ - if (applying) { - /* Apply of BIF, cp is in calling function */ - if (cp) erts_trace_return_to(p, cp); - } else { - /* Direct bif call, I points into calling function */ - erts_trace_return_to(p, I); - } - } - } - } - ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); - return result; -} - /* Sends trace message: * {trace_ts, Pid, What, Msg, Timestamp} * or {trace, Pid, What, Msg} diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index 6d5eae73b0..049226ab7d 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -77,66 +77,25 @@ void erts_init_unicode(void) { max_loop_limit = CONTEXT_REDS * LOOP_FACTOR; /* Non visual BIFs to trap to. */ - memset(&characters_to_utf8_trap_exp, 0, sizeof(Export)); - characters_to_utf8_trap_exp.address = - &characters_to_utf8_trap_exp.code[3]; - characters_to_utf8_trap_exp.code[0] = am_erlang; - characters_to_utf8_trap_exp.code[1] = - am_atom_put("characters_to_utf8_trap",23); - characters_to_utf8_trap_exp.code[2] = 3; - characters_to_utf8_trap_exp.code[3] = - (BeamInstr) em_apply_bif; - characters_to_utf8_trap_exp.code[4] = - (BeamInstr) &characters_to_utf8_trap; - - memset(&characters_to_list_trap_1_exp, 0, sizeof(Export)); - characters_to_list_trap_1_exp.address = - &characters_to_list_trap_1_exp.code[3]; - characters_to_list_trap_1_exp.code[0] = am_erlang; - characters_to_list_trap_1_exp.code[1] = - am_atom_put("characters_to_list_trap_1",25); - characters_to_list_trap_1_exp.code[2] = 3; - characters_to_list_trap_1_exp.code[3] = - (BeamInstr) em_apply_bif; - characters_to_list_trap_1_exp.code[4] = - (BeamInstr) &characters_to_list_trap_1; - - memset(&characters_to_list_trap_2_exp, 0, sizeof(Export)); - characters_to_list_trap_2_exp.address = - &characters_to_list_trap_2_exp.code[3]; - characters_to_list_trap_2_exp.code[0] = am_erlang; - characters_to_list_trap_2_exp.code[1] = - am_atom_put("characters_to_list_trap_2",25); - characters_to_list_trap_2_exp.code[2] = 3; - characters_to_list_trap_2_exp.code[3] = - (BeamInstr) em_apply_bif; - characters_to_list_trap_2_exp.code[4] = - (BeamInstr) &characters_to_list_trap_2; - - - memset(&characters_to_list_trap_3_exp, 0, sizeof(Export)); - characters_to_list_trap_3_exp.address = - &characters_to_list_trap_3_exp.code[3]; - characters_to_list_trap_3_exp.code[0] = am_erlang; - characters_to_list_trap_3_exp.code[1] = - am_atom_put("characters_to_list_trap_3",25); - characters_to_list_trap_3_exp.code[2] = 3; - characters_to_list_trap_3_exp.code[3] = - (BeamInstr) em_apply_bif; - characters_to_list_trap_3_exp.code[4] = - (BeamInstr) &characters_to_list_trap_3; - - memset(&characters_to_list_trap_4_exp, 0, sizeof(Export)); - characters_to_list_trap_4_exp.address = - &characters_to_list_trap_4_exp.code[3]; - characters_to_list_trap_4_exp.code[0] = am_erlang; - characters_to_list_trap_4_exp.code[1] = - am_atom_put("characters_to_list_trap_4",25); - characters_to_list_trap_4_exp.code[2] = 1; - characters_to_list_trap_4_exp.code[3] = - (BeamInstr) em_apply_bif; - characters_to_list_trap_4_exp.code[4] = - (BeamInstr) &characters_to_list_trap_4; + erts_init_trap_export(&characters_to_utf8_trap_exp, + am_erlang, am_atom_put("characters_to_utf8_trap",23), 3, + &characters_to_utf8_trap); + + erts_init_trap_export(&characters_to_list_trap_1_exp, + am_erlang, am_atom_put("characters_to_list_trap_1",25), 3, + &characters_to_list_trap_1); + + erts_init_trap_export(&characters_to_list_trap_2_exp, + am_erlang, am_atom_put("characters_to_list_trap_2",25), 3, + &characters_to_list_trap_2); + + erts_init_trap_export(&characters_to_list_trap_3_exp, + am_erlang, am_atom_put("characters_to_list_trap_3",25), 3, + &characters_to_list_trap_3); + + erts_init_trap_export(&characters_to_list_trap_4_exp, + am_erlang, am_atom_put("characters_to_list_trap_4",25), 1, + &characters_to_list_trap_4); c_to_b_int_trap_exportp = erts_export_put(am_unicode,am_characters_to_binary_int,2); c_to_l_int_trap_exportp = erts_export_put(am_unicode,am_characters_to_list_int,2); diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 5dc307e383..a0e12e57f2 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -29,19 +29,10 @@ /* #define FORCE_HEAP_FRAGS */ -#if defined(HYBRID) -/* # define CHECK_FOR_HOLES */ -#endif - #if defined(DEBUG) && !defined(CHECK_FOR_HOLES) && !defined(__WIN32__) # define CHECK_FOR_HOLES #endif -#if defined(HYBRID) -/* # define INCREMENTAL 1 */ /* Incremental garbage collection */ -/* # define INC_TIME_BASED 1 */ /* Time-based incremental GC (vs Work-based) */ -#endif - #define BEAM 1 #define EMULATOR "BEAM" #define SEQ_TRACE 1 @@ -70,16 +61,6 @@ #define H_DEFAULT_SIZE 233 /* default (heap + stack) min size */ #define VH_DEFAULT_SIZE 32768 /* default virtual (bin) heap min size (words) */ -#ifdef HYBRID -# define SH_DEFAULT_SIZE 2629425 /* default message area min size */ -#endif - -#ifdef INCREMENTAL -# define INC_NoPAGES 256 /* Number of pages in the old generation */ -# define INC_PAGESIZE 32768 /* The size of each page */ -# define INC_STORAGE_SIZE 1024 /* The size of gray stack and similar */ -#endif - #define CP_SIZE 1 #define ErtsHAllocLockCheck(P) \ diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d index c1024dafc4..ac9b0052e5 100644 --- a/erts/emulator/beam/erlang_dtrace.d +++ b/erts/emulator/beam/erlang_dtrace.d @@ -639,9 +639,9 @@ provider erlang { * Entry into the efile_drv.c file I/O driver * * For a list of command numbers used by this driver, see the section - * "Guide to probe arguments" in ../../../README.md. That section - * also contains explanation of the various integer and string - * arguments that may be present when any particular probe fires. + * "Guide to efile_drv.c probe arguments" in ../../../HOWTO/DTRACE.md. + * That section also contains explanation of the various integer and + * string arguments that may be present when any particular probe fires. * * NOTE: Not all Linux platforms (using SystemTap) can support * arguments beyond arg9. diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c index fb0ee99119..6b5121f917 100644 --- a/erts/emulator/beam/export.c +++ b/erts/emulator/beam/export.c @@ -32,98 +32,162 @@ #define EXPORT_HASH(m,f,a) ((m)*(f)+(a)) -static IndexTable export_table; /* Not locked. */ -static Hash secondary_export_table; /* Locked. */ +#ifdef DEBUG +# define IF_DEBUG(x) x +#else +# define IF_DEBUG(x) +#endif -#include "erl_smp.h" +static IndexTable export_tables[ERTS_NUM_CODE_IX]; /* Active not locked */ -static erts_smp_rwmtx_t export_table_lock; /* Locks the secondary export table. */ +static erts_smp_atomic_t total_entries_bytes; -#define export_read_lock() erts_smp_rwmtx_rlock(&export_table_lock) -#define export_read_unlock() erts_smp_rwmtx_runlock(&export_table_lock) -#define export_write_lock() erts_smp_rwmtx_rwlock(&export_table_lock) -#define export_write_unlock() erts_smp_rwmtx_rwunlock(&export_table_lock) +#include "erl_smp.h" + +/* This lock protects the staging export table from concurrent access + * AND it protects the staging table from becoming active. + */ +erts_smp_mtx_t export_staging_lock; extern BeamInstr* em_call_error_handler; extern BeamInstr* em_call_traced_function; +struct export_entry +{ + IndexSlot slot; /* MUST BE LOCATED AT TOP OF STRUCT!!! */ + Export* ep; +}; + +/* Helper struct that brings things together in one allocation +*/ +struct export_blob +{ + Export exp; + struct export_entry entryv[ERTS_NUM_CODE_IX]; + /* Note that entryv is not indexed by "code_ix". + */ +}; + +/* Helper struct only used as template +*/ +struct export_templ +{ + struct export_entry entry; + Export exp; +}; + +static struct export_blob* entry_to_blob(struct export_entry* ee) +{ + return (struct export_blob*) + ((char*)ee->ep - offsetof(struct export_blob,exp)); +} + void export_info(int to, void *to_arg) { #ifdef ERTS_SMP int lock = !ERTS_IS_CRASH_DUMPING; if (lock) - export_read_lock(); + export_staging_lock(); #endif - index_info(to, to_arg, &export_table); - hash_info(to, to_arg, &secondary_export_table); + index_info(to, to_arg, &export_tables[erts_active_code_ix()]); + hash_info(to, to_arg, &export_tables[erts_staging_code_ix()].htable); #ifdef ERTS_SMP if (lock) - export_read_unlock(); + export_staging_unlock(); #endif } static HashValue -export_hash(Export* x) +export_hash(struct export_entry* ee) { + Export* x = ee->ep; return EXPORT_HASH(x->code[0], x->code[1], x->code[2]); } static int -export_cmp(Export* tmpl, Export* obj) +export_cmp(struct export_entry* tmpl_e, struct export_entry* obj_e) { + Export* tmpl = tmpl_e->ep; + Export* obj = obj_e->ep; return !(tmpl->code[0] == obj->code[0] && tmpl->code[1] == obj->code[1] && tmpl->code[2] == obj->code[2]); } -static Export* -export_alloc(Export* tmpl) +static struct export_entry* +export_alloc(struct export_entry* tmpl_e) { - Export* obj = (Export*) erts_alloc(ERTS_ALC_T_EXPORT, sizeof(Export)); - - obj->fake_op_func_info_for_hipe[0] = 0; - obj->fake_op_func_info_for_hipe[1] = 0; - obj->code[0] = tmpl->code[0]; - obj->code[1] = tmpl->code[1]; - obj->code[2] = tmpl->code[2]; - obj->slot.index = -1; - obj->address = obj->code+3; - obj->code[3] = (BeamInstr) em_call_error_handler; - obj->code[4] = 0; - obj->match_prog_set = NULL; - return obj; + struct export_blob* blob; + unsigned ix; + + if (tmpl_e->slot.index == -1) { /* Template, allocate blob */ + Export* tmpl = tmpl_e->ep; + Export* obj; + + blob = (struct export_blob*) erts_alloc(ERTS_ALC_T_EXPORT, sizeof(*blob)); + erts_smp_atomic_add_nob(&total_entries_bytes, sizeof(*blob)); + obj = &blob->exp; + obj->fake_op_func_info_for_hipe[0] = 0; + obj->fake_op_func_info_for_hipe[1] = 0; + obj->code[0] = tmpl->code[0]; + obj->code[1] = tmpl->code[1]; + obj->code[2] = tmpl->code[2]; + obj->code[3] = (BeamInstr) em_call_error_handler; + obj->code[4] = 0; + + for (ix=0; ix<ERTS_NUM_CODE_IX; ix++) { + obj->addressv[ix] = obj->code+3; + + blob->entryv[ix].slot.index = -1; + blob->entryv[ix].ep = &blob->exp; + } + ix = 0; + } + else { /* Existing entry in another table, use free entry in blob */ + blob = entry_to_blob(tmpl_e); + for (ix = 0; blob->entryv[ix].slot.index >= 0; ix++) { + ASSERT(ix < ERTS_NUM_CODE_IX); + } + } + return &blob->entryv[ix]; } - -static void -export_free(Export* obj) +static void +export_free(struct export_entry* obj) { - erts_free(ERTS_ALC_T_EXPORT, (void*) obj); + struct export_blob* blob = entry_to_blob(obj); + int i; + obj->slot.index = -1; + for (i=0; i < ERTS_NUM_CODE_IX; i++) { + if (blob->entryv[i].slot.index >= 0) { + return; + } + } + erts_free(ERTS_ALC_T_EXPORT, blob); + erts_smp_atomic_add_nob(&total_entries_bytes, -sizeof(*blob)); } - void init_export_table(void) { HashFunctions f; - erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; - rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ; - rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED; + int i; - erts_smp_rwmtx_init_opt(&export_table_lock, &rwmtx_opt, "export_tab"); + erts_smp_mtx_init(&export_staging_lock, "export_tab"); + erts_smp_atomic_init_nob(&total_entries_bytes, 0); f.hash = (H_FUN) export_hash; f.cmp = (HCMP_FUN) export_cmp; f.alloc = (HALLOC_FUN) export_alloc; f.free = (HFREE_FUN) export_free; - erts_index_init(ERTS_ALC_T_EXPORT_TABLE, &export_table, "export_list", - EXPORT_INITIAL_SIZE, EXPORT_LIMIT, f); - hash_init(ERTS_ALC_T_EXPORT_TABLE, &secondary_export_table, - "secondary_export_table", 50, f); + for (i=0; i<ERTS_NUM_CODE_IX; i++) { + erts_index_init(ERTS_ALC_T_EXPORT_TABLE, &export_tables[i], "export_list", + EXPORT_INITIAL_SIZE, EXPORT_LIMIT, f); + } } /* @@ -138,29 +202,42 @@ init_export_table(void) * called through such an export entry. * 3) This function is suitable for the implementation of erlang:apply/3. */ +extern Export* /* inline-helper */ +erts_find_export_entry(Eterm m, Eterm f, unsigned int a,ErtsCodeIndex code_ix); Export* -erts_find_export_entry(Eterm m, Eterm f, unsigned int a) +erts_find_export_entry(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix) { HashValue hval = EXPORT_HASH((BeamInstr) m, (BeamInstr) f, (BeamInstr) a); int ix; HashBucket* b; - ix = hval % export_table.htable.size; - b = export_table.htable.bucket[ix]; + ix = hval % export_tables[code_ix].htable.size; + b = export_tables[code_ix].htable.bucket[ix]; /* * Note: We have inlined the code from hash.c for speed. */ while (b != (HashBucket*) 0) { - Export* ep = (Export *) b; + Export* ep = ((struct export_entry*) b)->ep; if (ep->code[0] == m && ep->code[1] == f && ep->code[2] == a) { - break; + return ep; } b = b->next; } - return (Export*)b; + return NULL; +} + +static struct export_entry* init_template(struct export_templ* templ, + Eterm m, Eterm f, unsigned a) +{ + templ->entry.ep = &templ->exp; + templ->entry.slot.index = -1; + templ->exp.code[0] = m; + templ->exp.code[1] = f; + templ->exp.code[2] = a; + return &templ->entry; } @@ -176,47 +253,42 @@ erts_find_export_entry(Eterm m, Eterm f, unsigned int a) */ Export* -erts_find_function(Eterm m, Eterm f, unsigned int a) +erts_find_function(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix) { - Export e; - Export* ep; - - e.code[0] = m; - e.code[1] = f; - e.code[2] = a; - - ep = hash_get(&export_table.htable, (void*) &e); - if (ep != NULL && ep->address == ep->code+3 && - ep->code[3] != (BeamInstr) em_call_traced_function) { - ep = NULL; + struct export_templ templ; + struct export_entry* ee; + + ee = hash_get(&export_tables[code_ix].htable, init_template(&templ, m, f, a)); + if (ee == NULL || + (ee->ep->addressv[code_ix] == ee->ep->code+3 && + ee->ep->code[3] != (BeamInstr) BeamOp(op_i_generic_breakpoint))) { + return NULL; } - return ep; + return ee->ep; } /* * Returns a pointer to an existing export entry for a MFA, * or creates a new one and returns the pointer. * - * This function provides unlocked write access to the main export - * table. It should only be used during start up or when - * all other threads are blocked. + * This function acts on the staging export table. It should only be used + * to load new code. */ Export* erts_export_put(Eterm mod, Eterm func, unsigned int arity) { - Export e; - int ix; + ErtsCodeIndex code_ix = erts_staging_code_ix(); + struct export_templ templ; + struct export_entry* ee; - ERTS_SMP_LC_ASSERT(erts_initialized == 0 - || erts_smp_thr_progress_is_blocking()); ASSERT(is_atom(mod)); ASSERT(is_atom(func)); - e.code[0] = mod; - e.code[1] = func; - e.code[2] = arity; - ix = index_put(&export_table, (void*) &e); - return (Export*) erts_index_lookup(&export_table, ix); + export_staging_lock(); + ee = (struct export_entry*) index_put_entry(&export_tables[code_ix], + init_template(&templ, mod, func, arity)); + export_staging_unlock(); + return ee->ep; } /* @@ -224,77 +296,117 @@ erts_export_put(Eterm mod, Eterm func, unsigned int arity) * export entry (making a call through it will cause the error_handler to * be called). * - * Stub export entries will be placed in the secondary export table. - * erts_export_consolidate() will move all stub export entries into the - * main export table (will be done the next time code is loaded). + * Stub export entries will be placed in the loader export table. */ Export* erts_export_get_or_make_stub(Eterm mod, Eterm func, unsigned int arity) { - Export e; + ErtsCodeIndex code_ix; Export* ep; + IF_DEBUG(int retrying = 0;) ASSERT(is_atom(mod)); ASSERT(is_atom(func)); - - e.code[0] = mod; - e.code[1] = func; - e.code[2] = arity; - ep = erts_find_export_entry(mod, func, arity); - if (ep == 0) { - /* - * The code is not loaded (yet). Put the export in the secondary - * export table, to avoid having to lock the main export table. - */ - export_write_lock(); - ep = (Export *) hash_put(&secondary_export_table, (void*) &e); - export_write_unlock(); - } + + do { + code_ix = erts_active_code_ix(); + ep = erts_find_export_entry(mod, func, arity, code_ix); + if (ep == 0) { + /* + * The code is not loaded (yet). Put the export in the staging + * export table, to avoid having to lock the active export table. + */ + export_staging_lock(); + if (erts_active_code_ix() == code_ix) { + struct export_templ templ; + struct export_entry* entry; + + IndexTable* tab = &export_tables[erts_staging_code_ix()]; + init_template(&templ, mod, func, arity); + entry = (struct export_entry *) index_put_entry(tab, &templ.entry); + ep = entry->ep; + ASSERT(ep); + } + else { /* race */ + ASSERT(!retrying); + IF_DEBUG(retrying = 1); + } + export_staging_unlock(); + } + } while (!ep); return ep; } -/* - * To be called before loading code (with other threads blocked). - * This function will move all export entries from the secondary - * export table into the primary. - */ -void -erts_export_consolidate(void) +Export *export_list(int i, ErtsCodeIndex code_ix) { -#ifdef DEBUG - HashInfo hi; -#endif - - ERTS_SMP_LC_ASSERT(erts_initialized == 0 - || erts_smp_thr_progress_is_blocking()); - - export_write_lock(); - erts_index_merge(&secondary_export_table, &export_table); - erts_hash_merge(&secondary_export_table, &export_table.htable); - export_write_unlock(); -#ifdef DEBUG - hash_get_info(&hi, &export_table.htable); - ASSERT(export_table.entries == hi.objs); -#endif + return ((struct export_entry*) erts_index_lookup(&export_tables[code_ix], i))->ep; } -Export *export_list(int i) +int export_list_size(ErtsCodeIndex code_ix) { - return (Export*) erts_index_lookup(&export_table, i); + return export_tables[code_ix].entries; } -int export_list_size(void) +int export_table_sz(void) +{ + int i, bytes = 0; + + export_staging_lock(); + for (i=0; i<ERTS_NUM_CODE_IX; i++) { + bytes += index_table_sz(&export_tables[i]); + } + export_staging_unlock(); + return bytes; +} +int export_entries_sz(void) { - return export_table.entries; + return erts_smp_atomic_read_nob(&total_entries_bytes); } +Export *export_get(Export *e) +{ + struct export_entry ee; + struct export_entry* entry; -int export_table_sz(void) + ee.ep = e; + entry = (struct export_entry*)hash_get(&export_tables[erts_active_code_ix()].htable, &ee); + return entry ? entry->ep : NULL; +} + +IF_DEBUG(static ErtsCodeIndex debug_start_load_ix = 0;) + + +void export_start_staging(void) { - return index_table_sz(&export_table); + ErtsCodeIndex dst_ix = erts_staging_code_ix(); + ErtsCodeIndex src_ix = erts_active_code_ix(); + IndexTable* dst = &export_tables[dst_ix]; + IndexTable* src = &export_tables[src_ix]; + struct export_entry* src_entry; + struct export_entry* dst_entry; + int i; + + ASSERT(dst_ix != src_ix); + ASSERT(debug_start_load_ix == -1); + + export_staging_lock(); + /* + * Insert all entries in src into dst table + */ + for (i = 0; i < src->entries; i++) { + src_entry = (struct export_entry*) erts_index_lookup(src, i); + src_entry->ep->addressv[dst_ix] = src_entry->ep->addressv[src_ix]; + dst_entry = (struct export_entry*) index_put_entry(dst, src_entry); + ASSERT(entry_to_blob(src_entry) == entry_to_blob(dst_entry)); + } + export_staging_unlock(); + + IF_DEBUG(debug_start_load_ix = dst_ix); } -Export *export_get(Export *e) +void export_end_staging(int commit) { - return hash_get(&export_table.htable, e); + ASSERT(debug_start_load_ix == erts_staging_code_ix()); + IF_DEBUG(debug_start_load_ix = -1); } + diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h index c604fdf7c3..ee06e69aff 100644 --- a/erts/emulator/beam/export.h +++ b/erts/emulator/beam/export.h @@ -28,14 +28,15 @@ #include "index.h" #endif +#include "code_ix.h" + /* ** Export entry */ + typedef struct export { - IndexSlot slot; /* MUST BE LOCATED AT TOP OF STRUCT!!! */ - void* address; /* Pointer to code for function. */ - struct binary* match_prog_set; /* Match program for tracing. */ + void* addressv[ERTS_NUM_CODE_IX]; /* Pointer to code for function. */ BeamInstr fake_op_func_info_for_hipe[2]; /* MUST be just before code[] */ /* @@ -44,12 +45,12 @@ typedef struct export * code[2]: Arity (untagged integer). * code[3]: This entry is 0 unless the 'address' field points to it. * Threaded code instruction to load function - * (em_call_error_handler), execute BIF (em_apply_bif, - * em_apply_apply), or call a traced function - * (em_call_traced_function). - * code[4]: Function pointer to BIF function (for BIFs only) + * (em_call_error_handler), execute BIF (em_apply_bif), + * or a breakpoint instruction (op_i_generic_breakpoint). + * code[4]: Function pointer to BIF function (for BIFs only), * or pointer to threaded code if the module has an - * on_load function that has not been run yet. + * on_load function that has not been run yet, or pointer + * to code for function code[3] is a breakpont instruction. * Otherwise: 0. */ BeamInstr code[5]; @@ -59,21 +60,38 @@ typedef struct export void init_export_table(void); void export_info(int, void *); -Export* erts_find_export_entry(Eterm m, Eterm f, unsigned int a); +ERTS_GLB_INLINE Export* erts_active_export_entry(Eterm m, Eterm f, unsigned a); Export* erts_export_put(Eterm mod, Eterm func, unsigned int arity); - Export* erts_export_get_or_make_stub(Eterm, Eterm, unsigned); -void erts_export_consolidate(void); -Export *export_list(int); -int export_list_size(void); +Export *export_list(int,ErtsCodeIndex); +int export_list_size(ErtsCodeIndex); int export_table_sz(void); +int export_entries_sz(void); Export *export_get(Export*); +void export_start_staging(void); +void export_end_staging(int commit); + +extern erts_smp_mtx_t export_staging_lock; +#define export_staging_lock() erts_smp_mtx_lock(&export_staging_lock) +#define export_staging_unlock() erts_smp_mtx_unlock(&export_staging_lock) #include "beam_load.h" /* For em_* extern declarations */ #define ExportIsBuiltIn(EntryPtr) \ -(((EntryPtr)->address == (EntryPtr)->code + 3) && \ +(((EntryPtr)->addressv[erts_active_code_ix()] == (EntryPtr)->code + 3) && \ ((EntryPtr)->code[3] == (BeamInstr) em_apply_bif)) -#endif +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE Export* +erts_active_export_entry(Eterm m, Eterm f, unsigned int a) +{ + extern Export* erts_find_export_entry(Eterm m, Eterm f, unsigned a, ErtsCodeIndex); + return erts_find_export_entry(m, f, a, erts_active_code_ix()); +} + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +#endif /* __EXPORT_H__ */ + diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 44abc83d6d..feb1740649 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2562,7 +2562,7 @@ dec_term_atom_common: goto error; } if (edep && (edep->flags & ERTS_DIST_EXT_BTT_SAFE)) { - if (!erts_find_export_entry(mod, name, arity)) + if (!erts_active_export_entry(mod, name, arity)) goto error; } *objp = make_export(hp); @@ -2626,14 +2626,12 @@ dec_term_atom_common: } old_uniq = unsigned_val(temp); -#ifndef HYBRID /* FIND ME! */ /* * It is safe to link the fun into the fun list only when * no more validity tests can fail. */ funp->next = off_heap->first; off_heap->first = (struct erl_off_heap_header*)funp; -#endif funp->fe = erts_put_fun_entry2(module, old_uniq, old_index, uniq, index, arity); @@ -2704,14 +2702,12 @@ dec_term_atom_common: goto error; } -#ifndef HYBRID /* FIND ME! */ /* * It is safe to link the fun into the fun list only when * no more validity tests can fail. */ funp->next = off_heap->first; off_heap->first = (struct erl_off_heap_header*)funp; -#endif old_uniq = unsigned_val(temp); funp->fe = erts_put_fun_entry(module, old_uniq, old_index); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index e9dbba3d4a..c9be20322d 100644..100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -28,6 +28,7 @@ #include "hash.h" #include "index.h" #include "atom.h" +#include "code_ix.h" #include "export.h" #include "module.h" #include "register.h" @@ -173,6 +174,7 @@ struct port { char *name; /* String used in the open */ erts_driver_t* drv_ptr; UWord drv_data; + SWord os_pid; /* Child process ID */ ErtsProcList *suspended; /* List of suspended processes. */ LineBuf *linebuf; /* Buffer to hold data not ready for process to get (line oriented I/O)*/ @@ -568,92 +570,6 @@ extern erts_smp_atomic32_t erts_max_gen_gcs; extern int erts_disable_tolerant_timeofday; -#ifdef HYBRID - -/* Message Area heap pointers */ -extern Eterm *global_heap; /* Heap start */ -extern Eterm *global_hend; /* Heap end */ -extern Eterm *global_htop; /* Heap top (heap pointer) */ -extern Eterm *global_saved_htop; /* Saved heap top (heap pointer) */ -extern Uint global_heap_sz; /* Heap size, in words */ -extern Eterm *global_old_heap; /* Old generation */ -extern Eterm *global_old_hend; -extern ErlOffHeap erts_global_offheap; /* Global MSO (OffHeap) list */ - -extern Uint16 global_gen_gcs; -extern Uint16 global_max_gen_gcs; -extern Uint global_gc_flags; - -#ifdef INCREMENTAL -#define ACTIVATE(p) -#define DEACTIVATE(p) -#define IS_ACTIVE(p) 1 - -#define INC_ACTIVATE(p) do { \ - if ((p)->active) { \ - if ((p)->active_next != NULL) { \ - (p)->active_next->active_prev = (p)->active_prev; \ - if ((p)->active_prev) { \ - (p)->active_prev->active_next = (p)->active_next; \ - } else { \ - inc_active_proc = (p)->active_next; \ - } \ - inc_active_last->active_next = (p); \ - (p)->active_next = NULL; \ - (p)->active_prev = inc_active_last; \ - inc_active_last = (p); \ - } \ - } else { \ - (p)->active_next = NULL; \ - (p)->active_prev = inc_active_last; \ - if (inc_active_last) { \ - inc_active_last->active_next = (p); \ - } else { \ - inc_active_proc = (p); \ - } \ - inc_active_last = (p); \ - (p)->active = 1; \ - } \ -} while(0); - -#define INC_DEACTIVATE(p) do { \ - ASSERT((p)->active == 1); \ - if ((p)->active_next == NULL) { \ - inc_active_last = (p)->active_prev; \ - } else { \ - (p)->active_next->active_prev = (p)->active_prev; \ - } \ - if ((p)->active_prev == NULL) { \ - inc_active_proc = (p)->active_next; \ - } else { \ - (p)->active_prev->active_next = (p)->active_next; \ - } \ - (p)->active = 0; \ -} while(0); - -#define INC_IS_ACTIVE(p) ((p)->active != 0) - -#else -extern Eterm *global_old_htop; -extern Eterm *global_high_water; -#define ACTIVATE(p) (p)->active = 1; -#define DEACTIVATE(p) (p)->active = 0; -#define IS_ACTIVE(p) ((p)->active != 0) -#define INC_ACTIVATE(p) -#define INC_IS_ACTIVE(p) 1 -#endif /* INCREMENTAL */ - -#else -# define ACTIVATE(p) -# define DEACTIVATE(p) -# define IS_ACTIVE(p) 1 -# define INC_ACTIVATE(p) -#endif /* HYBRID */ - -#ifdef HYBRID -extern Uint global_heap_min_sz; -#endif - extern int bif_reductions; /* reductions + fcalls (when doing call_bif) */ extern int stackdump_on_exit; @@ -853,6 +769,8 @@ void erts_queue_monitor_message(Process *, Eterm, Eterm, Eterm); +void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a, + Eterm (*bif)(Process*,Eterm*)); void erts_init_bif(void); Eterm erl_send(Process *p, Eterm to, Eterm msg); @@ -875,24 +793,33 @@ typedef struct { Eterm* fname_ptr; /* Pointer to fname table */ } FunctionInfo; -struct LoaderState* erts_alloc_loader_state(void); -Eterm erts_prepare_loading(struct LoaderState*, Process *c_p, +Binary* erts_alloc_loader_state(void); +Eterm erts_module_for_prepared_code(Binary* magic); +Eterm erts_prepare_loading(Binary* loader_state, Process *c_p, Eterm group_leader, Eterm* modp, byte* code, Uint size); -Eterm erts_finish_loading(struct LoaderState* stp, Process* c_p, +Eterm erts_finish_loading(Binary* loader_state, Process* c_p, ErtsProcLocks c_p_locks, Eterm* modp); -Eterm erts_load_module(Process *c_p, ErtsProcLocks c_p_locks, - Eterm group_leader, Eterm* mod, byte* code, Uint size); +Eterm erts_preload_module(Process *c_p, ErtsProcLocks c_p_locks, + Eterm group_leader, Eterm* mod, byte* code, Uint size); void init_load(void); BeamInstr* find_function_from_pc(BeamInstr* pc); Eterm* erts_build_mfa_item(FunctionInfo* fi, Eterm* hp, Eterm args, Eterm* mfa_p); -void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info); void erts_set_current_function(FunctionInfo* fi, BeamInstr* current); Eterm erts_module_info_0(Process* p, Eterm module); Eterm erts_module_info_1(Process* p, Eterm module, Eterm what); Eterm erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info); +/* beam_ranges.c */ +void erts_init_ranges(void); +void erts_start_staging_ranges(void); +void erts_end_staging_ranges(int commit); +void erts_update_ranges(BeamInstr* code, Uint size); +void erts_remove_from_ranges(BeamInstr* code); +UWord erts_ranges_sz(void); +void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info); + /* break.c */ void init_break_handler(void); void erts_set_ignore_break(void); @@ -909,7 +836,6 @@ __decl_noreturn void __noreturn erl_exit_flush_async(int n, char*, ...); void erl_error(char*, va_list); /* copy.c */ -void init_copy(void); Eterm copy_object(Eterm, Process*); #if HALFWORD_HEAP @@ -939,116 +865,6 @@ Eterm copy_shallow(Eterm*, Uint, Eterm**, ErlOffHeap*); void move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first, Eterm* refs, unsigned nrefs); -#ifdef HYBRID -#define RRMA_DEFAULT_SIZE 256 -#define RRMA_STORE(p,ptr,src) do { \ - ASSERT((p)->rrma != NULL); \ - ASSERT((p)->rrsrc != NULL); \ - (p)->rrma[(p)->nrr] = (ptr); \ - (p)->rrsrc[(p)->nrr++] = (src); \ - if ((p)->nrr == (p)->rrsz) \ - { \ - (p)->rrsz *= 2; \ - (p)->rrma = (Eterm *) erts_realloc(ERTS_ALC_T_ROOTSET, \ - (void*)(p)->rrma, \ - sizeof(Eterm) * (p)->rrsz); \ - (p)->rrsrc = (Eterm **) erts_realloc(ERTS_ALC_T_ROOTSET, \ - (void*)(p)->rrsrc, \ - sizeof(Eterm) * (p)->rrsz); \ - } \ -} while(0) - -/* Note that RRMA_REMOVE decreases the given index after deletion. - * This is done so that a loop with an increasing index can call - * remove without having to decrease the index to see the element - * placed in the hole after the deleted element. - */ -#define RRMA_REMOVE(p,index) do { \ - p->rrsrc[index] = p->rrsrc[--p->nrr]; \ - p->rrma[index--] = p->rrma[p->nrr]; \ - } while(0); - - -/* The MessageArea STACKs are used while copying messages to the - * message area. - */ -#define MA_STACK_EXTERNAL_DECLARE(type,_s_) \ - typedef type ma_##_s_##_type; \ - extern ma_##_s_##_type *ma_##_s_##_stack; \ - extern Uint ma_##_s_##_top; \ - extern Uint ma_##_s_##_size; - -#define MA_STACK_DECLARE(_s_) \ - ma_##_s_##_type *ma_##_s_##_stack; Uint ma_##_s_##_top; Uint ma_##_s_##_size; - -#define MA_STACK_ALLOC(_s_) do { \ - ma_##_s_##_top = 0; \ - ma_##_s_##_size = 512; \ - ma_##_s_##_stack = (ma_##_s_##_type*)erts_alloc(ERTS_ALC_T_OBJECT_STACK, \ - sizeof(ma_##_s_##_type) * ma_##_s_##_size); \ -} while(0) - - -#define MA_STACK_PUSH(_s_,val) do { \ - ma_##_s_##_stack[ma_##_s_##_top++] = (val); \ - if (ma_##_s_##_top == ma_##_s_##_size) \ - { \ - ma_##_s_##_size *= 2; \ - ma_##_s_##_stack = \ - (ma_##_s_##_type*) erts_realloc(ERTS_ALC_T_OBJECT_STACK, \ - (void*)ma_##_s_##_stack, \ - sizeof(ma_##_s_##_type) * ma_##_s_##_size); \ - } \ -} while(0) - -#define MA_STACK_POP(_s_) (ma_##_s_##_top != 0 ? ma_##_s_##_stack[--ma_##_s_##_top] : 0) -#define MA_STACK_TOP(_s_) (ma_##_s_##_stack[ma_##_s_##_top - 1]) -#define MA_STACK_UPDATE(_s_,offset,value) \ - *(ma_##_s_##_stack[ma_##_s_##_top - 1] + (offset)) = (value) -#define MA_STACK_SIZE(_s_) (ma_##_s_##_top) -#define MA_STACK_ELM(_s_,i) ma_##_s_##_stack[i] - -MA_STACK_EXTERNAL_DECLARE(Eterm,src); -MA_STACK_EXTERNAL_DECLARE(Eterm*,dst); -MA_STACK_EXTERNAL_DECLARE(Uint,offset); - - -#ifdef INCREMENTAL -extern Eterm *ma_pending_stack; -extern Uint ma_pending_top; -extern Uint ma_pending_size; - -#define NO_COPY(obj) (IS_CONST(obj) || \ - (((ptr_val(obj) >= global_heap) && \ - (ptr_val(obj) < global_htop)) || \ - ((ptr_val(obj) >= inc_fromspc) && \ - (ptr_val(obj) < inc_fromend)) || \ - ((ptr_val(obj) >= global_old_heap) && \ - (ptr_val(obj) < global_old_hend)))) - -#else - -#define NO_COPY(obj) (IS_CONST(obj) || \ - (((ptr_val(obj) >= global_heap) && \ - (ptr_val(obj) < global_htop)) || \ - ((ptr_val(obj) >= global_old_heap) && \ - (ptr_val(obj) < global_old_hend)))) - -#endif /* INCREMENTAL */ - -#define LAZY_COPY(from,obj) do { \ - if (!NO_COPY(obj)) { \ - BM_LAZY_COPY_START; \ - BM_COUNT(messages_copied); \ - obj = copy_struct_lazy(from,obj,0); \ - BM_LAZY_COPY_STOP; \ - } \ -} while(0) - -Eterm copy_struct_lazy(Process*, Eterm, Uint); - -#endif /* HYBRID */ - /* Utilities */ extern void erts_delete_nodes_monitors(Process *, ErtsProcLocks); extern Eterm erts_monitor_nodes(Process *, Eterm, Eterm); @@ -1142,10 +958,6 @@ void erts_offset_heap_ptr(Eterm*, Uint, Sint, Eterm*, Eterm*); void erts_offset_heap(Eterm*, Uint, Sint, Eterm*, Eterm*); void erts_free_heap_frags(Process* p); -#ifdef HYBRID -int erts_global_garbage_collect(Process*, int, Eterm*, int); -#endif - /* io.c */ struct erl_drv_port_data_lock { @@ -1191,6 +1003,10 @@ void erts_fire_port_monitor(Port *prt, Eterm ref); void erts_smp_xports_unlock(Port *); #endif +#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_COUNT) +void erts_lcnt_enable_io_lock_count(int enable); +#endif + #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) int erts_lc_is_port_locked(Port *); #endif @@ -1598,7 +1414,7 @@ void erts_cleanup_offheap(ErlOffHeap *offheap); int erts_fit_in_bits_int64(Sint64); int erts_fit_in_bits_int32(Sint32); int list_length(Eterm); -Export* erts_find_function(Eterm, Eterm, unsigned int); +Export* erts_find_function(Eterm, Eterm, unsigned int, ErtsCodeIndex); int erts_is_builtin(Eterm, Eterm, int); Uint32 make_broken_hash(Eterm); Uint32 block_hash(byte *, unsigned, Uint32); @@ -1866,17 +1682,19 @@ struct trace_pattern_flags { }; extern const struct trace_pattern_flags erts_trace_pattern_flags_off; extern int erts_call_time_breakpoint_tracing; -int erts_set_trace_pattern(Eterm* mfa, int specified, +int erts_set_trace_pattern(Process*p, Eterm* mfa, int specified, Binary* match_prog_set, Binary *meta_match_prog_set, int on, struct trace_pattern_flags, - Eterm meta_tracer_pid); + Eterm meta_tracer_pid, int is_blocking); void erts_get_default_trace_pattern(int *trace_pattern_is_on, Binary **match_spec, Binary **meta_match_spec, struct trace_pattern_flags *trace_pattern_flags, Eterm *meta_tracer_pid); +int erts_is_default_trace_enabled(void); void erts_bif_trace_init(void); +int erts_finish_breakpointing(void); /* ** Call_trace uses this API for the parameter matching functions @@ -1922,14 +1740,6 @@ extern void erts_match_prog_foreach_offheap(Binary *b, breakpoint functions */ #define MATCH_SET_EXCEPTION_TRACE (0x4) /* exception trace requested */ #define MATCH_SET_RX_TRACE (MATCH_SET_RETURN_TRACE|MATCH_SET_EXCEPTION_TRACE) -/* - * Flag values when tracing bif - * Future note: flag field is 8 bits - */ -#define BIF_TRACE_AS_LOCAL (0x1) -#define BIF_TRACE_AS_GLOBAL (0x2) -#define BIF_TRACE_AS_META (0x4) -#define BIF_TRACE_AS_CALL_TIME (0x8) extern erts_driver_t vanilla_driver; extern erts_driver_t spawn_driver; diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c index a4a3007f93..25d5cce0f3 100644 --- a/erts/emulator/beam/index.c +++ b/erts/emulator/beam/index.c @@ -68,14 +68,14 @@ erts_index_init(ErtsAlcType_t type, IndexTable* t, char* name, return t; } -int -index_put(IndexTable* t, void* tmpl) +IndexSlot* +index_put_entry(IndexTable* t, void* tmpl) { int ix; IndexSlot* p = (IndexSlot*) hash_put(&t->htable, tmpl); if (p->index >= 0) { - return p->index; + return p; } ix = t->entries; @@ -92,7 +92,7 @@ index_put(IndexTable* t, void* tmpl) t->entries++; p->index = ix; t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p; - return ix; + return p; } int index_get(IndexTable* t, void* tmpl) @@ -135,3 +135,18 @@ void erts_index_merge(Hash* src, IndexTable* dst) } } } + +void index_erase_latest_from(IndexTable* t, Uint from_ix) +{ + if(from_ix < (Uint)t->entries) { + int ix; + for (ix = from_ix; ix < t->entries; ix++) { + IndexSlot* obj = t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK]; + hash_erase(&t->htable, obj); + } + t->entries = from_ix; + } + else { + ASSERT(from_ix == t->entries); + } +} diff --git a/erts/emulator/beam/index.h b/erts/emulator/beam/index.h index 4eb9b1f992..3afe48d007 100644 --- a/erts/emulator/beam/index.h +++ b/erts/emulator/beam/index.h @@ -55,12 +55,24 @@ void index_info(int, void *, IndexTable*); int index_table_sz(IndexTable *); int index_get(IndexTable*, void*); -int index_put(IndexTable*, void*); + +IndexSlot* index_put_entry(IndexTable*, void*); void erts_index_merge(Hash*, IndexTable*); +/* Erase all entries with index 'ix' and higher +*/ +void index_erase_latest_from(IndexTable*, Uint ix); + +ERTS_GLB_INLINE int index_put(IndexTable*, void*); ERTS_GLB_INLINE IndexSlot* erts_index_lookup(IndexTable*, Uint); #if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE int index_put(IndexTable* t, void* tmpl) +{ + return index_put_entry(t, tmpl)->index; +} + ERTS_GLB_INLINE IndexSlot* erts_index_lookup(IndexTable* t, Uint ix) { diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 4dd60b4d23..845593d0b2 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -438,6 +438,7 @@ setup_port(Port* prt, Eterm pid, erts_driver_t *driver, sys_strcpy(new_name, name); erts_smp_runq_lock(runq); erts_smp_port_state_lock(prt); + prt->os_pid = -1; prt->status = ERTS_PORT_SFLG_CONNECTED | xstatus; prt->snapshot = erts_smp_atomic32_read_nob(&erts_ports_snapshot); old_name = prt->name; @@ -621,7 +622,11 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ port->lock = erts_alloc(ERTS_ALC_T_PORT_LOCK, sizeof(erts_smp_mtx_t)); erts_smp_mtx_init_x(port->lock, +#ifdef ERTS_ENABLE_LOCK_COUNT + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_lock" : NULL, +#else "port_lock", +#endif port->id); xstatus |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK; } @@ -779,7 +784,13 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */ creator_port->xports = xplp; port->lock = erts_alloc(ERTS_ALC_T_PORT_LOCK, sizeof(erts_smp_mtx_t)); - erts_smp_mtx_init_locked_x(port->lock, "port_lock", port_id); + erts_smp_mtx_init_locked_x(port->lock, +#ifdef ERTS_ENABLE_LOCK_COUNT + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_lock" : NULL, +#else + "port_lock", +#endif + port_id); xstatus |= ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK; } @@ -1343,7 +1354,13 @@ void init_io(void) #ifdef ERTS_SMP erts_port[i].lock = NULL; erts_port[i].xports = NULL; - erts_smp_spinlock_init_x(&erts_port[i].state_lck, "port_state", make_small(i)); + erts_smp_spinlock_init_x(&erts_port[i].state_lck, +#ifdef ERTS_ENABLE_LOCK_COUNT + (erts_lcnt_rt_options & ERTS_LCNT_OPT_PORTLOCK) ? "port_state" : NULL, +#else + "port_state", +#endif + make_small(0)); #endif erts_port[i].tracer_proc = NIL; erts_port[i].trace_flags = 0; @@ -1376,6 +1393,27 @@ void init_io(void) erts_smp_mtx_unlock(&erts_driver_list_lock); } +#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP) +void erts_lcnt_enable_io_lock_count(int enable) { + int i; + + for (i = 0; i < erts_max_ports; i++) { + Port* p = &erts_port[i]; + if (enable) { + erts_lcnt_init_lock_x(&p->state_lck.lcnt, "port_state", ERTS_LCNT_LT_SPINLOCK, make_small(i)); + if (p->lock) { + erts_lcnt_init_lock_x(&p->lock->lcnt, "port_lock", ERTS_LCNT_LT_MUTEX, make_small(i)); + } + } else { + erts_lcnt_destroy_lock(&p->state_lck.lcnt); + if (p->lock) { + erts_lcnt_destroy_lock(&p->lock->lcnt); + } + } + } +} +#endif + /* * Buffering of data when using line oriented I/O on ports */ @@ -3244,6 +3282,8 @@ driver_deliver_term(ErlDrvPort port, Uint size = ptr[1]; Uint offset = ptr[2]; + erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) size); + if (size <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hbp = (ErlHeapBin *) hp; hp += heap_bin_size(size); @@ -3275,6 +3315,9 @@ driver_deliver_term(ErlDrvPort port, case ERL_DRV_BUF2BINARY: { /* char*, size */ byte *bufp = (byte *) ptr[0]; Uint size = (Uint) ptr[1]; + + erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) size); + if (size <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hbp = (ErlHeapBin *) hp; hp += heap_bin_size(size); @@ -3311,6 +3354,7 @@ driver_deliver_term(ErlDrvPort port, } case ERL_DRV_STRING: /* char*, length */ + erts_smp_atomic_add_nob(&erts_bytes_in, (erts_aint_t) ptr[1]); mess = buf_to_intlist(&hp, (char*)ptr[0], ptr[1], NIL); ptr += 2; break; diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c index b93b1ad09a..1ef71cda79 100644 --- a/erts/emulator/beam/module.c +++ b/erts/emulator/beam/module.c @@ -26,21 +26,32 @@ #include "global.h" #include "module.h" +#ifdef DEBUG +# define IF_DEBUG(x) x +#else +# define IF_DEBUG(x) +#endif + #define MODULE_SIZE 50 #define MODULE_LIMIT (64*1024) -static IndexTable module_table; +static IndexTable module_tables[ERTS_NUM_CODE_IX]; -/* - * SMP note: We don't need to look accesses to the module table because - * there is one only scheduler thread when we update it. +erts_smp_rwmtx_t the_old_code_rwlocks[ERTS_NUM_CODE_IX]; + +static erts_smp_atomic_t tot_module_bytes; + +/* SMP note: Active module table lookup and current module instance can be + * read without any locks. Old module instances are protected by + * "the_old_code_rwlocks" as purging is done on active module table. + * Staging table is protected by the "code_ix lock". */ #include "erl_smp.h" void module_info(int to, void *to_arg) { - index_info(to, to_arg, &module_table); + index_info(to, to_arg, &module_tables[erts_active_code_ix()]); } @@ -59,45 +70,67 @@ static int module_cmp(Module* tmpl, Module* obj) static Module* module_alloc(Module* tmpl) { Module* obj = (Module*) erts_alloc(ERTS_ALC_T_MODULE, sizeof(Module)); + erts_smp_atomic_add_nob(&tot_module_bytes, sizeof(Module)); obj->module = tmpl->module; - obj->code = 0; - obj->old_code = 0; - obj->code_length = 0; - obj->old_code_length = 0; + obj->curr.code = 0; + obj->old.code = 0; + obj->curr.code_length = 0; + obj->old.code_length = 0; obj->slot.index = -1; - obj->nif = NULL; - obj->old_nif = NULL; + obj->curr.nif = NULL; + obj->old.nif = NULL; + obj->curr.num_breakpoints = 0; + obj->old.num_breakpoints = 0; + obj->curr.num_traced_exports = 0; + obj->old.num_traced_exports = 0; return obj; } +static void module_free(Module* mod) +{ + erts_free(ERTS_ALC_T_MODULE, mod); + erts_smp_atomic_add_nob(&tot_module_bytes, -sizeof(Module)); +} void init_module_table(void) { HashFunctions f; + int i; f.hash = (H_FUN) module_hash; f.cmp = (HCMP_FUN) module_cmp; f.alloc = (HALLOC_FUN) module_alloc; - f.free = 0; + f.free = (HFREE_FUN) module_free; + + for (i = 0; i < ERTS_NUM_CODE_IX; i++) { + erts_index_init(ERTS_ALC_T_MODULE_TABLE, &module_tables[i], "module_code", + MODULE_SIZE, MODULE_LIMIT, f); + } - erts_index_init(ERTS_ALC_T_MODULE_TABLE, &module_table, "module_code", - MODULE_SIZE, MODULE_LIMIT, f); + for (i=0; i<ERTS_NUM_CODE_IX; i++) { + erts_smp_rwmtx_init_x(&the_old_code_rwlocks[i], "old_code", make_small(i)); + } + erts_smp_atomic_init_nob(&tot_module_bytes, 0); } Module* -erts_get_module(Eterm mod) +erts_get_module(Eterm mod, ErtsCodeIndex code_ix) { Module e; int index; + IndexTable* mod_tab; ASSERT(is_atom(mod)); + + mod_tab = &module_tables[code_ix]; + e.module = atom_val(mod); - index = index_get(&module_table, (void*) &e); + index = index_get(mod_tab, (void*) &e); if (index == -1) { return NULL; } else { - return (Module*) erts_index_lookup(&module_table, index); + return (Module*) erts_index_lookup(mod_tab, index); } } @@ -105,27 +138,101 @@ Module* erts_put_module(Eterm mod) { Module e; - int index; + IndexTable* mod_tab; + int oldsz, newsz; + Module* res; ASSERT(is_atom(mod)); ERTS_SMP_LC_ASSERT(erts_initialized == 0 - || erts_smp_thr_progress_is_blocking()); + || erts_is_code_ix_locked()); + + mod_tab = &module_tables[erts_staging_code_ix()]; e.module = atom_val(mod); - index = index_put(&module_table, (void*) &e); - return (Module*) erts_index_lookup(&module_table, index); + oldsz = index_table_sz(mod_tab); + res = (Module*) index_put_entry(mod_tab, (void*) &e); + newsz = index_table_sz(mod_tab); + erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz)); + return res; } -Module *module_code(int i) +Module *module_code(int i, ErtsCodeIndex code_ix) { - return (Module*) erts_index_lookup(&module_table, i); + return (Module*) erts_index_lookup(&module_tables[code_ix], i); } -int module_code_size(void) +int module_code_size(ErtsCodeIndex code_ix) { - return module_table.entries; + return module_tables[code_ix].entries; } int module_table_sz(void) { - return index_table_sz(&module_table); + return erts_smp_atomic_read_nob(&tot_module_bytes); +} + +#ifdef DEBUG +static ErtsCodeIndex dbg_load_code_ix = 0; +#endif + +static int entries_at_start_staging = 0; + +void module_start_staging(void) +{ + IndexTable* src = &module_tables[erts_active_code_ix()]; + IndexTable* dst = &module_tables[erts_staging_code_ix()]; + Module* src_mod; + Module* dst_mod; + int i, oldsz, newsz; + + ASSERT(dbg_load_code_ix == -1); + ASSERT(dst->entries <= src->entries); + + /* + * Make sure our existing modules are up-to-date + */ + for (i = 0; i < dst->entries; i++) { + src_mod = (Module*) erts_index_lookup(src, i); + dst_mod = (Module*) erts_index_lookup(dst, i); + ASSERT(src_mod->module == dst_mod->module); + + dst_mod->curr = src_mod->curr; + dst_mod->old = src_mod->old; + } + + /* + * Copy all new modules from active table + */ + oldsz = index_table_sz(dst); + for (i = dst->entries; i < src->entries; i++) { + src_mod = (Module*) erts_index_lookup(src, i); + dst_mod = (Module*) index_put_entry(dst, src_mod); + ASSERT(dst_mod != src_mod); + + dst_mod->curr = src_mod->curr; + dst_mod->old = src_mod->old; + } + newsz = index_table_sz(dst); + erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz)); + + entries_at_start_staging = dst->entries; + IF_DEBUG(dbg_load_code_ix = erts_staging_code_ix()); +} + +void module_end_staging(int commit) +{ + ASSERT(dbg_load_code_ix == erts_staging_code_ix()); + + if (!commit) { /* abort */ + IndexTable* tab = &module_tables[erts_staging_code_ix()]; + int oldsz, newsz; + + ASSERT(entries_at_start_staging <= tab->entries); + oldsz = index_table_sz(tab); + index_erase_latest_from(tab, entries_at_start_staging); + newsz = index_table_sz(tab); + erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz)); + } + + IF_DEBUG(dbg_load_code_ix = -1); } + diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h index 694e4ab72f..6e123a0ffe 100644 --- a/erts/emulator/beam/module.h +++ b/erts/emulator/beam/module.h @@ -24,28 +24,72 @@ #include "index.h" #endif +struct erl_module_instance { + BeamInstr* code; + int code_length; /* Length of loaded code in bytes. */ + unsigned catches; + struct erl_module_nif* nif; + int num_breakpoints; + int num_traced_exports; +}; typedef struct erl_module { IndexSlot slot; /* Must be located at top of struct! */ int module; /* Atom index for module (not tagged). */ - BeamInstr* code; - BeamInstr* old_code; - int code_length; /* Length of loaded code in bytes. */ - int old_code_length; /* Length of old loaded code in bytes */ - unsigned catches, old_catches; - struct erl_module_nif* nif; - struct erl_module_nif* old_nif; + struct erl_module_instance curr; + struct erl_module_instance old; /* protected by "old_code" rwlock */ } Module; -Module* erts_get_module(Eterm mod); +Module* erts_get_module(Eterm mod, ErtsCodeIndex code_ix); Module* erts_put_module(Eterm mod); void init_module_table(void); +void module_start_staging(void); +void module_end_staging(int commit); void module_info(int, void *); -Module *module_code(int); -int module_code_size(void); +Module *module_code(int, ErtsCodeIndex); +int module_code_size(ErtsCodeIndex); int module_table_sz(void); +ERTS_GLB_INLINE void erts_rwlock_old_code(ErtsCodeIndex); +ERTS_GLB_INLINE void erts_rwunlock_old_code(ErtsCodeIndex); +ERTS_GLB_INLINE void erts_rlock_old_code(ErtsCodeIndex); +ERTS_GLB_INLINE void erts_runlock_old_code(ErtsCodeIndex); +#ifdef ERTS_ENABLE_LOCK_CHECK +int erts_is_old_code_rlocked(ErtsCodeIndex); +#endif + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +extern erts_smp_rwmtx_t the_old_code_rwlocks[ERTS_NUM_CODE_IX]; + +ERTS_GLB_INLINE void erts_rwlock_old_code(ErtsCodeIndex code_ix) +{ + erts_smp_rwmtx_rwlock(&the_old_code_rwlocks[code_ix]); +} +ERTS_GLB_INLINE void erts_rwunlock_old_code(ErtsCodeIndex code_ix) +{ + erts_smp_rwmtx_rwunlock(&the_old_code_rwlocks[code_ix]); +} +ERTS_GLB_INLINE void erts_rlock_old_code(ErtsCodeIndex code_ix) +{ + erts_smp_rwmtx_rlock(&the_old_code_rwlocks[code_ix]); +} +ERTS_GLB_INLINE void erts_runlock_old_code(ErtsCodeIndex code_ix) +{ + erts_smp_rwmtx_runlock(&the_old_code_rwlocks[code_ix]); +} + +#ifdef ERTS_ENABLE_LOCK_CHECK +ERTS_GLB_INLINE int erts_is_old_code_rlocked(ErtsCodeIndex code_ix) +{ + return erts_smp_lc_rwmtx_is_rlocked(&the_old_code_rwlocks[code_ix]); +} #endif + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + + +#endif /* !__MODULE_H__ */ diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 9b168889dd..6764e88c81 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -62,11 +62,8 @@ label L i_func_info I a a I int_code_end -i_trace_breakpoint -i_mtrace_breakpoint +i_generic_breakpoint i_debug_breakpoint -i_count_breakpoint -i_time_breakpoint i_return_time_trace i_return_to_trace i_yield @@ -522,7 +519,6 @@ apply_bif call_nif call_error_handler error_action_code -call_traced_function return_trace # @@ -829,16 +825,20 @@ call_ext_only Ar=u==2 Bif=u$bif:erlang:load_nif/2 => allocate u Ar | i_call_ext # -# The apply/2 and apply/3 BIFs are instructions. +# apply/2 is an instruction, not a BIF. # call_ext u==2 u$func:erlang:apply/2 => i_apply_fun call_ext_last u==2 u$func:erlang:apply/2 D => i_apply_fun_last D call_ext_only u==2 u$func:erlang:apply/2 => i_apply_fun_only -call_ext u==3 u$func:erlang:apply/3 => i_apply -call_ext_last u==3 u$func:erlang:apply/3 D => i_apply_last D -call_ext_only u==3 u$func:erlang:apply/3 => i_apply_only +# +# The apply/3 BIF is an instruction. +# + +call_ext u==3 u$bif:erlang:apply/3 => i_apply +call_ext_last u==3 u$bif:erlang:apply/3 D => i_apply_last D +call_ext_only u==3 u$bif:erlang:apply/3 => i_apply_only # # The exit/1 and throw/1 BIFs never execute the instruction following them; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 0d3b910278..1dc6f1d233 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -25,11 +25,6 @@ # define NO_FPE_SIGNALS #endif -/* xxxP __VXWORKS__ */ -#ifdef VXWORKS -#include <vxWorks.h> -#endif - #ifdef DISABLE_CHILD_WAITER_THREAD #undef ENABLE_CHILD_WAITER_THREAD #endif @@ -43,8 +38,6 @@ #if defined (__WIN32__) # include "erl_win_sys.h" -#elif defined (VXWORKS) -# include "erl_vxworks_sys.h" #else # include "erl_unix_sys.h" #ifndef UNIX @@ -182,12 +175,6 @@ void erl_assert_error(char* expr, char* file, int line); # define const #endif -#ifdef VXWORKS -/* Replace VxWorks' printf with a real one that does fprintf(stdout, ...) */ -int real_printf(const char *fmt, ...); -# define printf real_printf -#endif - #undef __deprecated #if ERTS_AT_LEAST_GCC_VSN__(3, 0, 0) # define __deprecated __attribute__((deprecated)) @@ -486,38 +473,28 @@ static unsigned long zero_value = 0, one_value = 1; # define SET_NONBLOCKING(fd) ioctlsocket((fd), FIONBIO, &one_value) # else -# ifdef VXWORKS -# include <fcntl.h> /* xxxP added for O_WRONLY etc ... macro:s ... */ -# include <ioLib.h> -static const int zero_value = 0, one_value = 1; -# define SET_BLOCKING(fd) ioctl((fd), FIONBIO, (int)&zero_value) -# define SET_NONBLOCKING(fd) ioctl((fd), FIONBIO, (int)&one_value) -# define ERRNO_BLOCK EWOULDBLOCK - -# else -# ifdef NB_FIONBIO /* Old BSD */ -# include <sys/ioctl.h> +# ifdef NB_FIONBIO /* Old BSD */ +# include <sys/ioctl.h> static const int zero_value = 0, one_value = 1; -# define SET_BLOCKING(fd) ioctl((fd), FIONBIO, &zero_value) -# define SET_NONBLOCKING(fd) ioctl((fd), FIONBIO, &one_value) -# define ERRNO_BLOCK EWOULDBLOCK -# else /* !NB_FIONBIO */ -# include <fcntl.h> -# ifdef NB_O_NDELAY /* Nothing needs this? */ -# define NB_FLAG O_NDELAY -# ifndef ERRNO_BLOCK /* allow override (e.g. EAGAIN) via Makefile */ -# define ERRNO_BLOCK EWOULDBLOCK -# endif -# else /* !NB_O_NDELAY */ /* The True Way - POSIX!:-) */ -# define NB_FLAG O_NONBLOCK -# define ERRNO_BLOCK EAGAIN -# endif /* !NB_O_NDELAY */ -# define SET_BLOCKING(fd) fcntl((fd), F_SETFL, \ - fcntl((fd), F_GETFL, 0) & ~NB_FLAG) -# define SET_NONBLOCKING(fd) fcntl((fd), F_SETFL, \ - fcntl((fd), F_GETFL, 0) | NB_FLAG) -# endif /* !NB_FIONBIO */ -# endif /* _WXWORKS_ */ +# define SET_BLOCKING(fd) ioctl((fd), FIONBIO, &zero_value) +# define SET_NONBLOCKING(fd) ioctl((fd), FIONBIO, &one_value) +# define ERRNO_BLOCK EWOULDBLOCK +# else /* !NB_FIONBIO */ +# include <fcntl.h> +# ifdef NB_O_NDELAY /* Nothing needs this? */ +# define NB_FLAG O_NDELAY +# ifndef ERRNO_BLOCK /* allow override (e.g. EAGAIN) via Makefile */ +# define ERRNO_BLOCK EWOULDBLOCK +# endif +# else /* !NB_O_NDELAY */ /* The True Way - POSIX!:-) */ +# define NB_FLAG O_NONBLOCK +# define ERRNO_BLOCK EAGAIN +# endif /* !NB_O_NDELAY */ +# define SET_BLOCKING(fd) fcntl((fd), F_SETFL, \ + fcntl((fd), F_GETFL, 0) & ~NB_FLAG) +# define SET_NONBLOCKING(fd) fcntl((fd), F_SETFL, \ + fcntl((fd), F_GETFL, 0) | NB_FLAG) +# endif /* !NB_FIONBIO */ # endif /* !__WIN32__ */ #endif /* WANT_NONBLOCKING */ @@ -870,13 +847,6 @@ erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val) extern int erts_use_kernel_poll; #endif -#if defined(VXWORKS) -/* NOTE! sys_calloc2 does not exist on other - platforms than VxWorks and OSE */ -void* sys_calloc2(Uint, Uint); -#endif /* VXWORKS || OSE */ - - #define sys_memcpy(s1,s2,n) memcpy(s1,s2,n) #define sys_memmove(s1,s2,n) memmove(s1,s2,n) #define sys_memcmp(s1,s2,n) memcmp(s1,s2,n) @@ -983,43 +953,6 @@ void erl_bin_write(unsigned char *, int, int); # define DEBUGF(x) #endif - -#ifdef VXWORKS -/* This includes redefines of malloc etc - this should be done after sys_alloc, etc, above */ -# include "reclaim.h" -/*********************Malloc and friends************************ - * There is a problem with the naming of malloc and friends, - * malloc is used throughout sys.c and the resolver to mean save_alloc, - * but it should actually mean either sys_alloc or sys_alloc2, - * so the definitions from reclaim_master.h are not any - * good, i redefine the malloc family here, although it's quite - * ugly, actually it would be preferrable to use the - * names sys_alloc and so on throughout the offending code, but - * that will be saved as an later exercise... - * I also add an own calloc, to make the BSD resolver source happy. - ***************************************************************/ -/* Undefine malloc and friends */ -# ifdef malloc -# undef malloc -# endif -# ifdef calloc -# undef calloc -# endif -# ifdef realloc -# undef realloc -# endif -# ifdef free -# undef free -# endif -/* Redefine malloc and friends */ -# define malloc sys_alloc -# define calloc sys_calloc -# define realloc sys_realloc -# define free sys_free - -#endif - #ifdef __WIN32__ #ifdef ARCH_64 #define ERTS_ALLOC_ALIGN_BYTES 16 @@ -1035,23 +968,20 @@ void erl_bin_write(unsigned char *, int, int); #ifdef __WIN32__ - void call_break_handler(void); char* last_error(void); char* win32_errorstr(int); - - #endif /************************************************************************ * Find out the native filename encoding of the process (look at locale of * Unix processes and just do UTF16 on windows ************************************************************************/ -#define ERL_FILENAME_UNKNOWN 0 -#define ERL_FILENAME_LATIN1 1 -#define ERL_FILENAME_UTF8 2 -#define ERL_FILENAME_UTF8_MAC 3 -#define ERL_FILENAME_WIN_WCHAR 4 +#define ERL_FILENAME_UNKNOWN (0) +#define ERL_FILENAME_LATIN1 (1) +#define ERL_FILENAME_UTF8 (2) +#define ERL_FILENAME_UTF8_MAC (3) +#define ERL_FILENAME_WIN_WCHAR (4) int erts_get_native_filename_encoding(void); /* The set function is only to be used by erl_init! */ @@ -1061,4 +991,3 @@ int erts_get_user_requested_filename_encoding(void); void erts_init_sys_common_misc(void); #endif - diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 603d1d47b6..347247ee7b 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -522,7 +522,7 @@ static void *ef_safe_alloc(Uint s) static void *ef_safe_realloc(void *op, Uint s) { void *p = EF_REALLOC(op, s); - if (!p) erl_exit(1, "efile drv: Can't reallocate %d bytes of memory\n", s); + if (!p) erl_exit(1, "efile drv: Can't reallocate %lu bytes of memory\n", (unsigned long)s); return p; } diff --git a/erts/emulator/drivers/common/gzio.c b/erts/emulator/drivers/common/gzio.c index a9303d55bc..ca6d25adb4 100644 --- a/erts/emulator/drivers/common/gzio.c +++ b/erts/emulator/drivers/common/gzio.c @@ -21,11 +21,6 @@ #include "erl_driver.h" #include "sys.h" -#ifdef VXWORKS -/* pull in FOPEN from zutil.h instead */ -#undef F_OPEN -#endif - #ifdef __WIN32__ #ifndef HAVE_CONFLICTING_FREAD_DECLARATION #define HAVE_CONFLICTING_FREAD_DECLARATION diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index bf376f0494..0d6419e50e 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -284,27 +284,15 @@ static unsigned long one_value = 1; #else -#ifdef VXWORKS -#include <sockLib.h> -#include <sys/times.h> -#include <iosLib.h> -#include <taskLib.h> -#include <selectLib.h> -#include <ioLib.h> -#else #include <sys/time.h> #ifdef NETDB_H_NEEDS_IN_H #include <netinet/in.h> #endif #include <netdb.h> -#endif #include <sys/socket.h> #include <netinet/in.h> -#ifdef VXWORKS -#include <rpc/rpctypes.h> -#endif #ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H #include <rpc/types.h> #endif @@ -312,12 +300,10 @@ static unsigned long one_value = 1; #include <netinet/tcp.h> #include <arpa/inet.h> -#if (!defined(VXWORKS)) #include <sys/param.h> #ifdef HAVE_ARPA_NAMESER_H #include <arpa/nameser.h> #endif -#endif #ifdef HAVE_SYS_SOCKIO_H #include <sys/sockio.h> @@ -331,7 +317,7 @@ static unsigned long one_value = 1; /* SCTP support -- currently for UNIX platforms only: */ #undef HAVE_SCTP -#if (!defined(VXWORKS) && !defined(__WIN32__) && defined(HAVE_SCTP_H)) +#if (!defined(__WIN32__) && defined(HAVE_SCTP_H)) #include <netinet/sctp.h> @@ -478,15 +464,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define sock_connect(s, addr, len) connect((s), (addr), (len)) #define sock_listen(s, b) listen((s), (b)) #define sock_bind(s, addr, len) bind((s), (addr), (len)) -#ifdef VXWORKS -#define sock_getopt(s,t,n,v,l) wrap_sockopt(&getsockopt,\ - s,t,n,v,(unsigned int)(l)) -#define sock_setopt(s,t,n,v,l) wrap_sockopt(&setsockopt,\ - s,t,n,v,(unsigned int)(l)) -#else #define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) #define sock_setopt(s,t,n,v,l) setsockopt((s),(t),(n),(v),(l)) -#endif #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_peer(s, addr, len) getpeername((s), (addr), (len)) #define sock_ntohs(x) ntohs((x)) @@ -535,6 +514,12 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #endif /* __WIN32__ */ +#ifdef HAVE_SOCKLEN_T +# define SOCKLEN_T socklen_t +#else +# define SOCKLEN_T int +#endif + #include "packet_parser.h" #define get_int24(s) ((((unsigned char*) (s))[0] << 16) | \ @@ -685,7 +670,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_LOPT_EXITONCLOSE 26 /* exit port on active close or not ! */ #define INET_LOPT_TCP_HIWTRMRK 27 /* set local high watermark */ #define INET_LOPT_TCP_LOWTRMRK 28 /* set local low watermark */ -#define INET_LOPT_BIT8 29 /* set 8 bit detection */ + /* 29 unused */ #define INET_LOPT_TCP_SEND_TIMEOUT 30 /* set send timeout */ #define INET_LOPT_TCP_DELAY_SEND 31 /* Delay sends until next poll */ #define INET_LOPT_PACKET_SIZE 32 /* Max packet size */ @@ -720,12 +705,6 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_IFOPT_FLAGS 6 #define INET_IFOPT_HWADDR 7 -/* INET_LOPT_BIT8 options */ -#define INET_BIT8_CLEAR 0 -#define INET_BIT8_SET 1 -#define INET_BIT8_ON 2 -#define INET_BIT8_OFF 3 - /* INET_REQ_GETSTAT enumeration */ #define INET_STAT_RECV_CNT 1 #define INET_STAT_RECV_MAX 2 @@ -921,7 +900,6 @@ typedef struct { int mode; /* BINARY | LIST (affect how to interpret hsz) */ int exitf; /* exit port on close or not */ - int bit8f; /* check if data has bit number 7 set */ int deliver; /* Delivery mode, TERM or PORT */ ErlDrvTermData caller; /* recipient of sync reply */ @@ -940,8 +918,6 @@ typedef struct { int sfamily; /* address family */ enum PacketParseType htype; /* header type (TCP only?) */ unsigned int psize; /* max packet size (TCP only?) */ - int bit8; /* set if bit8f==true and data some data - seen had the 7th bit set */ inet_address remote; /* remote address for connected sockets */ inet_address peer_addr; /* fake peer address */ inet_address name_addr; /* fake local address */ @@ -3124,6 +3100,7 @@ static int tcp_message(inet_descriptor* desc, const char* buf, int len) int i = 0; DEBUGF(("tcp_message(%ld): len = %d\r\n", (long)desc->port, len)); + /* XXX fprintf(stderr,"tcp_message send.\r\n"); */ i = LOAD_ATOM(spec, i, am_tcp); i = LOAD_PORT(spec, i, desc->dport); @@ -3341,17 +3318,6 @@ static int packet_error_message(udp_descriptor* udesc, int err) } -/* scan buffer for bit 7 */ -static void scanbit8(inet_descriptor* desc, const char* buf, int len) -{ - int c; - - if (!desc->bit8f || desc->bit8) return; - c = 0; - while(len--) c |= *buf++; - desc->bit8 = ((c & 0x80) != 0); -} - /* ** active=TRUE: ** (NOTE! distribution MUST use active=TRUE, deliver=PORT) @@ -3369,8 +3335,6 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len) packet_get_body(desc->inet.htype, &body, &bodylen); - scanbit8(INETP(desc), body, bodylen); - if (desc->inet.deliver == INET_DELIVER_PORT) { code = inet_port_data(INETP(desc), body, bodylen); } @@ -3402,8 +3366,6 @@ tcp_reply_binary_data(tcp_descriptor* desc, ErlDrvBinary* bin, int offs, int len packet_get_body(desc->inet.htype, &body, &bodylen); offs = body - bin->orig_bytes; /* body offset now */ - scanbit8(INETP(desc), body, bodylen); - if (desc->inet.deliver == INET_DELIVER_PORT) code = inet_port_binary_data(INETP(desc), bin, offs, bodylen); else if ((code=packet_parse(desc->inet.htype, buf, len, &desc->http_state, @@ -3429,8 +3391,6 @@ packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz, { int code; - scanbit8(desc, bin->orig_bytes+offs, len); - if (desc->active == INET_PASSIVE) /* "inet" is actually for both UDP and SCTP, as well as TCP! */ return inet_async_binary_data(desc, hsz, bin, offs, len, extra); @@ -5279,50 +5239,6 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, #endif - - -#ifdef VXWORKS -/* -** THIS is a terrible creature, a bug in the TCP part -** of the old VxWorks stack (non SENS) created a race. -** If (and only if?) a socket got closed from the other -** end and we tried a set/getsockopt on the TCP level, -** the task would generate a bus error... -*/ -static STATUS wrap_sockopt(STATUS (*function)() /* Yep, no parameter - check */, - int s, int level, int optname, - char *optval, unsigned int optlen - /* optlen is a pointer if function - is getsockopt... */) -{ - fd_set rs; - struct timeval timeout; - int to_read; - int ret; - - FD_ZERO(&rs); - FD_SET(s,&rs); - memset(&timeout,0,sizeof(timeout)); - if (level == IPPROTO_TCP) { - taskLock(); - if (select(s+1,&rs,NULL,NULL,&timeout)) { - if (ioctl(s,FIONREAD,(int)&to_read) == ERROR || - to_read == 0) { /* End of file, other end closed? */ - sock_errno() = EBADF; - taskUnlock(); - return ERROR; - } - } - ret = (*function)(s,level,optname,optval,optlen); - taskUnlock(); - } else { - ret = (*function)(s,level,optname,optval,optlen); - } - return ret; -} -#endif - /* Per H @ Tail-f: The original code here had problems that possibly only occur if you abuse it for non-INET sockets, but anyway: a) If the getsockopt for SO_PRIORITY or IP_TOS failed, the actual @@ -5348,13 +5264,8 @@ static int setopt_prio_tos_trick int res; int res_prio; int res_tos; -#ifdef HAVE_SOCKLEN_T - socklen_t -#else - int -#endif - tmp_arg_sz_prio = sizeof(tmp_ival_prio), - tmp_arg_sz_tos = sizeof(tmp_ival_tos); + SOCKLEN_T tmp_arg_sz_prio = sizeof(tmp_ival_prio); + SOCKLEN_T tmp_arg_sz_tos = sizeof(tmp_ival_tos); res_prio = sock_getopt(fd, SOL_SOCKET, SO_PRIORITY, (char *) &tmp_ival_prio, &tmp_arg_sz_prio); @@ -5426,6 +5337,7 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) if (IS_SCTP(desc)) return sctp_set_opts(desc, ptr, len); #endif + /* XXX { int i; for(i=0;i<len;++i) fprintf(stderr,"0x%02X, ", (unsigned) ptr[i]); fprintf(stderr,"\r\n");} */ while(len >= 5) { opt = *ptr++; @@ -5497,29 +5409,6 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) desc->exitf = ival; continue; - case INET_LOPT_BIT8: - DEBUGF(("inet_set_opts(%ld): s=%d, BIT8=%d\r\n", - (long)desc->port, desc->s, ival)); - switch(ival) { - case INET_BIT8_ON: - desc->bit8f = 1; - desc->bit8 = 0; - break; - case INET_BIT8_OFF: - desc->bit8f = 0; - desc->bit8 = 0; - break; - case INET_BIT8_CLEAR: - desc->bit8f = 1; - desc->bit8 = 0; - break; - case INET_BIT8_SET: - desc->bit8f = 1; - desc->bit8 = 1; - break; - } - continue; - case INET_LOPT_TCP_HIWTRMRK: if (desc->stype == SOCK_STREAM) { tcp_descriptor* tdesc = (tcp_descriptor*) desc; @@ -5601,23 +5490,11 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) case INET_OPT_SNDBUF: type = SO_SNDBUF; DEBUGF(("inet_set_opts(%ld): s=%d, SO_SNDBUF=%d\r\n", (long)desc->port, desc->s, ival)); - /* - * Setting buffer sizes in VxWorks gives unexpected results - * our workaround is to leave it at default. - */ -#ifdef VXWORKS - goto skip_os_setopt; -#else break; -#endif case INET_OPT_RCVBUF: type = SO_RCVBUF; DEBUGF(("inet_set_opts(%ld): s=%d, SO_RCVBUF=%d\r\n", (long)desc->port, desc->s, ival)); -#ifdef VXWORKS - goto skip_os_setopt; -#else break; -#endif case INET_OPT_LINGER: type = SO_LINGER; if (len < 4) return -1; @@ -5739,9 +5616,6 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) } DEBUGF(("inet_set_opts(%ld): s=%d returned %d\r\n", (long)desc->port, desc->s, res)); -#ifdef VXWORKS -skip_os_setopt: -#endif if (type == SO_RCVBUF) { /* make sure we have desc->bufsz >= SO_RCVBUF */ if (ival > desc->bufsz) @@ -5755,10 +5629,16 @@ skip_os_setopt: if (desc->active != old_active) sock_select(desc, (FD_READ|FD_CLOSE), (desc->active>0)); + /* XXX: UDP sockets could also trigger immediate read here NIY */ if ((desc->stype==SOCK_STREAM) && desc->active) { if (!old_active || (desc->htype != old_htype)) { /* passive => active change OR header type change in active mode */ - return 1; + /* Return > 1 if only active changed to INET_ONCE -> direct read if + header type is unchanged. */ + /* XXX fprintf(stderr,"desc->htype == %d, old_htype == %d, + desc->active == %d, old_active == %d\r\n",(int)desc->htype, + (int) old_htype, (int) desc->active, (int) old_active );*/ + return 1+(desc->htype == old_htype && desc->active == INET_ONCE); } return 0; } @@ -6396,15 +6276,6 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, put_int32(desc->exitf, ptr); continue; - case INET_LOPT_BIT8: - *ptr++ = opt; - if (desc->bit8f) { - put_int32(desc->bit8, ptr); - } else { - put_int32(INET_BIT8_OFF, ptr); - } - continue; - case INET_LOPT_TCP_HIWTRMRK: if (desc->stype == SOCK_STREAM) { *ptr++ = opt; @@ -7463,8 +7334,6 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) desc->mode = INET_MODE_LIST; /* list mode */ desc->exitf = 1; /* exit port when close on active socket */ - desc->bit8f = 0; - desc->bit8 = 0; desc->deliver = INET_DELIVER_TERM; /* standard term format */ desc->active = INET_PASSIVE; /* start passive */ desc->oph = NULL; @@ -7592,17 +7461,27 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, case INET_REQ_SETOPTS: { /* set options */ DEBUGF(("inet_ctl(%ld): SETOPTS\r\n", (long)desc->port)); + /* XXX fprintf(stderr,"inet_ctl(%ld): SETOPTS (len = %d)\r\n", (long)desc->port,(int) len); */ switch(inet_set_opts(desc, buf, len)) { case -1: return ctl_error(EINVAL, rbuf, rsize); case 0: return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); - default: /* active/passive change!! */ + case 1: /* * Let's hope that the descriptor really is a tcp_descriptor here. */ + /* fprintf(stderr,"Triggered tcp_deliver by setopt.\r\n"); */ tcp_deliver((tcp_descriptor *) desc, 0); return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); + default: + /* fprintf(stderr,"Triggered tcp_recv by setopt.\r\n"); */ + /* + * Same as above, but active changed to once w/o header type + * change, so try a read instead of just deliver. + */ + tcp_recv((tcp_descriptor *) desc, 0); + return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } } @@ -7762,8 +7641,8 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, desc->state = INET_STATE_BOUND; if ((port = inet_address_port(&local)) == 0) { - len = sizeof(local); - sock_name(desc->s, (struct sockaddr*) &local, (unsigned int*)&len); + SOCKLEN_T adrlen = sizeof(local); + sock_name(desc->s, &local.sa, &adrlen); port = inet_address_port(&local); } port = sock_ntohs(port); @@ -7798,8 +7677,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } -#ifndef VXWORKS - case INET_REQ_GETSERVBYNAME: { /* L1 Name-String L2 Proto-String */ char namebuf[256]; char protobuf[256]; @@ -7850,8 +7727,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, return ctl_reply(INET_REP_OK, srv->s_name, len, rbuf, rsize); } -#endif /* !VXWORKS */ - default: return ctl_xerror(EXBADPORT, rbuf, rsize); } @@ -8082,7 +7957,6 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, /* Some flags must be inherited at this point */ copy_desc->inet.mode = desc->inet.mode; copy_desc->inet.exitf = desc->inet.exitf; - copy_desc->inet.bit8f = desc->inet.bit8f; copy_desc->inet.deliver = desc->inet.deliver; copy_desc->inet.htype = desc->inet.htype; copy_desc->inet.psize = desc->inet.psize; @@ -9196,6 +9070,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) #endif ASSERT(!INETP(desc)->is_ignored); DEBUGF(("tcp_inet_input(%ld) {s=%d\r\n", port, desc->inet.s)); + /* XXX fprintf(stderr,"tcp_inet_input(%ld) {s=%d}\r\n",(long) desc->inet.port, desc->inet.s); */ if (desc->inet.state == INET_STATE_ACCEPTING) { SOCKET s; unsigned int len; @@ -9786,7 +9661,6 @@ static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err) /* Some flags must be inherited at this point */ copy_desc->inet.mode = desc->inet.mode; copy_desc->inet.exitf = desc->inet.exitf; - copy_desc->inet.bit8f = desc->inet.bit8f; copy_desc->inet.deliver = desc->inet.deliver; copy_desc->inet.htype = desc->inet.htype; copy_desc->inet.psize = desc->inet.psize; diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index ad112f7590..2aa373aa7d 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -45,23 +45,6 @@ #include <fcntl.h> #endif /* DARWIN */ -#ifdef VXWORKS -#include <ioLib.h> -#include <dosFsLib.h> -#include <nfsLib.h> -#include <sys/stat.h> -/* -** Not nice to include usrLib.h as MANY normal variable names get reported -** as shadowing globals, like 'i' for example. -** Instead we declare the only function we use here -*/ -/* - * #include <usrLib.h> - */ -extern STATUS copy(char *, char *); -#include <errno.h> -#endif - #ifdef SUNOS4 # define getcwd(buf, size) getwd(buf) #endif @@ -93,276 +76,27 @@ extern STATUS copy(char *, char *); #define DIR_MODE 0777 #endif -#ifdef VXWORKS /* Currently only used on vxworks */ - -#define EF_ALLOC(S) driver_alloc((S)) -#define EF_REALLOC(P, S) driver_realloc((P), (S)) -#define EF_SAFE_ALLOC(S) ef_safe_alloc((S)) -#define EF_SAFE_REALLOC(P, S) ef_safe_realloc((P), (S)) -#define EF_FREE(P) do { if((P)) driver_free((P)); } while(0) - -void erl_exit(int n, char *fmt, ...); - -static void *ef_safe_alloc(Uint s) -{ - void *p = EF_ALLOC(s); - if (!p) erl_exit(1, - "unix efile drv: Can't allocate %d bytes of memory\n", - s); - return p; -} - -#if 0 /* Currently not used */ - -static void *ef_safe_realloc(void *op, Uint s) -{ - void *p = EF_REALLOC(op, s); - if (!p) erl_exit(1, - "unix efile drv: Can't reallocate %d bytes of memory\n", - s); - return p; -} - -#endif /* #if 0 */ -#endif /* #ifdef VXWORKS */ - #define IS_DOT_OR_DOTDOT(s) \ (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0'))) -#ifdef VXWORKS -static int vxworks_to_posix(int vx_errno); -#endif - -/* -** VxWorks (not) strikes again. Too long RESULTING paths -** may give the infamous bus error. Have to check ALL -** filenames and pathnames. No wonder the emulator is slow on -** these cards... -*/ -#ifdef VXWORKS -#define CHECK_PATHLEN(Name, ErrInfo) \ - if (path_size(Name) > PATH_MAX) { \ - errno = ENAMETOOLONG; \ - return check_error(-1, ErrInfo); \ - } -#else -#define CHECK_PATHLEN(X,Y) /* Nothing */ -#endif - static int check_error(int result, Efile_error* errInfo); static int check_error(int result, Efile_error *errInfo) { if (result < 0) { -#ifdef VXWORKS - errInfo->posix_errno = errInfo->os_errno = vxworks_to_posix(errno); -#else errInfo->posix_errno = errInfo->os_errno = errno; -#endif return 0; } return 1; } -#ifdef VXWORKS - -/* - * VxWorks has different error codes for different file systems. - * We map those to POSIX ones. - */ -static int -vxworks_to_posix(int vx_errno) -{ - DEBUGF(("[vxworks_to_posix] vx_errno: %08x\n", vx_errno)); - switch (vx_errno) { - /* dosFsLib mapping */ -#ifdef S_dosFsLib_FILE_ALREADY_EXISTS - case S_dosFsLib_FILE_ALREADY_EXISTS: return EEXIST; -#else - case S_dosFsLib_FILE_EXISTS: return EEXIST; -#endif -#ifdef S_dosFsLib_BAD_DISK - case S_dosFsLib_BAD_DISK: return EIO; -#endif -#ifdef S_dosFsLib_CANT_CHANGE_ROOT - case S_dosFsLib_CANT_CHANGE_ROOT: return EINVAL; -#endif -#ifdef S_dosFsLib_NO_BLOCK_DEVICE - case S_dosFsLib_NO_BLOCK_DEVICE: return ENOTBLK; -#endif -#ifdef S_dosFsLib_BAD_SEEK - case S_dosFsLib_BAD_SEEK: return ESPIPE; -#endif - case S_dosFsLib_VOLUME_NOT_AVAILABLE: return ENXIO; - case S_dosFsLib_DISK_FULL: return ENOSPC; - case S_dosFsLib_FILE_NOT_FOUND: return ENOENT; - case S_dosFsLib_NO_FREE_FILE_DESCRIPTORS: return ENFILE; - case S_dosFsLib_INVALID_NUMBER_OF_BYTES: return EINVAL; - case S_dosFsLib_ILLEGAL_NAME: return EINVAL; - case S_dosFsLib_CANT_DEL_ROOT: return EACCES; - case S_dosFsLib_NOT_FILE: return EISDIR; - case S_dosFsLib_NOT_DIRECTORY: return ENOTDIR; - case S_dosFsLib_NOT_SAME_VOLUME: return EXDEV; - case S_dosFsLib_READ_ONLY: return EACCES; - case S_dosFsLib_ROOT_DIR_FULL: return ENOSPC; - case S_dosFsLib_DIR_NOT_EMPTY: return EEXIST; - case S_dosFsLib_NO_LABEL: return ENXIO; - case S_dosFsLib_INVALID_PARAMETER: return EINVAL; - case S_dosFsLib_NO_CONTIG_SPACE: return ENOSPC; - case S_dosFsLib_FD_OBSOLETE: return EBADF; - case S_dosFsLib_DELETED: return EINVAL; - case S_dosFsLib_INTERNAL_ERROR: return EIO; - case S_dosFsLib_WRITE_ONLY: return EACCES; - /* nfsLib mapping - is needed since Windriver has used */ - /* inconsistent error codes (errno.h/nfsLib.h). */ - case S_nfsLib_NFS_OK: return 0; - case S_nfsLib_NFSERR_PERM: return EPERM; - case S_nfsLib_NFSERR_NOENT: return ENOENT; - case S_nfsLib_NFSERR_IO: return EIO; - case S_nfsLib_NFSERR_NXIO: return ENXIO; -#ifdef S_nfsLib_NFSERR_ACCES - case S_nfsLib_NFSERR_ACCES: return EACCES; -#else - case S_nfsLib_NFSERR_ACCESS: return EACCES; -#endif - case S_nfsLib_NFSERR_EXIST: return EEXIST; - case S_nfsLib_NFSERR_NODEV: return ENODEV; - case S_nfsLib_NFSERR_NOTDIR: return ENOTDIR; - case S_nfsLib_NFSERR_ISDIR: return EISDIR; - case S_nfsLib_NFSERR_FBIG: return EFBIG; - case S_nfsLib_NFSERR_NOSPC: return ENOSPC; - case S_nfsLib_NFSERR_ROFS: return EROFS; - case S_nfsLib_NFSERR_NAMETOOLONG: return ENAMETOOLONG; - case S_nfsLib_NFSERR_NOTEMPTY: return EEXIST; - case S_nfsLib_NFSERR_DQUOT: return ENOSPC; - case S_nfsLib_NFSERR_STALE: return EINVAL; - case S_nfsLib_NFSERR_WFLUSH: return ENXIO; - /* And sometimes (...) the error codes are from ioLib (as in the */ - /* case of the (for nfsLib) unimplemented rename function) */ - case S_ioLib_DISK_NOT_PRESENT: return EIO; -#if S_ioLib_DISK_NOT_PRESENT != S_ioLib_NO_DRIVER - case S_ioLib_NO_DRIVER: return ENXIO; -#endif - case S_ioLib_UNKNOWN_REQUEST: return ENOSYS; - case S_ioLib_DEVICE_TIMEOUT: return EIO; -#ifdef S_ioLib_UNFORMATED - /* Added (VxWorks 5.2 -> 5.3.1) */ - #if S_ioLib_UNFORMATED != S_ioLib_DEVICE_TIMEOUT - case S_ioLib_UNFORMATED: return EIO; - #endif -#endif -#if S_ioLib_DEVICE_TIMEOUT != S_ioLib_DEVICE_ERROR - case S_ioLib_DEVICE_ERROR: return ENXIO; -#endif - case S_ioLib_WRITE_PROTECTED: return EACCES; - case S_ioLib_NO_FILENAME: return EINVAL; - case S_ioLib_CANCELLED: return EINTR; - case S_ioLib_NO_DEVICE_NAME_IN_PATH: return EINVAL; - case S_ioLib_NAME_TOO_LONG: return ENAMETOOLONG; -#ifdef S_objLib_OBJ_UNAVAILABLE - case S_objLib_OBJ_UNAVAILABLE: return ENOENT; -#endif - - /* Temporary workaround for a weird error in passFs - (VxWorks Simsparc only). File operation fails because of - ENOENT, but errno is not set. */ -#ifdef SIMSPARCSOLARIS - case 0: return ENOENT; -#endif - - } - /* If the error code matches none of the above, assume */ - /* it is a POSIX one already. The upper bits (>=16) are */ - /* cleared since VxWorks uses those bits to indicate in */ - /* what module the error occured. */ - return vx_errno & 0xffff; -} - -static int -vxworks_enotsup(Efile_error *errInfo) -{ - errInfo->posix_errno = errInfo->os_errno = ENOTSUP; - return 0; -} - -static int -count_path_length(char *pathname, char *pathname2) -{ - static int stack[PATH_MAX / 2 + 1]; - int sp = 0; - char *tmp; - char *cpy = NULL; - int i; - int sum; - for(i = 0;i < 2;++i) { - if (!i) { - cpy = EF_SAFE_ALLOC(strlen(pathname)+1); - strcpy(cpy, pathname); - } else if (pathname2 != NULL) { - EF_FREE(cpy); - cpy = EF_SAFE_ALLOC(strlen(pathname2)+1); - strcpy(cpy, pathname2); - } else - break; - - for (tmp = strtok(cpy,"/"); tmp != NULL; tmp = strtok(NULL,"/")) { - if (!strcmp(tmp,"..") && sp > 0) - --sp; - else if (strcmp(tmp,".")) - stack[sp++] = strlen(tmp); - } - } - if (cpy != NULL) - EF_FREE(cpy); - sum = 0; - for(i = 0;i < sp; ++i) - sum += stack[i]+1; - return (sum) ? sum : 1; -} - -static int -path_size(char *pathname) -{ - static char currdir[PATH_MAX+2]; - if (*pathname == '/') - return count_path_length(pathname,NULL); - ioDefPathGet(currdir); - strcat(currdir,"/"); - return count_path_length(currdir,pathname); -} - -#endif /* VXWORKS */ - int efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to create. */ { - CHECK_PATHLEN(name,errInfo); #ifdef NO_MKDIR_MODE -#ifdef VXWORKS - /* This is a VxWorks/nfs workaround for erl_tar to create - * non-existant directories. (of some reason (...) VxWorks - * returns, the *non-module-prefixed*, 0xd code when - * trying to create a directory in a directory that doesn't exist). - * (see efile_openfile) - */ - if (mkdir(name) < 0) { - struct stat sb; - if (name[0] == '\0') { - /* Return the correct error code enoent */ - errno = S_nfsLib_NFSERR_NOENT; - } else if (stat(name, &sb) == OK) { - errno = S_nfsLib_NFSERR_EXIST; - } else if((strchr(name, '/') != NULL) && (errno == 0xd)) { - /* Return the correct error code enoent */ - errno = S_nfsLib_NFSERR_NOENT; - } - return check_error(-1, errInfo); - } else return 1; -#else return check_error(mkdir(name), errInfo); -#endif #else return check_error(mkdir(name, DIR_MODE), errInfo); #endif @@ -372,16 +106,9 @@ int efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to delete. */ { - CHECK_PATHLEN(name, errInfo); if (rmdir(name) == 0) { return 1; } -#ifdef VXWORKS - if (name[0] == '\0') { - /* Return the correct error code enoent */ - errno = S_nfsLib_NFSERR_NOENT; - } -#else if (errno == ENOTEMPTY) { errno = EEXIST; } @@ -401,7 +128,6 @@ efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ } errno = saved_errno; } -#endif return check_error(-1, errInfo); } @@ -409,7 +135,6 @@ int efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of file to delete. */ { - CHECK_PATHLEN(name,errInfo); if (unlink(name) == 0) { return 1; } @@ -457,32 +182,13 @@ efile_rename(Efile_error* errInfo, /* Where to return error codes. */ char* src, /* Original name. */ char* dst) /* New name. */ { - CHECK_PATHLEN(src,errInfo); - CHECK_PATHLEN(dst,errInfo); -#ifdef VXWORKS - - /* First check if src == dst, if so, just return. */ - /* VxWorks dos file system destroys the file otherwise, */ - /* VxWorks nfs file system rename doesn't work at all. */ - if(strcmp(src, dst) == 0) - return 1; -#endif if (rename(src, dst) == 0) { return 1; } -#ifdef VXWORKS - /* nfs for VxWorks doesn't support rename. We try to emulate it */ - /* (by first copying src to dst and then deleting src). */ - if(errno == S_ioLib_UNKNOWN_REQUEST && /* error code returned - by ioLib (!) */ - copy(src, dst) == OK && - unlink(src) == OK) - return 1; -#endif if (errno == ENOTEMPTY) { errno = EEXIST; } -#if defined (sparc) && !defined(VXWORKS) +#if defined (sparc) /* * SunOS 4.1.4 reports overwriting a non-empty directory with a * directory as EINVAL instead of EEXIST (first rule out the correct @@ -543,7 +249,6 @@ int efile_chdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to make current. */ { - CHECK_PATHLEN(name, errInfo); return check_error(chdir(name), errInfo); } @@ -600,8 +305,6 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ * If this is the first call, we must open the directory. */ - CHECK_PATHLEN(name, errInfo); - if (*p_dir_handle == NULL) { dp = opendir(name); if (dp == NULL) @@ -641,26 +344,8 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ struct stat statbuf; int fd; int mode; /* Open mode. */ -#ifdef VXWORKS - char pathbuff[PATH_MAX+2]; - char sbuff[PATH_MAX*2]; - char *totbuff = sbuff; - int nameneed; -#endif - - - CHECK_PATHLEN(name, errInfo); - -#ifdef VXWORKS - /* Have to check that it's not a directory. */ - if (stat(name,&statbuf) != ERROR && ISDIR(statbuf)) { - errno = EISDIR; - return check_error(-1, errInfo); - } -#endif if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) { -#if !defined(VXWORKS) && !defined(OSE) /* * For UNIX only, here is some ugly code to allow * /dev/null to be opened as a file. @@ -677,12 +362,9 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ } } if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) { -#endif errno = EISDIR; return check_error(-1, errInfo); -#if !defined(VXWORKS) && !defined(OSE) } -#endif } switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) { @@ -706,49 +388,14 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ if (flags & EFILE_MODE_APPEND) { mode &= ~O_TRUNC; -#ifndef VXWORKS - mode |= O_APPEND; /* Dont make VxWorks think things it shouldn't */ -#endif + mode |= O_APPEND; } if (flags & EFILE_MODE_EXCL) { mode |= O_EXCL; } -#ifdef VXWORKS - if (*name != '/') { - /* Make sure it is an absolute pathname, because ftruncate needs it */ - ioDefPathGet(pathbuff); - strcat(pathbuff,"/"); - nameneed = strlen(pathbuff) + strlen(name) + 1; - if (nameneed > PATH_MAX*2) - totbuff = EF_SAFE_ALLOC(nameneed); - strcpy(totbuff,pathbuff); - strcat(totbuff,name); - fd = open(totbuff, mode, FILE_MODE); - if (totbuff != sbuff) - EF_FREE(totbuff); - } else { - fd = open(name, mode, FILE_MODE); - } -#else fd = open(name, mode, FILE_MODE); -#endif - -#ifdef VXWORKS - - /* This is a VxWorks/nfs workaround for erl_tar to create - * non-existant directories. (of some reason (...) VxWorks - * returns, the *non-module-prefixed*, 0xd code when - * trying to write a file in a directory that doesn't exist). - * (see efile_mkdir) - */ - if ((fd < 0) && (strchr(name, '/') != NULL) && (errno == 0xd)) { - /* Return the correct error code enoent */ - errno = S_nfsLib_NFSERR_NOENT; - return check_error(-1, errInfo); - } -#endif if (!check_error(fd, errInfo)) return 0; @@ -797,11 +444,7 @@ efile_fsync(Efile_error *errInfo, /* Where to return error codes. */ int fd) /* File descriptor for file to sync. */ { #ifdef NO_FSYNC -#ifdef VXWORKS - return check_error(ioctl(fd, FIOSYNC, 0), errInfo); -#else - undefined fsync -#endif /* VXWORKS */ + undefined fsync /* XXX: Really? */ #else #if defined(DARWIN) && defined(F_FULLFSYNC) return check_error(fcntl(fd, F_FULLFSYNC), errInfo); @@ -818,21 +461,8 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, struct stat statbuf; /* Information about the file */ int result; -#ifdef VXWORKS - if (*name == '\0') { - errInfo->posix_errno = errInfo->os_errno = ENOENT; - return 0; - } -#endif - - CHECK_PATHLEN(name, errInfo); - if (info_for_link) { -#if (defined(VXWORKS)) - result = stat(name, &statbuf); -#else result = lstat(name, &statbuf); -#endif } else { result = stat(name, &statbuf); } @@ -849,19 +479,9 @@ efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, #ifdef NO_ACCESS /* Just look at read/write access for owner. */ -#ifdef VXWORKS - - pInfo->access = FA_NONE; - if(statbuf.st_mode & S_IRUSR) - pInfo->access |= FA_READ; - if(statbuf.st_mode & S_IWUSR) - pInfo->access |= FA_WRITE; - -#else pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1; -#endif /* VXWORKS */ #else pInfo->access = FA_NONE; if (access(name, R_OK) == 0) @@ -902,35 +522,6 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) { struct utimbuf tval; - CHECK_PATHLEN(name, errInfo); - -#ifdef VXWORKS - - if (pInfo->mode != -1) { - int fd; - struct stat statbuf; - - fd = open(name, O_RDONLY, 0); - if (!check_error(fd, errInfo)) - return 0; - if (fstat(fd, &statbuf) < 0) { - close(fd); - return check_error(-1, errInfo); - } - if (pInfo->mode & S_IWUSR) { - /* clear read only bit */ - statbuf.st_attrib &= ~DOS_ATTR_RDONLY; - } else { - /* set read only bit */ - statbuf.st_attrib |= DOS_ATTR_RDONLY; - } - /* This should work for dos files but not for nfs ditos, so don't - * report errors (to avoid problems when running e.g. erl_tar) - */ - ioctl(fd, FIOATTRIBSET, statbuf.st_attrib); - close(fd); - } -#else /* * On some systems chown will always fail for a non-root user unless * POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as @@ -952,20 +543,10 @@ efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) } } -#endif /* !VXWORKS */ - tval.actime = pInfo->accessTime; tval.modtime = pInfo->modifyTime; -#ifdef VXWORKS - /* VxWorks' utime doesn't work when the file is a nfs mounted - * one, don't report error if utime fails. - */ - utime(name, &tval); - return 1; -#else return check_error(utime(name, &tval), errInfo); -#endif } @@ -979,11 +560,6 @@ efile_write(Efile_error* errInfo, /* Where to return error codes. */ { ssize_t written; /* Bytes written in last operation. */ -#ifdef VXWORKS - if (flags & EFILE_MODE_APPEND) { - lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */ - } -#endif while (count > 0) { if ((written = write(fd, buf, count)) < 0) { if (errno != EINTR) @@ -1012,12 +588,6 @@ efile_writev(Efile_error* errInfo, /* Where to return error codes */ ASSERT(iovcnt >= 0); -#ifdef VXWORKS - if (flags & EFILE_MODE_APPEND) { - lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */ - } -#endif - while (cnt < iovcnt) { if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) { /* Empty buffer - skip */ @@ -1226,118 +796,6 @@ efile_seek(Efile_error* errInfo, /* Where to return error codes. */ int efile_truncate_file(Efile_error* errInfo, int *fd, int flags) { -#ifdef VXWORKS - off_t offset; - char namebuf[PATH_MAX+1]; - char namebuf2[PATH_MAX+10]; - int new; - int dummy; - int i; - int left; - static char buff[1024]; - struct stat st; - Efile_error tmperr; - - if ((offset = lseek(*fd, 0, 1)) < 0) { - return check_error((int) offset,errInfo); - } - if (ftruncate(*fd, offset) < 0) { - if (vxworks_to_posix(errno) != EINVAL) { - return check_error(-1, errInfo); - } - /* - ** Kludge - */ - if(ioctl(*fd,FIOGETNAME,(int) namebuf) < 0) { - return check_error(-1, errInfo); - } - for(i=0;i<1000;++i) { - sprintf(namebuf2,"%s%d",namebuf,i); - CHECK_PATHLEN(namebuf2,errInfo); - if (stat(namebuf2,&st) < 0) { - break; - } - } - if (i > 1000) { - errno = EINVAL; - return check_error(-1, errInfo); - } - if (close(*fd) < 0) { - return check_error(-1, errInfo); - } - if (efile_rename(&tmperr,namebuf,namebuf2) < 0) { - i = check_error(-1,&tmperr); - if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, - fd,&dummy)) { - *fd = -1; - } else { - *errInfo = tmperr; - } - return i; - } - if ((*fd = open(namebuf2, O_RDONLY, 0)) < 0) { - i = check_error(-1,errInfo); - efile_rename(&tmperr,namebuf2,namebuf); /* at least try */ - if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, - fd,&dummy)) { - *fd = -1; - } else { - lseek(*fd,offset,SEEK_SET); - } - return i; - } - /* Point of no return... */ - - if ((new = open(namebuf,O_RDWR | O_CREAT, FILE_MODE)) < 0) { - close(*fd); - *fd = -1; - return 0; - } - left = offset; - - while (left) { - if ((i = read(*fd,buff,(left > 1024) ? 1024 : left)) < 0) { - i = check_error(-1,errInfo); - close(new); - close(*fd); - unlink(namebuf); - efile_rename(&tmperr,namebuf2,namebuf); /* at least try */ - if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, - fd,&dummy)) { - *fd = -1; - } else { - lseek(*fd,offset,SEEK_SET); - } - return i; - } - left -= i; - if (write(new,buff,i) < 0) { - i = check_error(-1,errInfo); - close(new); - close(*fd); - unlink(namebuf); - rename(namebuf2,namebuf); /* at least try */ - if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, - fd,&dummy)) { - *fd = -1; - } else { - lseek(*fd,offset,SEEK_SET); - } - return i; - } - } - close(*fd); - unlink(namebuf2); - close(new); - i = efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,fd, - &dummy); - if (i) { - lseek(*fd,offset,SEEK_SET); - } - return i; - } - return 1; -#else #ifndef NO_FTRUNCATE off_t offset; @@ -1347,15 +805,11 @@ efile_truncate_file(Efile_error* errInfo, int *fd, int flags) #else return 1; #endif -#endif } int efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) { -#ifdef VXWORKS - return vxworks_enotsup(errInfo); -#else int len; ASSERT(size > 0); len = readlink(name, buffer, size-1); @@ -1364,7 +818,6 @@ efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) } buffer[len] = '\0'; return 1; -#endif } int @@ -1377,21 +830,13 @@ efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size) int efile_link(Efile_error* errInfo, char* old, char* new) { -#ifdef VXWORKS - return vxworks_enotsup(errInfo); -#else return check_error(link(old, new), errInfo); -#endif } int efile_symlink(Efile_error* errInfo, char* old, char* new) { -#ifdef VXWORKS - return vxworks_enotsup(errInfo); -#else return check_error(symlink(old, new), errInfo); -#endif } int diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c index e20a8a7969..651d0e3a75 100644 --- a/erts/emulator/hipe/hipe_arm.c +++ b/erts/emulator/hipe/hipe_arm.c @@ -181,11 +181,9 @@ void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process * curseg.base = base; curseg.code_pos = base; curseg.tramp_pos = (unsigned int*)((char*)base + SEGMENT_NRBYTES); -#if defined(__arm__) curseg.tramp_pos -= 2; curseg.tramp_pos[0] = 0xE51FF004; /* ldr pc, [pc,#-4] */ curseg.tramp_pos[1] = (unsigned int)&nbif_callemu; -#endif address = try_alloc(nrwords, nrcallees, callees, trampvec); if (!address) { @@ -214,11 +212,9 @@ static unsigned int *alloc_stub(Uint nrwords, unsigned int **tramp_callemu) curseg.base = base; curseg.code_pos = base; curseg.tramp_pos = (unsigned int*)((char*)base + SEGMENT_NRBYTES); -#if defined(__arm__) curseg.tramp_pos -= 2; curseg.tramp_pos[0] = 0xE51FF004; /* ldr pc, [pc,#-4] */ curseg.tramp_pos[1] = (unsigned int)&nbif_callemu; -#endif address = try_alloc(nrwords, 0, NIL, NULL); if (!address) { @@ -269,10 +265,8 @@ int hipe_patch_insn(void *address, Uint32 value, Eterm type) void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) { unsigned int *code; -#if defined(__arm__) unsigned int *tramp_callemu; int callemu_offset; -#endif /* * Native code calls BEAM via a stub looking as follows: @@ -288,13 +282,6 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) * (Trampolines are allowed to modify r12, but they don't.) */ -#if !defined(__arm__) - /* verify that 'ba' can reach nbif_callemu */ - if ((unsigned long)&nbif_callemu & ~0x01FFFFFCUL) - abort(); -#endif - -#if defined(__arm__) code = alloc_stub(4, &tramp_callemu); callemu_offset = ((int)&nbif_callemu - ((int)&code[2] + 8)) >> 2; if (!(callemu_offset >= -0x00800000 && callemu_offset <= 0x007FFFFF)) { @@ -302,11 +289,7 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) if (!(callemu_offset >= -0x00800000 && callemu_offset <= 0x007FFFFF)) abort(); } -#else - code = alloc_stub(4, &trampoline); -#endif -#if defined(__arm__) /* mov r0, #beamArity */ code[0] = 0xE3A00000 | (beamArity & 0xFF); /* ldr r8, [pc,#0] // beamAddress */ @@ -315,16 +298,6 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) code[2] = 0xEA000000 | (callemu_offset & 0x00FFFFFF); /* .long beamAddress */ code[3] = (unsigned int)beamAddress; -#else - /* addi r12,0,beamAddress@l */ - code[0] = 0x39800000 | ((unsigned long)beamAddress & 0xFFFF); - /* addi r0,0,beamArity */ - code[1] = 0x38000000 | (beamArity & 0x7FFF); - /* addis r12,r12,beamAddress@ha */ - code[2] = 0x3D8C0000 | at_ha((unsigned long)beamAddress); - /* ba nbif_callemu */ - code[3] = 0x48000002 | (unsigned long)&nbif_callemu; -#endif hipe_flush_icache_range(code, 4*sizeof(int)); @@ -334,60 +307,32 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) static void patch_b(Uint32 *address, Sint32 offset, Uint32 AA) { Uint32 oldI = *address; -#if defined(__arm__) Uint32 newI = (oldI & 0xFF000000) | (offset & 0x00FFFFFF); -#else - Uint32 newI = (oldI & 0xFC000001) | ((offset & 0x00FFFFFF) << 2) | (AA & 2); -#endif *address = newI; hipe_flush_icache_word(address); } int hipe_patch_call(void *callAddress, void *destAddress, void *trampoline) { -#if !defined(__arm__) - if ((Uint32)destAddress == ((Uint32)destAddress & 0x01FFFFFC)) { - /* The destination is in the [0,32MB[ range. - We can reach it with a ba/bla instruction. - This is the typical case for BIFs and primops. - It's also common for trap-to-BEAM stubs (on ppc32). */ - patch_b((Uint32*)callAddress, (Uint32)destAddress >> 2, 2); + Sint32 destOffset = ((Sint32)destAddress - ((Sint32)callAddress+8)) >> 2; + if (destOffset >= -0x800000 && destOffset <= 0x7FFFFF) { + /* The destination is within a [-32MB,+32MB[ range from us. + We can reach it with a b/bl instruction. + This is typical for nearby Erlang code. */ + patch_b((Uint32*)callAddress, destOffset, 0); } else { -#endif -#if defined(__arm__) - Sint32 destOffset = ((Sint32)destAddress - ((Sint32)callAddress+8)) >> 2; -#else - Sint32 destOffset = ((Sint32)destAddress - (Sint32)callAddress) >> 2; -#endif - if (destOffset >= -0x800000 && destOffset <= 0x7FFFFF) { - /* The destination is within a [-32MB,+32MB[ range from us. - We can reach it with a b/bl instruction. - This is typical for nearby Erlang code. */ - patch_b((Uint32*)callAddress, destOffset, 0); - } else { - /* The destination is too distant for b/bl/ba/bla. - Must do a b/bl to the trampoline. */ -#if defined(__arm__) - Sint32 trampOffset = ((Sint32)trampoline - ((Sint32)callAddress+8)) >> 2; -#else - Sint32 trampOffset = ((Sint32)trampoline - (Sint32)callAddress) >> 2; -#endif - if (trampOffset >= -0x800000 && trampOffset <= 0x7FFFFF) { - /* Update the trampoline's address computation. - (May be redundant, but we can't tell.) */ -#if defined(__arm__) - patch_imm32((Uint32*)trampoline+1, (Uint32)destAddress); -#else - patch_li((Uint32*)trampoline, (Uint32)destAddress); -#endif - /* Update this call site. */ - patch_b((Uint32*)callAddress, trampOffset, 0); - } else - return -1; - } -#if !defined(__arm__) + /* The destination is too distant for b/bl. + Must do a b/bl to the trampoline. */ + Sint32 trampOffset = ((Sint32)trampoline - ((Sint32)callAddress+8)) >> 2; + if (trampOffset >= -0x800000 && trampOffset <= 0x7FFFFF) { + /* Update the trampoline's address computation. + (May be redundant, but we can't tell.) */ + patch_imm32((Uint32*)trampoline+1, (Uint32)destAddress); + /* Update this call site. */ + patch_b((Uint32*)callAddress, trampOffset, 0); + } else + return -1; } -#endif return 0; } diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4 index e0c6f09796..17c013f1fb 100644 --- a/erts/emulator/hipe/hipe_arm_bifs.m4 +++ b/erts/emulator/hipe/hipe_arm_bifs.m4 @@ -27,7 +27,7 @@ include(`hipe/hipe_arm_asm.m4') .p2align 2 `#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) -# define CALL_BIF(F) mov r14, #F; str r14, [r0, #P_BIF_CALLEE]; bl hipe_debug_bif_wrapper +# define CALL_BIF(F) ldr r14, =F; str r14, [r0, #P_BIF_CALLEE]; bl hipe_debug_bif_wrapper #else # define CALL_BIF(F) bl F #endif' @@ -67,6 +67,7 @@ $1: RESTORE_CONTEXT_BIF beq nbif_1_simple_exception NBIF_RET(1) + .ltorg /* needed by LDR in debug version of `CALL_BIF' */ .size $1, .-$1 .type $1, %function #endif') @@ -95,6 +96,7 @@ $1: RESTORE_CONTEXT_BIF beq nbif_2_simple_exception NBIF_RET(2) + .ltorg .size $1, .-$1 .type $1, %function #endif') @@ -125,6 +127,7 @@ $1: RESTORE_CONTEXT_BIF beq nbif_3_simple_exception NBIF_RET(3) + .ltorg .size $1, .-$1 .type $1, %function #endif') @@ -149,6 +152,7 @@ $1: RESTORE_CONTEXT_BIF beq nbif_0_simple_exception NBIF_RET(0) + .ltorg .size $1, .-$1 .type $1, %function #endif') @@ -173,7 +177,8 @@ $1: /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_GC - bl $2 + /* ignore empty BIF__ARGS */ + CALL_BIF($2) TEST_GOT_MBUF(0) /* Restore registers. */ @@ -195,7 +200,9 @@ $1: /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_GC - bl $2 + str r1, [r0, #P_ARG0] /* Store BIF__ARGS in def_arg_reg[] */ + add r1, r0, #P_ARG0 + CALL_BIF($2) TEST_GOT_MBUF(1) /* Restore registers. Check for exception. */ @@ -220,7 +227,10 @@ $1: /* Save caller-save registers and call the C function. */ SAVE_CONTEXT_GC - bl $2 + str r1, [r0, #P_ARG0] /* Store BIF__ARGS in def_arg_reg[] */ + str r2, [r0, #P_ARG1] + add r1, r0, #P_ARG0 + CALL_BIF($2) TEST_GOT_MBUF(2) /* Restore registers. Check for exception. */ diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 26f183dc25..74868b868c 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -609,8 +609,8 @@ static Uint *hipe_find_emu_address(Eterm mod, Eterm name, unsigned int arity) Uint *code_base; int i, n; - modp = erts_get_module(mod); - if (modp == NULL || (code_base = modp->code) == NULL) + modp = erts_get_module(mod, erts_active_code_ix()); + if (modp == NULL || (code_base = modp->curr.code) == NULL) return NULL; n = code_base[MI_NUM_FUNCTIONS]; for (i = 0; i < n; ++i) { @@ -648,7 +648,7 @@ static void *hipe_get_emu_address(Eterm m, Eterm f, unsigned int arity, int is_r /* if not found, stub it via the export entry */ /* no lock needed around erts_export_get_or_make_stub() */ Export *export_entry = erts_export_get_or_make_stub(m, f, arity); - address = export_entry->address; + address = export_entry->addressv[erts_active_code_ix()]; } return address; } @@ -1093,10 +1093,8 @@ BIF_RETTYPE hipe_bifs_make_fun_3(BIF_ALIST_3) if (is_not_nil(free_vars)) BIF_ERROR(BIF_P, BADARG); -#ifndef HYBRID /* FIND ME! */ funp->next = MSO(BIF_P).funs; MSO(BIF_P).funs = funp; -#endif BIF_RET(make_fun(funp)); } @@ -1591,7 +1589,7 @@ BIF_RETTYPE hipe_nonclosure_address(BIF_ALIST_2) f = tp[2]; if (is_not_atom(m) || is_not_atom(f)) goto badfun; - if (!erts_find_export_entry(m, f, BIF_ARG_2)) + if (!erts_active_export_entry(m, f, BIF_ARG_2)) goto badfun; } else goto badfun; diff --git a/erts/emulator/hipe/hipe_bif1.c b/erts/emulator/hipe/hipe_bif1.c index 87cdfb8c7a..64de754e18 100644 --- a/erts/emulator/hipe/hipe_bif1.c +++ b/erts/emulator/hipe/hipe_bif1.c @@ -449,7 +449,7 @@ BIF_RETTYPE hipe_bifs_gc_info_0(BIF_ALIST_0) BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ -#if !(defined(BM_COUNTERS) && defined(HYBRID)) +#if !(defined(BM_COUNTERS)) Uint minor_global_gc = 0; Uint major_global_gc = 0; #endif @@ -459,17 +459,9 @@ BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0) #endif Eterm *hp; -#if defined(HYBRID) - Uint tmp_used_heap = (Uint)((BIF_P->htop - BIF_P->heap) + - (OLD_HTOP(BIF_P) - OLD_HEAP(BIF_P)) + - MBUF_SIZE(BIF_P)); - Uint tmp_allocated_heap = (Uint)((BIF_P->hend - BIF_P->heap) + - (OLD_HEND(BIF_P) - OLD_HEAP(BIF_P)) + - MBUF_SIZE(BIF_P)); -#else Uint tmp_used_heap = 0; Uint tmp_allocated_heap = 0; -#endif + hp = HAlloc(BIF_P, 7); BIF_RET(TUPLE6(hp, make_small((uint)minor_global_gc), @@ -486,7 +478,7 @@ BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0) BIF_RETTYPE hipe_bifs_incremental_gc_info_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ -#if !(defined(BM_COUNTERS) && defined(INCREMENTAL)) +#if !defined(BM_COUNTERS) Uint minor_gc_cycles = 0; Uint major_gc_cycles = 0; Uint minor_gc_stages = 0; @@ -512,17 +504,6 @@ BIF_RETTYPE hipe_bifs_gc_info_clear_0(BIF_ALIST_0) #ifdef BM_COUNTERS minor_gc = 0; major_gc = 0; -#ifdef HYBRID - minor_global_gc = 0; - major_global_gc = 0; - gc_in_copy = 0; -#ifdef INCREMENTAL - minor_gc_cycles = 0; - major_gc_cycles = 0; - minor_gc_stages = 0; - major_gc_stages = 0; -#endif -#endif #endif #ifdef BM_HEAP_SIZES diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index ee97541e15..ac0c2e2ffa 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -151,22 +151,6 @@ BIF_RETTYPE hipe_bifs_modeswitch_debug_off_0(BIF_ALIST_0) BIF_RET(am_true); } -/* BIFs for handling the message area */ - -BIF_RETTYPE hipe_bifs_show_message_area_0(BIF_ALIST_0) -{ -#ifdef HYBRID -#ifdef DEBUG - print_message_area(); -#else - printf("Only available in debug compiled emulator\r\n"); -#endif - BIF_RET(am_true); -#else - BIF_RET(am_false); -#endif -} - #if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) BIF_RETTYPE hipe_debug_bif_wrapper(BIF_ALIST_1); @@ -189,3 +173,10 @@ BIF_RETTYPE hipe_debug_bif_wrapper(BIF_ALIST_1) #endif /* ERTS_ENABLE_LOCK_CHECK && ERTS_SMP */ + +BIF_RETTYPE hipe_bifs_debug_native_called_2(BIF_ALIST_2) +{ + erts_printf("hipe_debug_native_called: %T(%T)\r\n", BIF_ARG_1, BIF_ARG_2); + BIF_RET(am_ok); +} + diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab index 51323ce7af..ddbd31f155 100644 --- a/erts/emulator/hipe/hipe_bif2.tab +++ b/erts/emulator/hipe/hipe_bif2.tab @@ -29,4 +29,4 @@ bif hipe_bifs:show_term/1 bif hipe_bifs:in_native/0 bif hipe_bifs:modeswitch_debug_on/0 bif hipe_bifs:modeswitch_debug_off/0 -bif hipe_bifs:show_message_area/0 +bif hipe_bifs:debug_native_called/2
\ No newline at end of file diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4 index 942fa0c5cb..d9aa09b79e 100644 --- a/erts/emulator/hipe/hipe_bif_list.m4 +++ b/erts/emulator/hipe/hipe_bif_list.m4 @@ -165,6 +165,7 @@ gc_bif_interface_2(nbif_put_2, put_2) gc_bif_interface_1(nbif_hipe_bifs_show_nstack_1, hipe_show_nstack_1) gc_bif_interface_1(nbif_hipe_bifs_show_pcb_1, hipe_bifs_show_pcb_1) gc_bif_interface_0(nbif_hipe_bifs_nstack_used_size_0, hipe_bifs_nstack_used_size_0) +gc_bif_interface_2(nbif_hipe_bifs_debug_native_called, hipe_bifs_debug_native_called_2) /* * Arithmetic operators called indirectly by the HiPE compiler. diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c index e0575c35ff..07e4b8a4d6 100644 --- a/erts/emulator/hipe/hipe_gc.c +++ b/erts/emulator/hipe/hipe_gc.c @@ -237,329 +237,3 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) } abort(); } - -#ifdef HYBRID - -#ifdef INCREMENTAL -Eterm *ma_fullsweep_nstack(Process *p, Eterm *n_htop, Eterm *n_hend) -{ - /* known nstack walk state */ - Eterm *nsp; - Eterm *nsp_end; - const struct sdesc *sdesc; - unsigned int sdesc_size; - unsigned long ra; - unsigned int i; - unsigned int mask; - /* arch-specific nstack walk state */ - struct nstack_walk_state walk_state; - - if (!nstack_walk_init_check(p)) - return n_htop; - - nsp = nstack_walk_nsp_begin(p); - nsp_end = nstack_walk_nsp_end(p); - - sdesc = nstack_walk_init_sdesc(p, &walk_state); - - for (;;) { - if (nstack_walk_nsp_reached_end(nsp, nsp_end)) { - if (nsp == nsp_end) - return n_htop; - fprintf(stderr, "%s: passed end of stack\r\n", __FUNCTION__); - break; - } - sdesc_size = nstack_walk_frame_size(sdesc); - i = 0; - mask = sdesc->livebits[0]; - for (;;) { - if (mask & 1) { - Eterm *nsp_i = nstack_walk_frame_index(nsp, i); - Eterm val = *nsp_i; - Eterm *obj_ptr = ptr_val(val); - switch (primary_tag(val)) { - case TAG_PRIMARY_LIST: - COPYMARK_CONS(obj_ptr, n_htop, nsp_i, n_hend); - break; - case TAG_PRIMARY_BOXED: - COPYMARK_BOXED(obj_ptr, n_htop, nsp_i, n_hend); - break; - default: - break; - } - } - if (++i >= sdesc_size) - break; - if (i & 31) - mask >>= 1; - else - mask = sdesc->livebits[i >> 5]; - } - ra = nstack_walk_frame_ra(nsp, sdesc); - if (ra == (unsigned long)nbif_stack_trap_ra) - ra = (unsigned long)p->hipe.ngra; - sdesc = hipe_find_sdesc(ra); - nsp = nstack_walk_next_frame(nsp, sdesc_size); - } - abort(); -} - -void ma_gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) -{ - /* known nstack walk state */ - Eterm *nsp; - Eterm *nsp_end; - const struct sdesc *sdesc; - unsigned int sdesc_size; - unsigned long ra; - unsigned int i; - unsigned int mask; - /* arch-specific nstack walk state */ - struct nstack_walk_state walk_state; - - /* ma_gensweep-specific state */ - Eterm *low_water, *high_water, *surface; - Eterm *n_htop; - Eterm *old_htop; - - if (!nstack_walk_init_check(p)) - return; - - nsp = nstack_walk_nsp_begin(p); - nsp_end = nstack_walk_nsp_end(p); - - low_water = global_heap; - //high_water = global_high_water; - surface = global_htop; - - old_htop = *ptr_old_htop; - n_htop = *ptr_n_htop; - - sdesc = nstack_walk_init_sdesc(p, &walk_state); - - for (;;) { - if (nstack_walk_nsp_reached_end(nsp, nsp_end)) { - if (nsp == nsp_end) { - *ptr_old_htop = old_htop; - *ptr_n_htop = n_htop; - return; - } - fprintf(stderr, "%s: passed end of stack\r\n", __FUNCTION__); - break; - } - sdesc_size = nstack_walk_frame_size(sdesc); - i = 0; - mask = sdesc->livebits[0]; - for (;;) { - if (mask & 1) { - Eterm *nsp_i = nstack_walk_frame_index(nsp, i); - Eterm gval = *nsp_i; - if (is_boxed(gval)) { - Eterm *ptr = boxed_val(gval); - Eterm val = *ptr; - if (MY_IS_MOVED(val)) { - *nsp_i = val; - } else if (ptr_within(ptr, low_water, high_water)) { - MOVE_BOXED(ptr, val, old_htop, nsp_i); - } else if (ptr_within(ptr, high_water, surface)) { - MOVE_BOXED(ptr, val, n_htop, nsp_i); - } - } else if (is_list(gval)) { - Eterm *ptr = list_val(gval); - Eterm val = *ptr; - if (is_non_value(val)) { - *nsp_i = ptr[1]; - } else if (ptr_within(ptr, low_water, high_water)) { - MOVE_CONS(ptr, val, old_htop, nsp_i); - } else if (ptr_within(ptr, high_water, surface)) { - MOVE_CONS(ptr, val, n_htop, nsp_i); - } - } - } - if (++i >= sdesc_size) - break; - if (i & 31) - mask >>= 1; - else - mask = sdesc->livebits[i >> 5]; - } - ra = nstack_walk_frame_ra(nsp, sdesc); - if (ra == (unsigned long)nbif_stack_trap_ra) - ra = (unsigned long)p->hipe.ngra; - sdesc = hipe_find_sdesc(ra); - nsp = nstack_walk_next_frame(nsp, sdesc_size); - } - abort(); -} - -#else /* not INCREMENTAL */ - -Eterm *ma_fullsweep_nstack(Process *p, Eterm *n_htop) -{ - /* known nstack walk state */ - Eterm *nsp; - Eterm *nsp_end; - const struct sdesc *sdesc; - unsigned int sdesc_size; - unsigned long ra; - unsigned int i; - unsigned int mask; - /* arch-specific nstack walk state */ - struct nstack_walk_state walk_state; - - /* ma_fullsweep-specific state */ - Eterm *gheap = global_heap; - Eterm *ghtop = global_htop; - Eterm *goheap = global_old_heap; - Eterm *gohtop = global_old_htop; - - if (!nstack_walk_init_check(p)) - return n_htop; - - nsp = nstack_walk_nsp_begin(p); - nsp_end = nstack_walk_nsp_end(p); - - sdesc = nstack_walk_init_sdesc(p, &walk_state); - - for (;;) { - if (nstack_walk_nsp_reached_end(nsp, nsp_end)) { - if (nsp == nsp_end) - return n_htop; - fprintf(stderr, "%s: passed end of stack\r\n", __FUNCTION__); - break; - } - sdesc_size = nstack_walk_frame_size(sdesc); - i = 0; - mask = sdesc->livebits[0]; - for (;;) { - if (mask & 1) { - Eterm *nsp_i = nstack_walk_frame_index(nsp, i); - Eterm gval = *nsp_i; - if (is_boxed(gval)) { - Eterm *ptr = boxed_val(gval); - Eterm val = *ptr; - if (MY_IS_MOVED(val)) { - *nsp_i = val; - } else if (ptr_within(ptr, gheap, ghtop)) { - MOVE_BOXED(ptr, val, n_htop, nsp_i); - } else if (ptr_within(ptr, goheap, gohtop)) { - MOVE_BOXED(ptr, val, n_htop, nsp_i); - } - } else if (is_list(gval)) { - Eterm *ptr = list_val(gval); - Eterm val = *ptr; - if (is_non_value(val)) { - *nsp_i = ptr[1]; - } else if (ptr_within(ptr, gheap, ghtop)) { - MOVE_CONS(ptr, val, n_htop, nsp_i); - } else if (ptr_within(ptr, gheap, ghtop)) { - MOVE_CONS(ptr, val, n_htop, nsp_i); - } - } - } - if (++i >= sdesc_size) - break; - if (i & 31) - mask >>= 1; - else - mask = sdesc->livebits[i >> 5]; - } - ra = nstack_walk_frame_ra(nsp, sdesc); - if (ra == (unsigned long)nbif_stack_trap_ra) - ra = (unsigned long)p->hipe.ngra; - sdesc = hipe_find_sdesc(ra); - nsp = nstack_walk_next_frame(nsp, sdesc_size); - } - abort(); -} - -void ma_gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop) -{ - /* known nstack walk state */ - Eterm *nsp; - Eterm *nsp_end; - const struct sdesc *sdesc; - unsigned int sdesc_size; - unsigned long ra; - unsigned int i; - unsigned int mask; - /* arch-specific nstack walk state */ - struct nstack_walk_state walk_state; - - /* ma_gensweep-specific state */ - Eterm *low_water, *high_water, *surface; - Eterm *n_htop; - Eterm *old_htop; - - if (!nstack_walk_init_check(p)) - return; - - nsp = nstack_walk_nsp_begin(p); - nsp_end = nstack_walk_nsp_end(p); - - low_water = global_heap; - high_water = global_high_water; - surface = global_htop; - - old_htop = *ptr_old_htop; - n_htop = *ptr_n_htop; - - sdesc = nstack_walk_init_sdesc(p, &walk_state); - - for (;;) { - if (nstack_walk_nsp_reached_end(nsp, nsp_end)) { - if (nsp == nsp_end) { - *ptr_old_htop = old_htop; - *ptr_n_htop = n_htop; - return; - } - fprintf(stderr, "%s: passed end of stack\r\n", __FUNCTION__); - break; - } - sdesc_size = nstack_walk_frame_size(sdesc); - i = 0; - mask = sdesc->livebits[0]; - for (;;) { - if (mask & 1) { - Eterm *nsp_i = nstack_walk_frame_index(nsp, i); - Eterm gval = *nsp_i; - if (is_boxed(gval)) { - Eterm *ptr = boxed_val(gval); - Eterm val = *ptr; - if (MY_IS_MOVED(val)) { - *nsp_i = val; - } else if (ptr_within(ptr, low_water, high_water)) { - MOVE_BOXED(ptr, val, old_htop, nsp_i); - } else if (ptr_within(ptr, high_water, surface)) { - MOVE_BOXED(ptr, val, n_htop, nsp_i); - } - } else if (is_list(gval)) { - Eterm *ptr = list_val(gval); - Eterm val = *ptr; - if (is_non_value(val)) { - *nsp_i = ptr[1]; - } else if (ptr_within(ptr, low_water, high_water)) { - MOVE_CONS(ptr, val, old_htop, nsp_i); - } else if (ptr_within(ptr, high_water, surface)) { - MOVE_CONS(ptr, val, n_htop, nsp_i); - } - } - } - if (++i >= sdesc_size) - break; - if (i & 31) - mask >>= 1; - else - mask = sdesc->livebits[i >> 5]; - } - ra = nstack_walk_frame_ra(nsp, sdesc); - if (ra == (unsigned long)nbif_stack_trap_ra) - ra = (unsigned long)p->hipe.ngra; - sdesc = hipe_find_sdesc(ra); - nsp = nstack_walk_next_frame(nsp, sdesc_size); - } - abort(); -} -#endif /* INCREMENTAL */ - -#endif /* HYBRID */ diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index d07d14028c..6e9041c84a 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -467,15 +467,11 @@ static const struct rts_param { int value; } rts_params[] = { { 1, "P_OFF_HEAP_FUNS", -#if !defined(HYBRID) 1, offsetof(struct process, off_heap.first) -#endif }, { 4, "EFT_NEXT", -#if !defined(HYBRID) 1, offsetof(struct erl_fun_thing, next) -#endif }, /* These are always defined, but their values depend on the diff --git a/erts/emulator/hipe/hipe_native_bif.h b/erts/emulator/hipe/hipe_native_bif.h index 9e3a156fbc..3f460a5a5c 100644 --- a/erts/emulator/hipe/hipe_native_bif.h +++ b/erts/emulator/hipe/hipe_native_bif.h @@ -110,6 +110,9 @@ int hipe_bs_put_big_integer(Eterm, Uint, byte*, unsigned, unsigned); AEXTERN(Eterm,nbif_check_get_msg,(Process*)); Eterm hipe_check_get_msg(Process*); +AEXTERN(BIF_RETTYPE,nbif_hipe_bifs_debug_native_called,(Process*,Eterm,Eterm)); +BIF_RETTYPE hipe_bifs_debug_native_called_2(BIF_ALIST_2); + /* * SMP-specific stuff */ diff --git a/erts/emulator/hipe/hipe_primops.h b/erts/emulator/hipe/hipe_primops.h index 38509c105b..52b4681cfe 100644 --- a/erts/emulator/hipe/hipe_primops.h +++ b/erts/emulator/hipe/hipe_primops.h @@ -80,6 +80,7 @@ PRIMOP_LIST(am_fclearerror_error, &nbif_fclearerror_error) #ifdef NO_FPE_SIGNALS PRIMOP_LIST(am_emulate_fpe, &nbif_emulate_fpe) #endif +PRIMOP_LIST(am_debug_native_called, &nbif_hipe_bifs_debug_native_called) #if defined(__sparc__) #include "hipe_sparc_primops.h" diff --git a/erts/emulator/hipe/hipe_stack.c b/erts/emulator/hipe/hipe_stack.c index da462a64e1..53c316ba52 100644 --- a/erts/emulator/hipe/hipe_stack.c +++ b/erts/emulator/hipe/hipe_stack.c @@ -130,7 +130,7 @@ struct sdesc *hipe_decode_sdesc(Eterm arg) struct sdesc *sdesc; if (is_not_tuple(arg) || - (tuple_val(arg))[0] != make_arityval(5) || + (tuple_val(arg))[0] != make_arityval(6) || term_to_Uint((tuple_val(arg))[1], &ra) == 0 || term_to_Uint((tuple_val(arg))[2], &exnra) == 0 || is_not_small((tuple_val(arg))[3]) || @@ -183,5 +183,13 @@ struct sdesc *hipe_decode_sdesc(Eterm arg) off = unsigned_val(live[i]); sdesc->livebits[off / 32] |= (1 << (off & 31)); } +#ifdef DEBUG + { + Eterm mfa_tpl = tuple_val(arg)[6]; + sdesc->dbg_M = tuple_val(mfa_tpl)[1]; + sdesc->dbg_F = tuple_val(mfa_tpl)[2]; + sdesc->dbg_A = tuple_val(mfa_tpl)[3]; + } +#endif return sdesc; } diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h index 4c14b4a519..da706ae25e 100644 --- a/erts/emulator/hipe/hipe_stack.h +++ b/erts/emulator/hipe/hipe_stack.h @@ -35,6 +35,10 @@ struct sdesc { struct sdesc *next; /* hash collision chain */ } bucket; unsigned int summary; /* frame size, exn handler presence flag, arity */ +#ifdef DEBUG + Eterm dbg_M, dbg_F; + unsigned dbg_A; +#endif unsigned int livebits[1]; /* size depends on arch & data in summary field */ }; @@ -116,13 +120,4 @@ extern int hipe_fill_stacktrace(Process*, int, Eterm**); extern Eterm *fullsweep_nstack(Process *p, Eterm *n_htop); extern void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop); -#ifdef HYBRID -#ifdef INCREMENTAL -extern Eterm *ma_fullsweep_nstack(Process *p, Eterm *n_htop, Eterm *n_hend); -#else -extern Eterm *ma_fullsweep_nstack(Process *p, Eterm *n_htop); -#endif -extern void ma_gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop); -#endif /* HYBRID */ - #endif /* HIPE_STACK_H */ diff --git a/erts/emulator/hipe/hipe_x86_gc.h b/erts/emulator/hipe/hipe_x86_gc.h index e4607ad27d..aa4abb6f59 100644 --- a/erts/emulator/hipe/hipe_x86_gc.h +++ b/erts/emulator/hipe/hipe_x86_gc.h @@ -69,6 +69,11 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state) nstkarity = 0; state->sdesc0[0].summary = (0 << 9) | (0 << 8) | nstkarity; state->sdesc0[0].livebits[0] = 0; +# ifdef DEBUG + state->sdesc0[0].dbg_M = 0; + state->sdesc0[0].dbg_F = am_init; + state->sdesc0[0].dbg_A = 0; +# endif /* XXX: this appears to prevent a gcc-4.1.1 bug on x86 */ __asm__ __volatile__("" : : "m"(*state) : "memory"); return &state->sdesc0[0]; diff --git a/erts/emulator/hipe/hipe_x86_glue.h b/erts/emulator/hipe/hipe_x86_glue.h index b0db93267c..63ad250d60 100644 --- a/erts/emulator/hipe/hipe_x86_glue.h +++ b/erts/emulator/hipe/hipe_x86_glue.h @@ -62,6 +62,9 @@ static __inline__ void hipe_arch_glue_init(void) .sdesc = { .bucket = { .hvalue = (unsigned long)nbif_return }, .summary = (1<<8), + #ifdef DEBUG + .dbg_F = am_return, + #endif }, }; hipe_init_sdesc_table(&nbif_return_sdesc.sdesc); diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index c1336c60d9..ce014c19c2 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -39,7 +39,6 @@ #include "dtrace-wrapper.h" #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS -# define ERTS_DRV_EV_STATE_EXTRA_SIZE 128 #else # include "safe_hash.h" # define DRV_EV_STATE_HTAB_SIZE 1024 @@ -334,7 +333,9 @@ static void grow_drv_ev_state(int min_ix) { int i; - int new_len = min_ix + 1 + ERTS_DRV_EV_STATE_EXTRA_SIZE; + int new_len; + + new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_ix + 1); if (new_len > max_fds) new_len = max_fds; diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 26858052c4..b0eaf14d90 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -55,17 +55,12 @@ # ifdef SYS_SELECT_H # include <sys/select.h> # endif -# ifdef VXWORKS -# include <selectLib.h> -# endif #endif -#ifndef VXWORKS -# ifdef NO_SYSCONF -# if ERTS_POLL_USE_SELECT -# include <sys/param.h> -# else -# include <limits.h> -# endif +#ifdef NO_SYSCONF +# if ERTS_POLL_USE_SELECT +# include <sys/param.h> +# else +# include <limits.h> # endif #endif #include "erl_thr_progress.h" @@ -105,8 +100,8 @@ #define ERTS_POLL_COALESCE_KP_RES (ERTS_POLL_USE_KQUEUE || ERTS_POLL_USE_EPOLL) -#define FDS_STATUS_EXTRA_FREE_SIZE 128 -#define POLL_FDS_EXTRA_FREE_SIZE 128 +#define ERTS_EV_TABLE_MIN_LENGTH 1024 +#define ERTS_EV_TABLE_EXP_THRESHOLD (2048*1024) #ifdef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT # define ERTS_POLL_ASYNC_INTERRUPT_SUPPORT 1 @@ -563,6 +558,28 @@ free_update_requests_block(ErtsPollSet ps, * --- Growing poll set structures ------------------------------------------- */ +int +ERTS_POLL_EXPORT(erts_poll_get_table_len) (int new_len) +{ + if (new_len < ERTS_EV_TABLE_MIN_LENGTH) { + new_len = ERTS_EV_TABLE_MIN_LENGTH; + } else if (new_len < ERTS_EV_TABLE_EXP_THRESHOLD) { + /* find next power of 2 */ + --new_len; + new_len |= new_len >> 1; + new_len |= new_len >> 2; + new_len |= new_len >> 4; + new_len |= new_len >> 8; + new_len |= new_len >> 16; + ++new_len; + } else { + /* grow incrementally */ + new_len += ERTS_EV_TABLE_EXP_THRESHOLD; + } + return new_len; +} + + #if ERTS_POLL_USE_KERNEL_POLL static void grow_res_events(ErtsPollSet ps, int new_len) @@ -575,7 +592,7 @@ grow_res_events(ErtsPollSet ps, int new_len) #elif ERTS_POLL_USE_KQUEUE struct kevent #endif - )*new_len; + ) * ERTS_POLL_EXPORT(erts_poll_get_table_len)(new_len); /* We do not need to save previously stored data */ if (ps->res_events) erts_free(ERTS_ALC_T_POLL_RES_EVS, ps->res_events); @@ -589,7 +606,7 @@ static void grow_poll_fds(ErtsPollSet ps, int min_ix) { int i; - int new_len = min_ix + 1 + POLL_FDS_EXTRA_FREE_SIZE; + int new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_ix + 1); if (new_len > max_fds) new_len = max_fds; ps->poll_fds = (ps->poll_fds_len @@ -611,7 +628,7 @@ static void grow_fds_status(ErtsPollSet ps, int min_fd) { int i; - int new_len = min_fd + 1 + FDS_STATUS_EXTRA_FREE_SIZE; + int new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_fd + 1); ASSERT(min_fd < max_fds); if (new_len > max_fds) new_len = max_fds; @@ -2200,10 +2217,6 @@ ERTS_POLL_EXPORT(erts_poll_max_fds)(void) * --- Initialization -------------------------------------------------------- */ -#ifdef VXWORKS -extern int erts_vxworks_max_files; -#endif - void ERTS_POLL_EXPORT(erts_poll_init)(void) { @@ -2212,9 +2225,7 @@ ERTS_POLL_EXPORT(erts_poll_init)(void) errno = 0; -#if defined(VXWORKS) - max_fds = erts_vxworks_max_files; -#elif !defined(NO_SYSCONF) +#if !defined(NO_SYSCONF) max_fds = sysconf(_SC_OPEN_MAX); #elif ERTS_POLL_USE_SELECT max_fds = NOFILE; diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index 8dde619105..502290e4bb 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -246,4 +246,6 @@ void ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet, ErtsPollEvents [], int); +int ERTS_POLL_EXPORT(erts_poll_get_table_len)(int); + #endif /* #ifndef ERL_POLL_H__ */ diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index f94e0f2296..bf69f3bf90 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -1163,6 +1163,8 @@ static int set_driver_data(int port_num, report_exit_list = report_exit; } + erts_port[port_num].os_pid = pid; + if (read_write & DO_READ) { driver_data[ifd].packet_bytes = packet_bytes; driver_data[ifd].port_num = port_num; diff --git a/erts/emulator/sys/vxworks/driver_int.h b/erts/emulator/sys/vxworks/driver_int.h deleted file mode 100644 index f6bc71a799..0000000000 --- a/erts/emulator/sys/vxworks/driver_int.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. - * - * The contents of this file are subject to the Erlang Public License, - * Version 1.1, (the "License"); you may not use this file except in - * compliance with the License. You should have received a copy of the - * Erlang Public License along with this software. If not, it can be - * retrieved online at http://www.erlang.org/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and limitations - * under the License. - * - * %CopyrightEnd% - */ -/*---------------------------------------------------------------------- -** Purpose : System dependant driver declarations -**---------------------------------------------------------------------- */ - -#ifndef __DRIVER_INT_H__ -#define __DRIVER_INT_H__ - -#include <ioLib.h> - -typedef struct iovec SysIOVec; - -#endif diff --git a/erts/emulator/sys/vxworks/erl_main.c b/erts/emulator/sys/vxworks/erl_main.c deleted file mode 100644 index c9b44a635a..0000000000 --- a/erts/emulator/sys/vxworks/erl_main.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. - * - * The contents of this file are subject to the Erlang Public License, - * Version 1.1, (the "License"); you may not use this file except in - * compliance with the License. You should have received a copy of the - * Erlang Public License along with this software. If not, it can be - * retrieved online at http://www.erlang.org/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and limitations - * under the License. - * - * %CopyrightEnd% - */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include "sys.h" -#include "erl_vm.h" - -#if defined(__GNUC__) -/* - * The generated assembler does the usual trick (relative - * branch-and-link to next instruction) to get a copy of the - * instruction ptr. Instead of branching to an explicit zero offset, - * it branches to the symbol `__eabi' --- which is expected to be - * undefined and thus zero (if it is defined as non-zero, things will - * be interesting --- as in the Chinese curse). To shut up the VxWorks - * linker, we define `__eabi' as zero. - * - * This is just a work around. It's really Wind River's GCC's code - * generator that should be fixed. - */ -__asm__(".equ __eabi, 0"); -#endif - -void -erl_main(int argc, char **argv) -{ - erl_start(argc, argv); -} diff --git a/erts/emulator/sys/vxworks/erl_vxworks_sys.h b/erts/emulator/sys/vxworks/erl_vxworks_sys.h deleted file mode 100644 index 3d53238ea6..0000000000 --- a/erts/emulator/sys/vxworks/erl_vxworks_sys.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1997-2011. All Rights Reserved. - * - * The contents of this file are subject to the Erlang Public License, - * Version 1.1, (the "License"); you may not use this file except in - * compliance with the License. You should have received a copy of the - * Erlang Public License along with this software. If not, it can be - * retrieved online at http://www.erlang.org/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and limitations - * under the License. - * - * %CopyrightEnd% - */ -#ifndef __ERL_VXWORKS_SYS_H__ -#define __ERL_VXWORKS_SYS_H__ - -/* stdarg.h don't work without this one... */ -#include <vxWorks.h> - -#include <stdio.h> -#include <math.h> -#include <limits.h> -#include <stdlib.h> -#define index StringIndexFunctionThatIDontWantDeclared -#include <string.h> -#undef index - - - -#include <sys/times.h> -#include <time.h>/* xxxP */ - -#include <dirent.h> -#include <sys/stat.h> - -/* xxxP from unix_sys.h begin */ - -/* - * Make sure that MAXPATHLEN is defined. - */ - -#ifndef MAXPATHLEN -# ifdef PATH_MAX -# define MAXPATHLEN PATH_MAX -# else -# define MAXPATHLEN 2048 -# endif -#endif - -/* xxxP end */ - - -/* Unimplemented math functions */ -#define NO_ASINH -#define NO_ACOSH -#define NO_ATANH -#define NO_ERF -#define NO_ERFC - -/* Stuff that is useful for port programs, drivers, etc */ -#ifndef VXWORKS -#define VXWORKS -#endif - -#define DONT_USE_MAIN -#define NO_FSYNC -#define NO_MKDIR_MODE -#define NO_UMASK -#define NO_SYMBOLIC_LINKS -#define NO_DEVICE_FILES -#define NO_UID -#define NO_ACCESS -#define NO_FCNTL -#define NO_SYSLOG -#define NO_SYSCONF -#define NO_PWD /* XXX Means what? */ -#define NO_DAEMON -/* This chooses ~250 reductions instead of 500 in config.h */ -#if (CPU == CPU32) -#define SLOW_PROCESSOR -#endif - -/* - * Even though we does not always have small memories on VxWorks - * we certainly does not have virtual memory. - */ -#if !defined(LARGE_MEMORY) -#define SMALL_MEMORY -#endif - -/*************** Floating point exception handling ***************/ - -/* There are no known ways to customize the handling of invalid floating - point operations, such as matherr() or ieee_handler(), in VxWorks 5.1. */ - -#if (CPU == MC68040 || CPU == CPU32 || CPU == PPC860 || CPU == PPC32 || \ - CPU == PPC603 || CPU == PPC604 || CPU == SIMSPARCSOLARIS) - -/* VxWorks 5.1 on Motorola 68040 never generates SIGFPE, but sets the - result of invalid floating point ops to Inf and NaN - unfortunately - the way to test for those values is undocumented and hidden in a - "private" include file... */ -/* Haven't found any better way, as of yet, for ppc860 xxxP*/ - -#include <private/mathP.h> -#define NO_FPE_SIGNALS -#define erts_get_current_fp_exception() NULL -#define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0) -#define __ERTS_FP_ERROR(fpexnp, f, Action) if (isInf(f) || isNan(f)) { Action; } else {} -#define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) __ERTS_FP_ERROR(fpexnp, f, Action) -#define __ERTS_SAVE_FP_EXCEPTION(fpexnp) -#define __ERTS_RESTORE_FP_EXCEPTION(fpexnp) - -#define ERTS_FP_CHECK_INIT(p) __ERTS_FP_CHECK_INIT(&(p)->fp_exception) -#define ERTS_FP_ERROR(p, f, A) __ERTS_FP_ERROR(&(p)->fp_exception, f, A) -#define ERTS_SAVE_FP_EXCEPTION(p) __ERTS_SAVE_FP_EXCEPTION(&(p)->fp_exception) -#define ERTS_RESTORE_FP_EXCEPTION(p) __ERTS_RESTORE_FP_EXCEPTION(&(p)->fp_exception) -#define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A) - -#define erts_sys_block_fpe() 0 -#define erts_sys_unblock_fpe(x) do{}while(0) - -#if (CPU == PPC603) -/* Need fppLib to change the Floating point registers - (fix_registers in sys.c)*/ - -#include <fppLib.h> - -#endif /* PPC603 */ - -#else - -Unsupported CPU value ! - -#endif - -typedef void *GETENV_STATE; - -#define HAVE_GETHRTIME - -extern int erts_clock_rate; - -#define SYS_CLK_TCK (erts_clock_rate) - -#define SYS_CLOCK_RESOLUTION 1 - -typedef struct _vxworks_tms { - clock_t tms_utime; - clock_t tms_stime; - clock_t tms_cutime; - clock_t tms_cstime; -} SysTimes; - -typedef long long SysHrTime; - -typedef time_t erts_time_t; -typedef struct timeval SysTimeval; - -extern int sys_init_hrtime(void); -extern SysHrTime sys_gethrtime(void); -extern void sys_gettimeofday(SysTimeval *tvp); -extern clock_t sys_times(SysTimes *t); - -#define SIZEOF_SHORT 2 -#define SIZEOF_INT 4 -#define SIZEOF_LONG 4 -#define SIZEOF_VOID_P 4 -#define SIZEOF_SIZE_T 4 -#define SIZEOF_OFF_T 4 - -/* - * Temporary buffer *only* used in sys code. - */ -#define SYS_TMP_BUF_SIZE 65536 - -/* Need to be able to interrupt erts_poll_wait() from signal handler */ -#define ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT - -#endif /* __ERL_VXWORKS_SYS_H__ */ diff --git a/erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c b/erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c deleted file mode 100644 index c56c633b2f..0000000000 --- a/erts/emulator/sys/vxworks/erl_vxworks_sys_ddll.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2006-2009. All Rights Reserved. - * - * The contents of this file are subject to the Erlang Public License, - * Version 1.1, (the "License"); you may not use this file except in - * compliance with the License. You should have received a copy of the - * Erlang Public License along with this software. If not, it can be - * retrieved online at http://www.erlang.org/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and limitations - * under the License. - * - * %CopyrightEnd% - */ - -/* - * Interface functions to the dynamic linker using dl* functions. - * (As far as I know it works on SunOS 4, 5, Linux and FreeBSD. /Seb) - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include <vxWorks.h> -#include <stdio.h> -#include <string.h> -#include <stdarg.h> -#include <a_out.h> -#include <symLib.h> -#include <loadLib.h> -#include <unldLib.h> -#include <moduleLib.h> -#include <sysSymTbl.h> -#include "sys.h" -#include "global.h" -#include "erl_alloc.h" -#include "erl_driver.h" - -#define EXT_LEN 4 -#define FILE_EXT ".eld" -#define ALT_FILE_EXT ".o" -/* ALT_FILE_EXT must not be longer than FILE_EXT */ -#define DRIVER_INIT_SUFFIX "_init" - -static MODULE_ID get_mid(char *); -static FUNCPTR lookup(char *); - -typedef enum { - NoError, - ModuleNotFound, - ModuleNotUnloadable, - UnknownError -} FakeSytemError; - -static char *errcode_tab[] = { - "No error", - "Module/file not found", - "Module cannot be unloaded", - "Unknown error" -}; - -void erl_sys_ddll_init(void) { - return; -} -/* - * Open a shared object - */ -int erts_sys_ddll_open2(char *full_name, void **handle, ErtsSysDdllError* err) -{ - int len; - - if (erts_sys_ddll_open_noext(full_name, handle, err) == ERL_DE_NO_ERROR) { - return ERL_DE_NO_ERROR; - } - if ((len = sys_strlen(full_name)) > PATH_MAX-EXT_LEN) { - return ERL_DE_LOAD_ERROR_NAME_TO_LONG; - } else { - static char dlname[PATH_MAX + 1]; - - sys_strcpy(dlname, full_name); - sys_strcpy(dlname+len, FILE_EXT); - if (erts_sys_ddll_open_noext(dlname, handle, err) == ERL_DE_NO_ERROR) { - return ERL_DE_NO_ERROR; - } - sys_strcpy(dlname+len, ALT_FILE_EXT); - return erts_sys_ddll_open_noext(dlname, handle, err); - } -} -int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err) -{ - MODULE_ID mid; - - if((mid = get_mid(dlname)) == NULL) { - return ERL_DE_DYNAMIC_ERROR_OFFSET - ((int) ModuleNotFound); - } - *handle = (void *) mid; - return ERL_DE_NO_ERROR; -} - -/* - * Find a symbol in the shared object - */ -#define PREALLOC_BUFFER_SIZE 256 -int erts_sys_ddll_sym2(void *handle, char *func_name, void **function, ErtsSysDdllError* err) -{ - FUNCPTR proc; - static char statbuf[PREALLOC_BUFFER_SIZE]; - char *buf = statbuf; - int need; - - if ((proc = lookup(func_name)) == NULL) { - if ((need = strlen(func_name)+2) > PREALLOC_BUFFER_SIZE) { - buf = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF,need); - } - buf[0] = '_'; - sys_strcpy(buf+1,func_name); - proc = lookup(buf); - if (buf != statbuf) { - erts_free(ERTS_ALC_T_DDLL_TMP_BUF, buf); - } - if (proc == NULL) { - return ERL_DE_LOOKUP_ERROR_NOT_FOUND; - } - } - *function = (void *) proc; - return ERL_DE_NO_ERROR; -} - -/* XXX:PaN These two will be changed with new driver interface! */ - -/* - * Load the driver init function, might appear under different names depending on object arch... - */ - -int erts_sys_ddll_load_driver_init(void *handle, void **function) -{ - MODULE_ID mid = (MODULE_ID) handle; - char *modname; - char *cp; - static char statbuf[PREALLOC_BUFFER_SIZE]; - char *fname = statbuf; - int len; - int res; - void *func; - int need; - - if((modname = moduleNameGet(mid)) == NULL) { - return ERL_DE_DYNAMIC_ERROR_OFFSET - ((int) ModuleNotFound); - } - - if((cp = strrchr(modname, '.')) == NULL) { - len = strlen(modname); - } else { - len = cp - modname; - } - - need = len + strlen(DRIVER_INIT_SUFFIX) + 1; - if (need > PREALLOC_BUFFER_SIZE) { - fname = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, need); /* erts_alloc exits on failure */ - } - sys_strncpy(fname, modname, len); - fname[len] = '\0'; - sys_strcat(fname, DRIVER_INIT_SUFFIX); - res = erts_sys_ddll_sym(handle, fname, &func); - if (fname != statbuf) { - erts_free(ERTS_ALC_T_DDLL_TMP_BUF, fname); - } - if ( res != ERL_DE_NO_ERROR) { - return res; - } - *function = func; - return ERL_DE_NO_ERROR; -} - -int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err) -{ - /* NIFs not implemented for vxworks */ - return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY; -} - -/* - * Call the driver_init function, whatever it's really called, simple on unix... -*/ -void *erts_sys_ddll_call_init(void *function) { - void *(*initfn)(void) = function; - return (*initfn)(); -} -void *erts_sys_ddll_call_nif_init(void *function) { - return erts_sys_ddll_call_init(function); -} - - -/* - * Close a chared object - */ -int erts_sys_ddll_close2(void *handle, ErtsSysDdllError* err) -{ - MODULE_ID mid = (MODULE_ID) handle; - if (unld(mid, 0) < 0) { - return ERL_DE_DYNAMIC_ERROR_OFFSET - ((int) ModuleNotUnloadable); - } - return ERL_DE_NO_ERROR; -} - -/* - * Return string that describes the (current) error - */ -char *erts_sys_ddll_error(int code) -{ - int actual_code; - if (code > ERL_DE_DYNAMIC_ERROR_OFFSET) { - return "Unspecified error"; - } - actual_code = -1*(code - ERL_DE_DYNAMIC_ERROR_OFFSET); - if (actual_code > ((int) UnknownError)) { - actual_code = UnknownError; - } - return errcode_tab[actual_code]; -} - -static FUNCPTR lookup(char *sym) -{ - FUNCPTR entry; - SYM_TYPE type; - - if (symFindByNameAndType(sysSymTbl, sym, (char **)&entry, - &type, N_EXT | N_TEXT, N_EXT | N_TEXT) != OK) { - return NULL ; - } - return entry; -} - -static MODULE_ID get_mid(char* name) -{ - int fd; - MODULE_ID mid = NULL; - - if((fd = open(name, O_RDONLY, 0664)) >= 0) { - mid = loadModule(fd, GLOBAL_SYMBOLS); - close(fd); - } - return mid; -} - -void erts_sys_ddll_free_error(ErtsSysDdllError* err) -{ - /* NYI */ -} - diff --git a/erts/emulator/sys/vxworks/sys.c b/erts/emulator/sys/vxworks/sys.c deleted file mode 100644 index 739b026fb1..0000000000 --- a/erts/emulator/sys/vxworks/sys.c +++ /dev/null @@ -1,2610 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1997-2011. All Rights Reserved. - * - * The contents of this file are subject to the Erlang Public License, - * Version 1.1, (the "License"); you may not use this file except in - * compliance with the License. You should have received a copy of the - * Erlang Public License along with this software. If not, it can be - * retrieved online at http://www.erlang.org/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and limitations - * under the License. - * - * %CopyrightEnd% - */ -/* - * system-dependent functions - * - */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include <vxWorks.h> -#include <version.h> -#include <string.h> -#include <types.h> -#include <sigLib.h> -#include <ioLib.h> -#include <iosLib.h> -#include <envLib.h> -#include <fioLib.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <symLib.h> -#include <sysLib.h> -#include <sysSymTbl.h> -#include <loadLib.h> -#include <taskLib.h> -#include <taskVarLib.h> -#include <taskHookLib.h> -#include <tickLib.h> -#include <time.h> -#include <rngLib.h> -#include <semLib.h> -#include <selectLib.h> -#include <sockLib.h> -#include <a_out.h> -#include <wdLib.h> -#include <timers.h> -#include <ctype.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <stdarg.h> - - -#ifndef WANT_NONBLOCKING -#define WANT_NONBLOCKING -#endif - -#include "sys.h" -#include "erl_alloc.h" - -/* don't need global.h, but bif_table.h (included by bif.h) won't compile otherwise */ -#include "global.h" -#include "bif.h" - -#include "erl_sys_driver.h" - -#include "elib_stat.h" - -#include "reclaim_private.h" /* Some more or less private reclaim facilities */ - -#ifndef RETSIGTYPE -#define RETSIGTYPE void -#endif - -EXTERN_FUNCTION(void, erl_start, (int, char**)); -EXTERN_FUNCTION(void, erl_exit, (int n, char*, _DOTS_)); -EXTERN_FUNCTION(void, erl_error, (char*, va_list)); -EXTERN_FUNCTION(int, driver_interrupt, (int, int)); -EXTERN_FUNCTION(void, increment_time, (int)); -EXTERN_FUNCTION(int, erts_next_time, (_VOID_)); -EXTERN_FUNCTION(void, set_reclaim_free_function, (FreeFunction)); -EXTERN_FUNCTION(int, erl_mem_info_get, (MEM_PART_STATS *)); -EXTERN_FUNCTION(void, erl_crash_dump, (char* file, int line, char* fmt, ...)); - -#define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG) - -/* these are defined in usrLib.c */ -extern int spTaskPriority, spTaskOptions; - -/* forward declarations */ -static FUNCTION(FUNCPTR, lookup, (char*)); -static FUNCTION(int, read_fill, (int, char*, int)); -#if (CPU == SPARC) -static FUNCTION(RETSIGTYPE, fpe_sig_handler, (int)); /*where is this fun? */ -#elif (CPU == PPC603) -static FUNCTION(void, fix_registers, (void)); -#endif -static FUNCTION(void, close_pipes, (int*, int*, int)); -static FUNCTION(void, delete_hook, (void)); -static FUNCTION(void, initialize_allocation, (void)); - -FUNCTION(STATUS, uxPipeDrv, (void)); -FUNCTION(STATUS, pipe, (int*)); -FUNCTION(void, uxPipeShow, (int)); - -void erl_main(int argc, char **argv); -void argcall(char *args); - -/* Malloc-realted functions called from the VxWorks shell */ -EXTERN_FUNCTION(int, erl_set_memory_block, - (int, int, int, int, int, int, int, int, int, int)); -EXTERN_FUNCTION(int, erl_memory_show, - (int, int, int, int, int, int, int, int, int, int)); - -#define DEFAULT_PORT_STACK_SIZE 100000 -static int port_stack_size; - -static int erlang_id = 0; /* Inited at loading, set/reset at each run */ - -/* interval time reported to emulator */ -static int sys_itime; - -/* XXX - This is defined in .../config/all/configAll.h (NUM_FILES), - and not easily accessible at compile or run time - however, - in VxWorks 5.1 it is stored in the (undocumented?) maxFiles variable; - probably shouldn't depend on it, but we try to pick it up... */ -static int max_files = 50; /* default configAll.h */ - -int erts_vxworks_max_files; - -/* - * used by the break handler (set by signal handler on ctl-c) - */ -volatile int erts_break_requested; - -/********************* General functions ****************************/ - -/* - * Reset the terminal to the original settings on exit - * (nothing to do for WxWorks). - */ -void sys_tty_reset(int exit_code) -{ -} - -Uint -erts_sys_misc_mem_sz(void) -{ - Uint res = erts_check_io_size(); - /* res += FIXME */ - return res; -} - -/* - * XXX This declaration should not be here. - */ -void erl_sys_schedule_loop(void); - -#ifdef SOFTDEBUG -static void do_trace(int line, char *file, char *format, ...) -{ - va_list va; - int tid = taskIdSelf(); - char buff[512]; - - va_start(va, format); - sprintf(buff,"Trace: Task: 0x%08x, %s:%d - ", - tid, file, line); - vsprintf(buff + strlen(buff), format, va); - va_end(va); - strcat(buff,"\r\n"); - write(2,buff,strlen(buff)); -} - -#define TRACE() do_trace(__LINE__, __FILE__,"") -#define TRACEF(Args...) do_trace(__LINE__,__FILE__, ## Args) -#endif - -void -erts_sys_pre_init(void) -{ - if (erlang_id != 0) { - /* NOTE: This particular case must *not* call erl_exit() */ - erts_fprintf(stderr, "Sorry, erlang is already running (as task %d)\n", - erlang_id); - exit(1); - } - - /* This must be done as early as possible... */ - if(!reclaim_init()) - fprintf(stderr, "Warning : reclaim facility should be initiated before " - "erlang is started!\n"); - erts_vxworks_max_files = max_files = reclaim_max_files(); - - /* Floating point exceptions */ -#if (CPU == SPARC) - sys_sigset(SIGFPE, fpe_sig_handler); -#elif (CPU == PPC603) - fix_registers(); -#endif - - /* register the private delete hook in reclaim */ - save_delete_hook((FUNCPTR)delete_hook, (caddr_t)0); - erlang_id = taskIdSelf(); -#ifdef DEBUG - printf("emulator task id = 0x%x\n", erlang_id); -#endif -} - -void erts_sys_alloc_init(void) -{ - initialize_allocation(); -} - -void -erl_sys_init(void) -{ - setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ); - /* XXX Bug in VxWorks stdio loses fputch()'ed output after the - setvbuf() but before a *printf(), and possibly worse (malloc - errors, crash?) - so let's give it a *printf().... */ - fprintf(stdout, "%s",""); -} - -void -erl_sys_args(int* argc, char** argv) -{ - erts_init_check_io(); - max_files = erts_check_io_max_files(); - ASSERT(max_files <= erts_vxworks_max_files); -} - -void -erts_sys_schedule_interrupt(int set) -{ - erts_check_io_interrupt(set); -} - -/* - * Called from schedule() when it runs out of runnable processes, - * or when Erlang code has performed INPUT_REDUCTIONS reduction - * steps. runnable == 0 iff there are no runnable Erlang processes. - */ -void -erl_sys_schedule(int runnable) -{ - erts_check_io(!runnable); -} - -void erts_do_break_handling(void) -{ - SET_BLOCKING(0); - /* call the break handling function, reset the flag */ - do_break(); - erts_break_requested = 0; - SET_NONBLOCKING(0); -} - -/* signal handling */ -RETSIGTYPE (*sys_sigset(sig, func))() - int sig; - RETSIGTYPE (*func)(); -{ - struct sigaction act, oact; - - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - act.sa_handler = func; - sigaction(sig, &act, &oact); - return(oact.sa_handler); -} - -void sys_sigblock(int sig) -{ - sigset_t mask; - - sigemptyset(&mask); - sigaddset(&mask, sig); - sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL); -} - -void sys_sigrelease(int sig) -{ - sigset_t mask; - - sigemptyset(&mask); - sigaddset(&mask, sig); - sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL); -} - -void -erts_sys_prepare_crash_dump(void) -{ - -} - -/* register signal handlers XXX - they don't work, need to find out why... */ -/* set up signal handlers for break and quit */ -static void request_break(void) -{ - /* just set a flag - checked for and handled - * in main thread (not signal handler). - * see check_io() - */ -#ifdef DEBUG - fprintf(stderr,"break!\n"); -#endif - erts_break_requested = 1; - erts_check_io_async_sig_interrupt(1); /* Make sure we don't sleep in erts_poll_wait */ -} - -static void do_quit(void) -{ - halt_0(0); -} - -void erts_set_ignore_break(void) { -} - -void init_break_handler(void) -{ - sys_sigset(SIGINT, request_break); - sys_sigset(SIGQUIT, do_quit); -} - -void erts_replace_intr(void) { -} - -int sys_max_files(void) -{ - return(max_files); -} - -/******************* Routines for time measurement *********************/ - -int sys_init_time(void) -{ - erts_clock_rate = sysClkRateGet(); - /* - ** One could imagine that it would be better returning - ** a resolution more near the clock rate, like in: - ** return 1000 / erts_clock_rate; - ** but tests show that such isn't the case (rounding errors?) - ** Well, we go for the Unix variant of returning 1 - ** as a constant virtual clock rate. - */ - return SYS_CLOCK_RESOLUTION; -} - -int erts_clock_rate; -static volatile int ticks_inuse; -static volatile unsigned long ticks_collected; /* will wrap */ -static WDOG_ID watchdog_id; -static ULONG user_time; -static int this_task_id, sys_itime; -static SysHrTime hrtime_wrap; -static unsigned long last_tick_count; - -static void tolerant_time_clockint(int count) -{ - if (watchdog_id != NULL) { - if (taskIsReady(this_task_id)) - user_time += 1; - ++count; - if (!ticks_inuse) { - ticks_collected += count; - count = 0; - } - wdStart(watchdog_id, 1, (FUNCPTR)tolerant_time_clockint, count); - } -} - -int sys_init_hrtime(void) -{ - this_task_id = taskIdSelf(); /* OK, this only works for one single task - in the system... */ - user_time = 0; - - ticks_inuse = 0; - ticks_collected = 0; - hrtime_wrap = 0; - last_tick_count = 0; - - sys_itime = 1000 / erts_clock_rate; - watchdog_id = wdCreate(); - wdStart(watchdog_id, 1, (FUNCPTR) tolerant_time_clockint, 0); - return 0; -} - -SysHrTime sys_gethrtime(void) -{ - SysHrTime ticks; - - ++ticks_inuse; - ticks = (SysHrTime) (ticks_collected & 0x7FFFFFFF); - ticks_inuse = 0; - if (ticks < (SysHrTime) last_tick_count) { - hrtime_wrap += 1UL << 31; - } - last_tick_count = ticks; - return (ticks + hrtime_wrap) * ((SysHrTime) (1000000000UL / - erts_clock_rate)); -} - -void sys_gettimeofday(SysTimeval *tvp) -{ - struct timespec now; - - clock_gettime(CLOCK_REALTIME, &now); - tvp->tv_sec = now.tv_sec; - tvp->tv_usec = now.tv_nsec / 1000; -} - -clock_t sys_times(SysTimes *t) -{ - t->tms_stime = t->tms_cutime = t->tms_cstime = 0; - ++ticks_inuse; - t->tms_utime = user_time; - ticks_inuse = 0; - return tickGet(); /* The best we can do... */ -} - -/* This is called when *this task* is deleted */ -static void delete_hook(void) -{ - if (watchdog_id != NULL) { - wdDelete(watchdog_id); - watchdog_id = NULL; - } - erlang_id = 0; - this_task_id = 0; -} - -/************************** OS info *******************************/ - -/* Used by erlang:info/1. */ -/* (This code was formerly in drv.XXX/XXX_os_drv.c) */ - -#define MAX_VER_STR 9 /* Number of characters to - consider in version string */ - -static FUNCTION(int, get_number, (char** str_ptr)); - -char os_type[] = "vxworks"; - -static int -get_number(char **str_ptr) -{ - char* s = *str_ptr; /* Pointer to beginning of string. */ - char* dot; /* Pointer to dot in string or NULL. */ - - if (!isdigit(*s)) - return 0; - if ((dot = strchr(s, '.')) == NULL) { - *str_ptr = s+strlen(s); - return atoi(s); - } else { - *dot = '\0'; - *str_ptr = dot+1; - return atoi(s); - } -} - -/* namebuf; Where to return the name. */ -/* size; Size of name buffer. */ -void -os_flavor(char *namebuf, unsigned size) -{ - strcpy(namebuf, "-"); -} - -/* int* pMajor; Pointer to major version. */ -/* int* pMinor; Pointer to minor version. */ -/* int* pBuild; Pointer to build number. */ -void -os_version(int *pMajor, int *pMinor, int *pBuild) -{ - char os_ver[MAX_VER_STR+2]; - char* release; /* Pointer to the release string: - * X.Y or X.Y.Z. - */ - strncpy(os_ver, vxWorksVersion, MAX_VER_STR); - release = os_ver; - *pMajor = get_number(&release); - *pMinor = get_number(&release); - *pBuild = get_number(&release); -} - -void init_getenv_state(GETENV_STATE *state) -{ - *state = NULL; -} - -char *getenv_string(GETENV_STATE *state0) -{ - return NULL; -} - -void fini_getenv_state(GETENV_STATE *state) -{ - *state = NULL; -} - -/************************** Port I/O *******************************/ - - -/* I. Common stuff */ - -#define TMP_BUF_MAX (tmp_buf_size - 1024) -static byte *tmp_buf; -static Uint tmp_buf_size; - -/* II. The spawn/fd/vanilla drivers */ - -/* This data is shared by these drivers - initialized by spawn_init() */ -static struct driver_data { - int port_num, ofd, packet_bytes, report_exit; - int exitcode, exit_reported; /* For returning of exit codes. */ -} *driver_data; /* indexed by fd */ - -/* - * Locking only for exitcodes and exit_reported, one global sem for all - * spawn ports as this is rare. - */ -static SEM_ID driver_data_sem = NULL; -/* - * Also locking when looking up entries in the load table - */ -static SEM_ID entry_data_sem = NULL; - -/* We maintain a linked fifo queue of these structs in order */ -/* to manage unfinnished reads/and writes on differenet fd's */ - -typedef struct pend { - char *cpos; - int fd; - int remain; - struct pend *next; - char buf[1]; /* this is a trick to be able to malloc one chunk */ -} Pend; - -static struct fd_data { - int inport, outport; - char *buf, *cpos; - int sz, remain; /* for input on fd */ - Pend* pending; /* pending outputs */ - -} *fd_data; /* indexed by fd */ - - -/* Driver interfaces */ -static ErlDrvData spawn_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts); -static ErlDrvData fd_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts); -static ErlDrvData vanilla_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts); -static int spawn_init(void); -static void fd_stop(ErlDrvData); -static void stop(ErlDrvData); -static void ready_input(ErlDrvData fd, ErlDrvEvent ready_fd); -static void ready_output(ErlDrvData fd, ErlDrvEvent ready_fd); -static void output(ErlDrvData fd, char *buf, ErlDrvSizeT len); -static void stop_select(ErlDrvEvent, void*); - -struct erl_drv_entry spawn_driver_entry = { - spawn_init, - spawn_start, - stop, - output, - ready_input, - ready_output, - "spawn", - NULL, /* finish */ - NULL, /* handle */ - NULL, /* control */ - NULL, /* timeout */ - NULL, /* outputv */ - NULL, /* ready_async */ - NULL, /* flush */ - NULL, /* call */ - NULL, /* event */ - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - 0, /* ERL_DRV_FLAGs */ - NULL, /* handle2 */ - NULL, /* process_exit */ - stop_select - -}; -struct erl_drv_entry fd_driver_entry = { - NULL, - fd_start, - fd_stop, - output, - ready_input, - ready_output, - "fd", - NULL, /* finish */ - NULL, /* handle */ - NULL, /* control */ - NULL, /* timeout */ - NULL, /* outputv */ - NULL, /* ready_async */ - NULL, /* flush */ - NULL, /* call */ - NULL, /* event */ - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - 0, /* ERL_DRV_FLAGs */ - NULL, /* handle2 */ - NULL, /* process_exit */ - stop_select -}; -struct erl_drv_entry vanilla_driver_entry = { - NULL, - vanilla_start, - stop, - output, - ready_input, - ready_output, - "vanilla", - NULL, /* finish */ - NULL, /* handle */ - NULL, /* control */ - NULL, /* timeout */ - NULL, /* outputv */ - NULL, /* ready_async */ - NULL, /* flush */ - NULL, /* call */ - NULL, /* event */ - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - 0, /* ERL_DRV_FLAGs */ - NULL, /* handle2 */ - NULL, /* process_exit */ - stop_select -}; - -/* -** Set up enough of the driver_data structure to be able to report exit status. -** Some things may be initiated again, but that is no real problem. -*/ -static int pre_set_driver_data(int ifd, int ofd, - int read_write, int report_exit) { - if (read_write & DO_READ) { - driver_data[ifd].report_exit = report_exit; - driver_data[ifd].exitcode = 0; - driver_data[ifd].exit_reported = 0; - if (read_write & DO_WRITE) { - driver_data[ifd].ofd = ofd; - if (ifd != ofd) { - driver_data[ofd] = driver_data[ifd]; - driver_data[ofd].report_exit = 0; - } - } else { /* DO_READ only */ - driver_data[ifd].ofd = -1; - } - return(ifd); - } else { /* DO_WRITE only */ - driver_data[ofd].report_exit = 0; - driver_data[ofd].exitcode = 0; - driver_data[ofd].exit_reported = 0; - driver_data[ofd].ofd = ofd; - return(ofd); - } -} - -/* -** Set up the driver_data structure, it may have been initiated -** partly by the function above, but we dont care. -*/ -static int set_driver_data(int port_num, int ifd, int ofd, - int packet_bytes, int read_write, - int report_exit) -{ - if (read_write & DO_READ) { - driver_data[ifd].packet_bytes = packet_bytes; - driver_data[ifd].port_num = port_num; - driver_data[ifd].report_exit = report_exit; - if (read_write & DO_WRITE) { - driver_data[ifd].ofd = ofd; - if (ifd != ofd) { - driver_data[ofd] = driver_data[ifd]; - driver_data[ofd].report_exit = 0; - } - } else { /* DO_READ only */ - driver_data[ifd].ofd = -1; - } - (void) driver_select(port_num, ifd, ERL_DRV_READ|ERL_DRV_USE, 1); - return(ifd); - } else { /* DO_WRITE only */ - driver_data[ofd].packet_bytes = packet_bytes; - driver_data[ofd].port_num = port_num; - driver_data[ofd].report_exit = 0; - driver_data[ofd].ofd = ofd; - return(ofd); - } -} - -static int need_new_sems = 1; - -static int spawn_init(void) -{ - char *stackenv; - int size; - driver_data = (struct driver_data *) - erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data)); - if (need_new_sems) { - driver_data_sem = semMCreate - (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); - entry_data_sem = semMCreate - (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); - } - if (driver_data_sem == NULL || entry_data_sem == NULL) { - erl_exit(1,"Could not allocate driver locking semaphore."); - } - need_new_sems = 0; - - (void)uxPipeDrv(); /* Install pipe driver */ - - if ((stackenv = getenv("ERLPORTSTACKSIZE")) != NULL && - (size = atoi(stackenv)) > 0) - port_stack_size = size; - else - port_stack_size = DEFAULT_PORT_STACK_SIZE; - return 0; -} - -/* Argv has to be built vith the save_xxx routines, not with whathever - sys_xxx2 has in mind... */ -#define argv_alloc save_malloc -#define argv_realloc save_realloc -#define argv_free save_free -/* Build argv, return argc or -1 on failure */ -static int build_argv(char *name, char ***argvp) -{ - int argvsize = 10, argc = 0; - char *args, *arglast = NULL, *argp; - char **argv; - -#ifdef DEBUG - fdprintf(2, "Building argv, %s =>\n", name); -#endif - if ((argv = (char **)argv_alloc(argvsize * sizeof(char *))) == NULL) - return(-1); - if ((args = argv_alloc(strlen(name) + 1)) == NULL) - return(-1); - strcpy(args, name); - argp = strtok_r(args, " \t", &arglast); - while (argp != NULL) { - if (argc + 1 >= argvsize) { - argvsize += 10; - argv = (char **)argv_realloc((char *)argv, argvsize*sizeof(char *)); - if (argv == NULL) { - argv_free(args); - return(-1); - } - } -#ifdef DEBUG - fdprintf(2, "%s\n", argp); -#endif - argv[argc++] = argp; - argp = strtok_r((char *)NULL, " \t", &arglast); - } - argv[argc] = NULL; - *argvp = argv; - return(argc); -} -#undef argv_alloc -#undef argv_realloc -#undef argv_free - - -/* Lookup and return global text symbol or NULL on failure - Symbol name is null-terminated and without the leading '_' */ -static FUNCPTR -lookup(char *sym) -{ - char buf[256]; - char *symname = buf; - int len; - FUNCPTR entry; - SYM_TYPE type; - - len = strlen(sym); - if (len > 254 && (symname = malloc(len+2)) == NULL) - return(NULL); -#if defined _ARCH_PPC || defined SIMSPARCSOLARIS - /* GCC for PPC and SIMSPARC doesn't add a leading _ to symbols */ - strcpy(symname, sym); -#else - sprintf(symname, "_%s", sym); -#endif - if (symFindByNameAndType(sysSymTbl, symname, (char **)&entry, - &type, N_EXT | N_TEXT, N_EXT | N_TEXT) != OK) - entry = NULL; - if (symname != buf) - free(symname); - return(entry); -} - -/* This function is spawned to build argc, argv, lookup the symbol to call, - connect and set up file descriptors, and make the actual call. - N.B. 'name' was allocated by the Erlang task (through plain_malloc) and - is freed by this port program task. - Note: 'name' may be a path containing '/'. */ - -static void call_proc(char *name, int ifd, int ofd, int read_write, - int redir_stderr, int driver_index, - int p6, int p7, int p8, int p9) -{ - int argc; - char **argv, *bname; - FUNCPTR entry; - int ret = -1; - - /* Must consume 'name' */ - argc = build_argv(name, &argv); - plain_free(name); - /* Find basename of path */ - if ((bname = strrchr(argv[0], '/')) != NULL) { - bname++; - } else { - bname = argv[0]; - } -#ifdef DEBUG - fdprintf(2, "Port program name: %s\n", bname); -#endif - semTake(entry_data_sem, WAIT_FOREVER); - - if (argc > 0) { - if ((entry = lookup(bname)) == NULL) { - int fd; - char *fn; - /* NOTE: We don't check the return value of loadModule, - since that was incompatibly changed from 5.0.2b to 5.1, - but rather do a repeated lookup(). */ - if ((fd = open(argv[0], O_RDONLY)) > 0) { - (void) loadModule(fd, GLOBAL_SYMBOLS); - close(fd); - entry = lookup(bname); - } - if (entry == NULL) { - /* filename == func failed, try func.o */ - if ((fn = malloc(strlen(argv[0]) + 3)) != NULL) { /* ".o\0" */ - strcpy(fn, argv[0]); - strcat(fn, ".o"); - if ((fd = open(fn, O_RDONLY)) > 0) { - (void) loadModule(fd, GLOBAL_SYMBOLS); - close(fd); - entry = lookup(bname); - } - free(fn); - } - } - } - } else { - entry = NULL; - } - semGive(entry_data_sem); - - if (read_write & DO_READ) { /* emulator read */ - save_fd(ofd); - ioTaskStdSet(0, 1, ofd); /* stdout for process */ - if(redir_stderr) - ioTaskStdSet(0, 2, ofd);/* stderr for process */ - } - if (read_write & DO_WRITE) { /* emulator write */ - save_fd(ifd); - ioTaskStdSet(0, 0, ifd); /* stdin for process */ - } - if (entry != NULL) { - ret = (*entry)(argc, argv, (char **)NULL); /* NULL for envp */ - } else { - fdprintf(2, "Could not exec \"%s\"\n", argv[0]); - ret = -1; - } - if (driver_data[driver_index].report_exit) { - semTake(driver_data_sem, WAIT_FOREVER); - driver_data[driver_index].exitcode = ret; - driver_data[driver_index].exit_reported = 1; - semGive(driver_data_sem); - } - /* We *don't* want to close the pipes here, but let the delete - hook take care of it - it might want to flush stdout and there'd - better be an open descriptor to flush to... */ - exit(ret); -} - -static void close_pipes(int ifd[2], int ofd[2], int read_write) -{ - if (read_write & DO_READ) { - (void) close(ifd[0]); - (void) close(ifd[1]); - } - if (read_write & DO_WRITE) { - (void) close(ofd[0]); - (void) close(ofd[1]); - } -} - -static void init_fd_data(int fd, int port_unused_argument) -{ - SET_NONBLOCKING(fd); - fd_data[fd].pending = NULL; - fd_data[fd].buf = fd_data[fd].cpos = NULL; - fd_data[fd].remain = fd_data[fd].sz = 0; -} - -static ErlDrvData spawn_start(ErlDrvPort port_num, char *name,SysDriverOpts* opts) -{ - int ifd[2], ofd[2], len, nl, id; - char taskname[11], *progname, *bname; - char *space_in_command; - int packet_bytes = opts->packet_bytes; - int read_write = opts->read_write; - int use_stdio = opts->use_stdio; - int redir_stderr = opts->redir_stderr; - int driver_index; - - if (!use_stdio){ - return (ErlDrvData) -3; - } - - /* Create pipes and set the Erlang task as owner of its - * read and write ends (through save_fd()). - */ - switch (read_write) { - case DO_READ: - if (pipe(ifd) < 0){ - return (ErlDrvData) -2; - } - if (ifd[0] >= max_files) { - close_pipes(ifd, ofd, read_write); - errno = ENFILE; - return (ErlDrvData) -2; - } - save_fd(ifd[0]); - break; - case DO_WRITE: - if (pipe(ofd) < 0) { - return (ErlDrvData) -2; - } - if (ofd[1] >= max_files) { - close_pipes(ifd, ofd, read_write); - errno = ENFILE; - return (ErlDrvData) -2; - } - save_fd(ofd[1]); - break; - case DO_READ|DO_WRITE: - if (pipe(ifd) < 0){ - return (ErlDrvData) -2; - } - if (ifd[0] >= max_files || pipe(ofd) < 0) { - close_pipes(ifd, ofd, DO_READ); - errno = ENFILE; - return (ErlDrvData) -2; - } - if (ofd[1] >= max_files) { - close_pipes(ifd, ofd, read_write); - errno = ENFILE; - return (ErlDrvData) -2; - } - save_fd(ifd[0]); - save_fd(ofd[1]); - break; - default: - return (ErlDrvData) -1; - } - - /* Allocate space for program name to be freed by the - * spawned task. We use plain_malloc so that the allocated - * space is not owned by the Erlang task. - */ - - if ((progname = plain_malloc(strlen(name) + 1)) == NULL) { - close_pipes(ifd, ofd, read_write); - errno = ENOMEM; - return (ErlDrvData) -2; - } - strcpy(progname, name); - - /* Check if name contains a space - * (e.g "port_test -o/home/gandalf/tornado/wind/target/erlang") - */ - if ((space_in_command = strrchr(progname, ' ')) != NULL) { - *space_in_command = '\0'; - } - - /* resulting in "port_test" */ - if ((bname = strrchr(progname, '/')) != NULL) - bname++; - else - bname = progname; - - /* resulting in "port_test" */ - len = strlen(bname); - nl = len > 10 ? 10 : len; - strncpy(taskname, bname, nl); - taskname[nl] = '\0'; - if (space_in_command != NULL) - *space_in_command = ' '; - driver_index = pre_set_driver_data(ifd[0], ofd[1], - read_write, opts->exit_status); - - /* resetting to "port_test -o/home/gandalf/tornado/wind/target/erlang" */ - if ((id = taskSpawn(taskname, spTaskPriority, spTaskOptions, - port_stack_size, (FUNCPTR)call_proc, (int)progname, - ofd[0], ifd[1], read_write, redir_stderr, driver_index, - 0,0,0,0)) - == ERROR) { - close_pipes(ifd, ofd, read_write); - plain_free(progname); /* only when spawn fails */ - errno = ENOMEM; - return (ErlDrvData) -2; - } -#ifdef DEBUG - fdprintf(2, "Spawned %s as %s[0x%x]\n", name, taskname, id); -#endif - if (read_write & DO_READ) - init_fd_data(ifd[0], port_num); - if (read_write & DO_WRITE) - init_fd_data(ofd[1], port_num); - return (ErlDrvData) (set_driver_data(port_num, ifd[0], ofd[1], - packet_bytes,read_write, - opts->exit_status)); -} - -static ErlDrvData fd_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts) -{ - if (((opts->read_write & DO_READ) && opts->ifd >= max_files) || - ((opts->read_write & DO_WRITE) && opts->ofd >= max_files)) { - return (ErlDrvData) -1; - } - - if (opts->read_write & DO_READ) - init_fd_data(opts->ifd, port_num); - if (opts->read_write & DO_WRITE) - init_fd_data(opts->ofd, port_num); - return (ErlDrvData) (set_driver_data(port_num, opts->ifd, opts->ofd, - opts->packet_bytes, opts->read_write, 0)); -} - -static void clear_fd_data(int fd) -{ - - if (fd_data[fd].sz > 0) - erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf); - fd_data[fd].buf = NULL; - fd_data[fd].sz = 0; - fd_data[fd].remain = 0; - fd_data[fd].cpos = NULL; -} - -static void nbio_stop_fd(int port_num, int fd) -{ - Pend *p, *p1; - - driver_select(port_num, fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); - clear_fd_data(fd); - p = fd_data[fd].pending; - SET_BLOCKING(fd); - while (p) { - p1 = p->next; - free(p); - p = p1; - } - fd_data[fd].pending = NULL; -} - -static void fd_stop(ErlDrvData drv_data) -{ - int ofd; - int fd = (int) drv_data; - - nbio_stop_fd(driver_data[fd].port_num, (int)fd); - ofd = driver_data[fd].ofd; - if (ofd != fd && ofd != -1) - nbio_stop_fd(driver_data[fd].port_num, (int)ofd); /* XXX fd = ofd? */ -} - -static ErlDrvData -vanilla_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts) -{ - int flags, fd; - struct stat statbuf; - - DEBUGF(("vanilla_start, name: %s [r=%1i w=%1i]\n", name, - opts->read_write & DO_READ, - opts->read_write & DO_WRITE)); - - flags = (opts->read_write == DO_READ ? O_RDONLY : - opts->read_write == DO_WRITE ? O_WRONLY|O_CREAT|O_TRUNC : - O_RDWR|O_CREAT); - if ((fd = open(name, flags, 0666)) < 0){ - errno = ENFILE; - return (ErlDrvData) -2; - } - if (fd >= max_files) { - close(fd); - errno = ENFILE; - return (ErlDrvData) -2; - } - if (fstat(fd, &statbuf) < 0) { - close(fd); - errno = ENFILE; - return (ErlDrvData) -2; - } - - /* Return error for reading regular files (doesn't work) */ - if (ISREG(statbuf) && ((opts->read_write) & DO_READ)) { - close(fd); - return (ErlDrvData) -3; - } - init_fd_data(fd, port_num); - return (ErlDrvData) (set_driver_data(port_num, fd, fd, - opts->packet_bytes, opts->read_write, 0)); -} - -/* Note that driver_data[fd].ifd == fd if the port was opened for reading, */ -/* otherwise (i.e. write only) driver_data[fd].ofd = fd. */ - -static void stop(ErlDrvData drv_data) -{ - int port_num, ofd; - int fd = (int) drv_data; - - port_num = driver_data[fd].port_num; - nbio_stop_fd(port_num, fd); - driver_select(port_num, fd, ERL_DRV_USE, 0); /* close(fd) */ - - ofd = driver_data[fd].ofd; - if (ofd != fd && ofd != -1) { - nbio_stop_fd(port_num, ofd); - driver_select(port_num, ofd, ERL_DRV_USE, 0); /* close(fd) */ - } -} - -static int sched_write(int port_num,int fd, char *buf, int len, int pb) -{ - Pend *p, *p2, *p3; - int p_bytes = len; - - p = (Pend*) erts_alloc_fnf(ERTS_ALC_T_PEND_DATA, pb + len + sizeof(Pend)); - if (!p) { - driver_failure(port_num, -1); - return(-1); - } - - switch(pb) { - case 4: put_int32(len, p->buf); break; - case 2: put_int16(len, p->buf); break; - case 1: put_int8(len, p->buf); break; - case 0: break; /* Handles this case too */ - } - sys_memcpy(p->buf + pb, buf, len); - driver_select(port_num, fd, ERL_DRV_WRITE|ERL_DRV_USE, 1); - p->cpos = p->buf; - p->fd = fd; - p->next = NULL; - p->remain = len + pb; - p2 = fd_data[fd].pending; - if (p2 == NULL) - fd_data[fd].pending = p; - else { - p3 = p2->next; - while(p3) { - p_bytes += p2->remain; - p2 = p2->next; - p3 = p3->next; - } - p2->next = p; - } - if (p_bytes > (1 << 13)) /* More than 8 k pending */ - set_busy_port(port_num, 1); - return(0); -} - -/* Fd is the value returned as drv_data by the start func */ -static void output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) -{ - int buf_done, port_num, wval, pb, ofd; - byte lb[4]; - struct iovec iv[2]; - int fd = (int) drv_data; - - pb = driver_data[fd].packet_bytes; - port_num = driver_data[fd].port_num; - - if ((ofd = driver_data[fd].ofd) == -1) { - return; - } - - if (fd_data[ofd].pending) { - sched_write(port_num, ofd, buf, len, pb); - return; - } - - if ((pb == 2 && len > 65535) || (pb == 1 && len > 255)) { - driver_failure_posix(port_num, EINVAL); - return; - } - if (pb == 0) { - wval = write(ofd, buf, len); - } else { - lb[0] = (len >> 24) & 255; /* MSB */ - lb[1] = (len >> 16) & 255; - lb[2] = (len >> 8) & 255; - lb[3] = len & 255; /* LSB */ - iv[0].iov_base = (char*) lb + (4 - pb); - iv[0].iov_len = pb; - iv[1].iov_base = buf; - iv[1].iov_len = len; - wval = writev(ofd, iv, 2); - } - if (wval == pb + len ) { - return; - } - if (wval < 0) { - if ((errno == EINTR) || (errno == ERRNO_BLOCK)) { - if (pb) { - sched_write(port_num, ofd, buf ,len, pb); - } else if (pb == 0) { - sched_write(port_num, ofd, buf ,len, 0); - } - return; - } - driver_failure_posix(driver_data[fd].port_num, EINVAL); - return; - } - if (wval < pb) { - sched_write(port_num, ofd, (lb +4 -pb) + wval, pb-wval, 0); - sched_write(port_num, ofd, buf ,len, 0); - return; - } - - /* we now know that wval < (pb + len) */ - buf_done = wval - pb; - sched_write(port_num, ofd, buf + buf_done, len - buf_done,0); -} - -static void stop_select(ErlDrvEvent fd, void* _) -{ - close((int)fd); -} - -static int ensure_header(int fd,char *buf,int packet_size, int sofar) -{ - int res = 0; - int remaining = packet_size - sofar; - - SET_BLOCKING(fd); - if (read_fill(fd, buf+sofar, remaining) != remaining) - return -1; - switch (packet_size) { - case 1: res = get_int8(buf); break; - case 2: res = get_int16(buf); break; - case 4: res = get_int32(buf); break; - } - SET_NONBLOCKING(fd); - return(res); -} - -static int port_inp_failure(int port_num, int ready_fd, int res) -{ - (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); - clear_fd_data(ready_fd); - if (res == 0) { - if (driver_data[ready_fd].report_exit) { - int tmpexit = 0; - int reported; - /* Lock the driver_data structure */ - semTake(driver_data_sem, WAIT_FOREVER); - if ((reported = driver_data[ready_fd].exit_reported)) - tmpexit = driver_data[ready_fd].exitcode; - semGive(driver_data_sem); - if (reported) { - erts_fprintf(stderr,"Exitcode %d reported\r\n", tmpexit); - driver_report_exit(port_num, tmpexit); - } - } - driver_failure_eof(port_num); - } else { - driver_failure(port_num, res); - } - return 0; -} - -/* fd is the drv_data that is returned from the */ -/* initial start routine */ -/* ready_fd is the descriptor that is ready to read */ - -static void ready_input(ErlDrvData drv_data, ErlDrvEvent drv_event) -{ - int port_num, packet_bytes, res; - Uint h = 0; - char *buf; - int fd = (int) drv_data; - int ready_fd = (int) drv_event; - - port_num = driver_data[fd].port_num; - packet_bytes = driver_data[fd].packet_bytes; - - if (packet_bytes == 0) { - if ((res = read(ready_fd, tmp_buf, tmp_buf_size)) > 0) { - driver_output(port_num, (char*)tmp_buf, res); - return; - } - port_inp_failure(port_num, ready_fd, res); - return; - } - - if (fd_data[ready_fd].remain > 0) { /* We try to read the remainder */ - /* space is allocated in buf */ - res = read(ready_fd, fd_data[ready_fd].cpos, - fd_data[ready_fd].remain); - if (res < 0) { - if ((errno == EINTR) || (errno == ERRNO_BLOCK)) { - ; - } else { - port_inp_failure(port_num, ready_fd, res); - } - } else if (res == 0) { - port_inp_failure(port_num, ready_fd, res); - } else if (res == fd_data[ready_fd].remain) { /* we're done */ - driver_output(port_num, fd_data[ready_fd].buf, - fd_data[ready_fd].sz); - clear_fd_data(ready_fd); - } else { /* if (res < fd_data[ready_fd].remain) */ - fd_data[ready_fd].cpos += res; - fd_data[ready_fd].remain -= res; - } - return; - } - - - if (fd_data[ready_fd].remain == 0) { /* clean fd */ - /* We make one read attempt and see what happens */ - res = read(ready_fd, tmp_buf, tmp_buf_size); - if (res < 0) { - if ((errno == EINTR) || (errno == ERRNO_BLOCK)) - return; - port_inp_failure(port_num, ready_fd, res); - return; - } - else if (res == 0) { /* eof */ - port_inp_failure(port_num, ready_fd, res); - return; - } - else if (res < packet_bytes) { /* Ugly case... get at least */ - if ((h = ensure_header(ready_fd, tmp_buf, packet_bytes, res))==-1) { - port_inp_failure(port_num, ready_fd, -1); - return; - } - buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); - if (!buf) { - port_inp_failure(port_num, ready_fd, -1); - return; - } - fd_data[ready_fd].buf = buf; - fd_data[ready_fd].sz = h; - fd_data[ready_fd].remain = h; - fd_data[ready_fd].cpos = buf; - return; - } - else { /* if (res >= packet_bytes) */ - unsigned char* cpos = tmp_buf; - int bytes_left = res; - while (1) { /* driver_output as many as possible */ - if (bytes_left == 0) { - clear_fd_data(ready_fd); - return; - } - if (bytes_left < packet_bytes) { /* Yet an ugly case */ - if((h=ensure_header(ready_fd, cpos, - packet_bytes, bytes_left))==-1) { - port_inp_failure(port_num, ready_fd, -1); - return; - } - buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); - if (!buf) - port_inp_failure(port_num, ready_fd, -1); - fd_data[ready_fd].buf = buf; - fd_data[ready_fd].sz = h; - fd_data[ready_fd].remain = h; - fd_data[ready_fd].cpos = buf; - return; - } - switch (packet_bytes) { - case 1: h = get_int8(cpos); cpos += 1; break; - case 2: h = get_int16(cpos); cpos += 2; break; - case 4: h = get_int32(cpos); cpos += 4; break; - } - bytes_left -= packet_bytes; - /* we've got the header, now check if we've got the data */ - if (h <= (bytes_left)) { - driver_output(port_num, (char*) cpos, h); - cpos += h; - bytes_left -= h; - continue; - } - else { /* The last message we got was split */ - buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); - if (!buf) { - port_inp_failure(port_num, ready_fd, -1); - } - sys_memcpy(buf, cpos, bytes_left); - fd_data[ready_fd].buf = buf; - fd_data[ready_fd].sz = h; - fd_data[ready_fd].remain = h - bytes_left; - fd_data[ready_fd].cpos = buf + bytes_left; - return; - } - } - return; - } - } - fprintf(stderr, "remain %d \n", fd_data[ready_fd].remain); - port_inp_failure(port_num, ready_fd, -1); -} - - -/* fd is the drv_data that is returned from the */ -/* initial start routine */ -/* ready_fd is the descriptor that is ready to read */ - -static void ready_output(ErlDrvData drv_data, ErlDrvEvent drv_event) -{ - Pend *p; - int wval; - - int fd = (int) drv_data; - int ready_fd = (int) drv_event; - - while(1) { - if ((p = fd_data[ready_fd].pending) == NULL) { - driver_select(driver_data[fd].port_num, ready_fd, - ERL_DRV_WRITE, 0); - return; - } - wval = write(p->fd, p->cpos, p->remain); - if (wval == p->remain) { - fd_data[ready_fd].pending = p->next; - erts_free(ERTS_ALC_T_PEND_DATA, p); - if (fd_data[ready_fd].pending == NULL) { - driver_select(driver_data[fd].port_num, ready_fd, - ERL_DRV_WRITE, 0); - set_busy_port(driver_data[fd].port_num, 0); - return; - } - else - continue; - } - else if (wval < 0) { - if (errno == ERRNO_BLOCK || errno == EINTR) - return; - else { - driver_select(driver_data[fd].port_num, ready_fd, - ERL_DRV_WRITE, 0); - driver_failure(driver_data[fd].port_num, -1); - return; - } - } - else if (wval < p->remain) { - p->cpos += wval; - p->remain -= wval; - return; - } - } -} - -/* Fills in the systems representation of the jam/beam process identifier. -** The Pid is put in STRING representation in the supplied buffer, -** no interpretatione of this should be done by the rest of the -** emulator. The buffer should be at least 21 bytes long. -*/ -void sys_get_pid(char *buffer){ - int p = taskIdSelf(); /* Hmm, may be negative??? requires some GB of - memory to make the TCB address convert to a - negative value. */ - sprintf(buffer,"%d", p); -} - -int -erts_sys_putenv(char *buffer, int sep_ix) -{ - return putenv(buffer); -} - -int -erts_sys_getenv(char *key, char *value, size_t *size) -{ - char *orig_value; - int res; - orig_value = getenv(key); - if (!orig_value) - res = -1; - else { - size_t len = sys_strlen(orig_value); - if (len >= *size) { - *size = len + 1; - res = 1; - } - else { - *size = len; - sys_memcpy((void *) value, (void *) orig_value, len+1); - res = 0; - } - } - return res; -} - -int -erts_sys_getenv__(char *key, char *value, size_t *size) -{ - return erts_sys_getenv(key, value, size); -} - -void -sys_init_io(void) -{ - tmp_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_TMP_BUF, SYS_TMP_BUF_SIZE); - tmp_buf_size = SYS_TMP_BUF_SIZE; - fd_data = (struct fd_data *) - erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(struct fd_data)); -} - - -/* Fill buffer, return buffer length, 0 for EOF, < 0 for error. */ - -static int read_fill(int fd, char *buf, int len) -{ - int i, got = 0; - do { - if ((i = read(fd, buf+got, len-got)) <= 0) { - return i; - } - got += i; - } while (got < len); - return (len); -} - - -/************************** Misc... *******************************/ - -extern const char pre_loaded_code[]; -extern char* const pre_loaded[]; - - -/* Float conversion */ - -int sys_chars_to_double(char *buf, double *fp) -{ - char *s = buf; - - /* The following check is incorporated from the Vee machine */ - -#define ISDIGIT(d) ((d) >= '0' && (d) <= '9') - - /* Robert says that something like this is what he really wanted: - * - * 7 == sscanf(Tbuf, "%[+-]%[0-9].%[0-9]%[eE]%[+-]%[0-9]%s", ....); - * if (*s2 == 0 || *s3 == 0 || *s4 == 0 || *s6 == 0 || *s7) - * break; - */ - - /* Scan string to check syntax. */ - if (*s == '+' || *s == '-') - s++; - - if (!ISDIGIT(*s)) /* Leading digits. */ - return -1; - while (ISDIGIT(*s)) s++; - if (*s++ != '.') /* Decimal part. */ - return -1; - if (!ISDIGIT(*s)) - return -1; - while (ISDIGIT(*s)) s++; - if (*s == 'e' || *s == 'E') { - /* There is an exponent. */ - s++; - if (*s == '+' || *s == '-') - s++; - if (!ISDIGIT(*s)) - return -1; - while (ISDIGIT(*s)) s++; - } - if (*s) /* That should be it */ - return -1; - - if (sscanf(buf, "%lf", fp) != 1) - return -1; - return 0; -} - -/* - ** Convert a double to ascii format 0.dddde[+|-]ddd - ** return number of characters converted - */ - -int sys_double_to_chars(double fp, char *buf) -{ - (void) sprintf(buf, "%.20e", fp); - return strlen(buf); -} - - -/* Floating point exceptions */ - -#if (CPU == SPARC) -jmp_buf fpe_jmp; - -RETSIGTYPE fpe_sig_handler(int sig) -{ - longjmp(fpe_jmp, 1); -} - -#elif (CPU == PPC603) -static void fix_registers(void){ - FP_CONTEXT fpcontext; - fppSave(&fpcontext); - fpcontext.fpcsr &= ~(_PPC_FPSCR_INIT); - fppRestore(&fpcontext); -} -#endif - - -/* Return a pointer to a vector of names of preloaded modules */ - -Preload* sys_preloaded(void) -{ - return (Preload *) pre_loaded; -} - -/* Return a pointer to preloaded code for module "module" */ -unsigned char* sys_preload_begin(Preload *pp) -{ - return pp->code; -} - -/* Clean up if allocated */ -void sys_preload_end(Preload *pp) -{ - /* Nothing */ -} - -/* Read a key from console (?) */ - -int sys_get_key(int fd) -{ - int c; - unsigned char rbuf[64]; - - fflush(stdout); /* Flush query ??? */ - - if ((c = read(fd,rbuf,64)) <= 0) - return c; - return rbuf[0]; -} - - -/* A real printf that does the equivalent of fprintf(stdout, ...) */ - -/* ARGSUSED */ -static STATUS -stdio_write(char *buf, int nchars, int fp) -{ - if (fwrite(buf, sizeof(char), nchars, (FILE *)fp) == 0) - return(ERROR); - return(OK); -} - -int real_printf(const char *fmt, ...) -{ - va_list ap; - int err; - - va_start(ap, fmt); - err = fioFormatV(fmt, ap, stdio_write, (int)stdout); - va_end(ap); - return(err); -} - - -/* - * Little function to do argc, argv calls from (e.g.) VxWorks shell - * The arguments should be in the form of a single ""-enclosed string - * NOTE: This isn't really part of the emulator, just included here - * so we can use the handy functions and memory reclamation. - */ -void argcall(char *args) -{ - int argc; - char **argv; - FUNCPTR entry; - - if (args != NULL) { - if ((argc = build_argv(args, &argv)) > 0) { - if ((entry = lookup(argv[0])) != NULL) - (*entry)(argc, argv, (char **)NULL); /* NULL for envp */ - else - fprintf(stderr, "Couldn't find %s\n", argv[0]); - } else - fprintf(stderr, "Failed to build argv!\n"); - } else - fprintf(stderr, "No argument list!\n"); -} - - -/* That concludes the Erlang stuff - now we just need to implement an OS... - - Just kidding, but resource reclamation isn't the strength of VxWorks */ -#undef calloc -#undef free -#undef cfree -#undef malloc -#undef realloc -#undef open -#undef creat -#undef socket -#undef accept -#undef close -#undef fopen -#undef fdopen -#undef freopen -#undef fclose - -/********************* Using elib_malloc ****************************/ -/* This gives us yet another level of malloc wrappers. The purpouse */ -/* is to be able to select between different varieties of memory */ -/* allocation without recompiling. */ -/* Maybe the performance is somewhat degraded by this, but */ -/* on the other hand, performance may be much better if the most */ -/* suiting malloc is used (not to mention the much lower */ -/* fragmentation). */ -/* /Patrik N */ -/********************************************************************/ - -/* - * I don't want to include the whole elib header, especially - * as it uses char * for generic pointers. Let's fool ANSI C instead. - */ -extern void *elib_malloc(size_t); -extern void *elib_realloc(void *, size_t); -extern void elib_free(void *); -extern void elib_init(void *, int); -extern void elib_force_init(void *, int); -extern size_t elib_sizeof(void *); - -/* Flags */ -#define USING_ELIB_MALLOC 1 /* We are using the elib_malloc */ -#define WARN_MALLOC_MIX 2 /* Warn if plain malloc or save_malloc - is mixed with sys_free2 or - sys_realloc2 */ -#define REALLOC_MOVES 4 /* Always move on realloc - (less fragmentation) */ -#define USER_POOL 8 /* The user supplied the memory - pool, it was not save_alloced. */ -#define RECLAIM_USER_POOL 16 /* Use the reclaim mechanism in the - user pool. */ -#define NEW_USER_POOL 32 /* The user pool is newly suppllied, - any old pool should be discarded */ - - -#define ELIB_LOCK \ -if(alloc_flags & USING_ELIB_MALLOC) \ - semTake(elib_malloc_sem, WAIT_FOREVER) - -#define ELIB_UNLOCK \ -if(alloc_flags & USING_ELIB_MALLOC) \ - semGive(elib_malloc_sem) - -#define USER_RECLAIM() ((alloc_flags & USING_ELIB_MALLOC) && \ - (alloc_flags & USER_POOL) && \ - (alloc_flags & RECLAIM_USER_POOL)) - -/* - * Global state - * The use of function pointers for the malloc/realloc/free functions - * is actually only useful in the malloc case, we must know what kind of - * realloc/free we are going to use, so we could call elib_xxx directly. - * However, as the overhead is small and this construction makes it - * fairly easy to add another malloc algorithm, the function pointers - * are used in realloc/free to. - */ -static MallocFunction actual_alloc = &save_malloc; -static ReallocFunction actual_realloc = &save_realloc; -static FreeFunction actual_free = &save_free; -static int alloc_flags = 0; -static int alloc_pool_size = 0; -static void *alloc_pool_ptr = NULL; -static SEM_ID elib_malloc_sem = NULL; - -/* - * Descide if we should use the save_free instead of elib_free or, - * in the case of the free used in a delete hook, if we should - * use plain free instead of elib_free. - */ -static int use_save_free(void *ptr){ - register int diff = ((char *) ptr) - ((char *) alloc_pool_ptr); - /* - * Hmmm... should it be save_free even if diff is exactly 0? - * The answer is Yes if the whole area is save_alloced and No if not, - * so reclaim_free_hook is NOT run in the case of one save_alloced area. - */ - return (!(alloc_flags & USING_ELIB_MALLOC) || - (diff < 0 || diff >= alloc_pool_size)); -} - -/* - * A free function used by the task deletion hook for the save_xxx functions. - * Set with the set_reclaim_free_function function. - */ -static void reclaim_free_hook(void *ptr){ - if(use_save_free(ptr)){ - free(ptr); - } else { - ELIB_LOCK; - (*actual_free)(ptr); - ELIB_UNLOCK; - } -} - - -/* - * Initialize, sets the values of pointers based on - * either nothing (the default) or what's set previously by the - * erl_set_memory_block function. - */ -static void initialize_allocation(void){ - set_reclaim_free_function(NULL); - if(alloc_pool_size == 0){ - actual_alloc = (void *(*)(size_t))&save_malloc; - actual_realloc = (void *(*)(void *, size_t))&save_realloc; - actual_free = &save_free; - alloc_flags &= ~(USING_ELIB_MALLOC | USER_POOL | RECLAIM_USER_POOL); - } else { - if(elib_malloc_sem == NULL) - elib_malloc_sem = semMCreate - (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); - if(elib_malloc_sem == NULL) - erl_exit(1,"Could not create mutex semaphore for elib_malloc"); - if(!(alloc_flags & USER_POOL)){ - if((alloc_pool_ptr = save_malloc(alloc_pool_size)) == NULL) - erl_exit(1,"Erlang set to allocate a %d byte block initially;" - " not enough memory available.", alloc_pool_size); - elib_force_init(alloc_pool_ptr, alloc_pool_size); - } else if(alloc_flags & NEW_USER_POOL){ - elib_force_init(alloc_pool_ptr, alloc_pool_size); - } - actual_alloc=&elib_malloc; - actual_realloc=&elib_realloc; - actual_free=&elib_free; - alloc_flags |= USING_ELIB_MALLOC; - /* We MUST see to that the right free function is used - otherwise we'll get a very nasty crash! */ - if(USER_RECLAIM()) - set_reclaim_free_function(&reclaim_free_hook); - } - alloc_flags &= ~(NEW_USER_POOL); /* It's never new after initialization*/ -} - -/* This does not exist on other platforms, we just use it in sys.c - and the BSD resolver */ -void *sys_calloc2(Uint nelem, Uint elsize){ - void *ptr = erts_alloc_fnf(ERTS_ALC_T_UNDEF, nelem*elsize); - if(ptr != NULL) - memset(ptr,0,nelem*elsize); - return ptr; -} - -/* - * The malloc wrapper - */ -void * -erts_sys_alloc(ErtsAlcType_t type, void *extra, Uint size) -{ - register void *ret; - ELIB_LOCK; - if(USER_RECLAIM()) - ret = save_malloc2((size_t)size,actual_alloc); - else - ret = (*actual_alloc)((size_t)size); - ELIB_UNLOCK; - return ret; -} - -/* - * The realloc wrapper, may respond to the "realloc-always-moves" flag - * if the area is initially allocated with elib_malloc. - */ -void * -erts_sys_realloc(ErtsAlcType_t type, void *extra, void *ptr, Uint size) -{ - register void *ret; - if(use_save_free(ptr)){ - if((alloc_flags & WARN_MALLOC_MIX) && - (alloc_flags & USING_ELIB_MALLOC)) - erts_fprintf(stderr,"Warning, save_malloced data realloced " - "by sys_realloc2\n"); - return save_realloc(ptr, (size_t) size); - } else { - ELIB_LOCK; - if((alloc_flags & REALLOC_MOVES) && - (alloc_flags & USING_ELIB_MALLOC)){ - size_t osz = elib_sizeof(ptr); - if(USER_RECLAIM()) - ret = save_malloc2((size_t) size, actual_alloc); - else - ret = (*actual_alloc)((size_t) size); - if(ret != NULL){ - memcpy(ret,ptr,(((size_t)size) < osz) ? ((size_t)size) : osz); - if(USER_RECLAIM()) - save_free2(ptr,actual_free); - else - (*actual_free)(ptr); - } - } else { - if(USER_RECLAIM()) - ret = save_realloc2(ptr,(size_t)size,actual_realloc); - else - ret = (*actual_realloc)(ptr,(size_t)size); - } - ELIB_UNLOCK; - return ret; - } -} - -/* - * Wrapped free(). - */ -void -erts_sys_free(ErtsAlcType_t type, void *extra, void *ptr) -{ - if(use_save_free(ptr)){ - /* - * This might happen when linked in drivers use save_malloc etc - * directly. - */ - if((alloc_flags & WARN_MALLOC_MIX) && - (alloc_flags & USING_ELIB_MALLOC)) - erts_fprintf(stderr,"Warning, save_malloced data freed by " - "sys_free2\n"); - save_free(ptr); - } else { - ELIB_LOCK; - if(USER_RECLAIM()) - save_free2(ptr,actual_free); - else - (*actual_free)(ptr); - ELIB_UNLOCK; - } -} - -/* - * External interface to be called before erlang is started - * Parameters: - * isize: The size of the memory block where erlang should malloc(). - * iptr: (optional) A pointer to a user supplied memory block of - * size isize. - * warn_save: Instructs sys_free2 and sys_realloc2 to warn if - * memory allocation/reallocation/freeing is mixed between - * pure malloc/save_malloc/sys_alloc2 routines (only - * warns if elib is actually used in the sys_alloc2 routines). - * realloc_moves: Always allocate a fresh memory block on reallocation - * (less fragmentation). - * reclaim_in_supplied: Use memory reclaim mechanisms inside the user - * supplied area, this makes one area reusable between - * starts of erlang and might be nice for drivers etc. - */ - -int erl_set_memory_block(int isize, int iptr, int warn_save, - int realloc_moves, int reclaim_in_supplied, int p5, - int p6, int p7, int p8, int p9){ - if(erlang_id != 0){ - erts_fprintf(stderr,"Error, cannot set erlang memory block while an " - "erlang task is running!\n"); - return 1; - } - if(isize < 8 * 1024 *1024) - erts_fprintf(stderr, - "Warning, the memory pool of %dMb may be to small to " - "run erlang in!\n", isize / (1024 * 1024)); - alloc_pool_size = (size_t) isize; - alloc_pool_ptr = (void *) iptr; - alloc_flags = 0; - /* USING_ELIB_MALLOC gets set by the initialization routine */ - if((void *)iptr != NULL) - alloc_flags |= (USER_POOL | NEW_USER_POOL); - if(realloc_moves) - alloc_flags |= REALLOC_MOVES; - if(warn_save) - alloc_flags |= WARN_MALLOC_MIX; - if((void *)iptr != NULL && reclaim_in_supplied) - alloc_flags |= RECLAIM_USER_POOL; - return 0; -} - -/* External statistics interface */ -int erl_memory_show(int p0, int p1, int p2, int p3, int p4, int p5, - int p6, int p7, int p8, int p9){ - struct elib_stat statistics; - if(!(alloc_flags & USING_ELIB_MALLOC) && erlang_id != 0){ - erts_printf("Using plain save_alloc, use memShow instead.\n"); - return 1; - } - if(erlang_id == 0 && !((alloc_flags & USER_POOL) && - !(alloc_flags & NEW_USER_POOL))){ - erts_printf("Sorry, no allocation statistics until erlang " - "is started.\n"); - return 1; - } - erts_printf("Allocation settings:\n"); - erts_printf("Using elib_malloc with memory pool size of %lu bytes.\n", - (unsigned long) alloc_pool_size); - erts_printf("Realloc-always-moves is %s\n", - (alloc_flags & REALLOC_MOVES) ? "on" : "off"); - erts_printf("Warnings about mixed malloc/free's are %s\n", - (alloc_flags & WARN_MALLOC_MIX) ? "on" : "off"); - if(alloc_flags & USER_POOL){ - erts_printf("The memory block used by elib is user supplied " - "at 0x%08x.\n", (unsigned int) alloc_pool_ptr); - if(alloc_flags & RECLAIM_USER_POOL) - erts_printf("Allocated memory within the user supplied pool\n" - " will be automatically reclaimed at task exit.\n"); - } else { - erts_printf("The memory block used by elib is save_malloc'ed " - "at 0x%08x.\n", (unsigned int) alloc_pool_ptr); - } - erts_printf("Statistics from elib_malloc:\n"); - ELIB_LOCK; - - elib_stat(&statistics); - ELIB_UNLOCK; - erts_printf("Type Size (bytes) Number of blocks\n"); - erts_printf("============= ============ ================\n"); - erts_printf("Total: %12lu %16lu\n", - (unsigned long) statistics.mem_total*4, - (unsigned long) statistics.mem_blocks); - erts_printf("Allocated: %12lu %16lu\n", - (unsigned long) statistics.mem_alloc*4, - (unsigned long) statistics.mem_blocks-statistics.free_blocks); - erts_printf("Free: %12lu %16lu\n", - (unsigned long) statistics.mem_free*4, - (unsigned long) statistics.free_blocks); - erts_printf("Largest free: %12lu -\n\n", - (unsigned long) statistics.max_free*4); - return 0; -} - - -/* -** More programmer friendly (as opposed to user friendly ;-) interface -** to the memory statistics. Resembles the VxWorks memPartInfoGet but -** does not take a partition id as parameter... -*/ -int erl_mem_info_get(MEM_PART_STATS *stats){ - struct elib_stat statistics; - if(!(alloc_flags & USING_ELIB_MALLOC)) - return -1; - ELIB_LOCK; - elib_stat(&statistics); - ELIB_UNLOCK; - stats->numBytesFree = statistics.mem_free*4; - stats->numBlocksFree = statistics.free_blocks; - stats->maxBlockSizeFree = statistics.max_free*4; - stats->numBytesAlloc = statistics.mem_alloc*4; - stats->numBlocksAlloc = statistics.mem_blocks-statistics.free_blocks; - return 0; -} - -/********************* Pipe driver **********************************/ -/* - * Purpose: Pipe driver with Unix (unnamed) pipe semantics. - * Author: Peter Hogfeldt ([email protected]) from an outline - * by Per Hedeland ([email protected]). - * - * Note: This driver must *not* use the reclaim facilities, hence it - * is placed here. (after the #undef's of open,malloc etc) - * - * This driver supports select() and non-blocking I/O via - * ioctl(fd, FIONBIO, val). - * - * 1997-03-21 Peter Hogfeldt - * Added non-blocking I/O. - * - */ - -/* - * SEMAPHORES - * - * Each end of a pipe has two semaphores: semExcl for serialising access to - * the pipe end, and semBlock for blocking I/O. - * - * reader->semBlock is available (full) if and only if the pipe is - * not empty, or the write end is closed. Otherwise - * it is unavailable (empty). It is initially - * unavailable. - * - * writer->semBlock is available (full) if and only if the pipe is - * not full, or if the reader end is closed. - * Otherwise it is unavailable. It is initially - * available. - */ - -#define UXPIPE_SIZE 4096 - -/* Forward declaration */ -typedef struct uxPipeDev UXPIPE_DEV; - -/* - * Pipe descriptor (one for each open pipe). - */ -typedef struct { - int drvNum; - UXPIPE_DEV *reader, *writer; - RING_ID ringId; -} UXPIPE; - -/* - * Device descriptor (one for each of the read and write - * ends of an open pipe). - */ -struct uxPipeDev { - UXPIPE *pipe; - int blocking; - SEL_WAKEUP_LIST wakeupList; - SEM_ID semExcl; - SEM_ID semBlock; -}; - -int uxPipeDrvNum = 0; /* driver number of pipe driver */ - -#define PIPE_NAME "/uxpipe" /* only used internally */ -#define PIPE_READ "/r" /* ditto */ -#define PIPE_WRITE "/w" /* ditto */ - -LOCAL char pipeRead[64], pipeWrite[64]; -LOCAL DEV_HDR devHdr; -LOCAL UXPIPE *newPipe; /* communicate btwn open()s in pipe() */ -LOCAL SEM_ID pipeSem; /* mutual exclusion in pipe() */ - -/* forward declarations */ -LOCAL int uxPipeOpen(DEV_HDR *pDv, char *name, int mode); -LOCAL int uxPipeClose(UXPIPE_DEV *pDev); -LOCAL int uxPipeRead(UXPIPE_DEV *pDev, char *buffer, int maxbytes); -LOCAL int uxPipeWrite(UXPIPE_DEV *pDev, char *buffer, int nbytes); -LOCAL STATUS uxPipeIoctl(FAST UXPIPE_DEV *pDev, FAST int function, int arg); - - -/*************************************************************************** - * - * uxPipeDrv - install Unix pipe driver - * - * This routine initializes the Unix pipe driver. It must be called - * before any other routine in this driver. - * - * RETURNS: - * OK, or ERROR if I/O system is unable to install driver. - */ - -STATUS -uxPipeDrv(void) -{ - if (uxPipeDrvNum > 0) - return (OK); /* driver already installed */ - if ((uxPipeDrvNum = iosDrvInstall((FUNCPTR) NULL, (FUNCPTR) NULL, - uxPipeOpen, uxPipeClose, uxPipeRead, - uxPipeWrite, uxPipeIoctl)) == ERROR) - return (ERROR); - if (iosDevAdd(&devHdr, PIPE_NAME, uxPipeDrvNum) == ERROR) - return (ERROR); - strcpy(pipeRead, PIPE_NAME); - strcat(pipeRead, PIPE_READ); - strcpy(pipeWrite, PIPE_NAME); - strcat(pipeWrite, PIPE_WRITE); - if ((pipeSem = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE)) == NULL) - return (ERROR); - return (OK); -} - -/*************************************************************************** - * - * uxPipeOpen - open a pipe - * - * RETURNS: Pointer to device descriptor, or ERROR if memory cannot be - * allocated (errno = ENOMEM), or invalid argument (errno = EINVAL). - */ - -/* - * DEV_HDR *pDv; pointer to device header (dummy) - * char *name; name of pipe to open ("/r" or "/w") - * int mode; access mode (O_RDONLY or O_WRONLY) - */ -LOCAL int -uxPipeOpen(DEV_HDR *pDv, char *name, int mode) -{ - UXPIPE_DEV *reader, *writer; - - if (mode == O_RDONLY && strcmp(name, PIPE_READ) == 0) { - /* reader open */ - if ((newPipe = (UXPIPE *) malloc(sizeof(UXPIPE))) != NULL) { - if ((newPipe->ringId = rngCreate(UXPIPE_SIZE)) != NULL) { - if ((reader = (UXPIPE_DEV *) malloc(sizeof(UXPIPE_DEV))) != NULL) { - if ((reader->semExcl = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) { - if ((reader->semBlock = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) != NULL) { - reader->pipe = newPipe; - reader->blocking = 1; - selWakeupListInit(&reader->wakeupList); - newPipe->reader = reader; - newPipe->writer = NULL; - newPipe->drvNum = uxPipeDrvNum; - return ((int) reader); - } - semDelete(reader->semExcl); - } - free(reader); - } - rngDelete(newPipe->ringId); - } - free(newPipe); - newPipe = NULL; - errno = ENOMEM; - } - } else if (mode == O_WRONLY && strcmp(name, PIPE_WRITE) == 0) { - /* writer open */ - if (newPipe != NULL && - (writer = (UXPIPE_DEV *) malloc(sizeof(UXPIPE_DEV))) != NULL) { - if ((writer->semExcl = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) { - if ((writer->semBlock = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) { - writer->blocking = 1; - writer->pipe = newPipe; - selWakeupListInit(&writer->wakeupList); - newPipe->writer = writer; - newPipe = NULL; - return ((int) writer); - } - semDelete(writer->semExcl); - } - free(writer); - } - if (newPipe != NULL) - free(newPipe); - newPipe = NULL; - errno = ENOMEM; - } else { - errno = EINVAL; - } - return (ERROR); -} - -/*************************************************************************** - * - * uxPipeClose - close read or write end of a pipe. - * - * RETURNS: - * OK, or ERROR if device descriptor does not refer to an open read or - write end of a pipe (errno = EBADF). - */ - -LOCAL int -uxPipeClose(UXPIPE_DEV *pDev) -{ - UXPIPE *pajp = pDev->pipe; - - taskLock(); - if (pDev == pajp->reader) { - /* Close this end */ - semDelete(pDev->semExcl); - semDelete(pDev->semBlock); - free(pDev); - pajp->reader = NULL; - /* Inform the other end */ - if (pajp->writer != NULL) { - selWakeupAll(&pajp->writer->wakeupList, SELWRITE); - semGive(pajp->writer->semBlock); - } - } else if (pDev == pajp->writer) { - /* Close this end */ - semDelete(pDev->semExcl); - semDelete(pDev->semBlock); - free(pDev); - pajp->writer = NULL; - /* Inform the other end */ - if (pajp->reader != NULL) { - selWakeupAll(&pajp->reader->wakeupList, SELREAD); - semGive(pajp->reader->semBlock); - } - } else { - errno = EBADF; - taskUnlock(); - return (ERROR); - } - if (pajp->reader == NULL && pajp->writer == NULL) { - rngDelete(pajp->ringId); - pajp->drvNum = 0; - free(pajp); - } - taskUnlock(); - return (OK); -} -/*************************************************************************** - * - * uxPipeRead - read from a pipe. - * - * Reads at most maxbytes bytes from the pipe. Blocks if blocking mode is - * set and the pipe is empty. - * - * RETURNS: - * number of bytes read, 0 on EOF, or ERROR if device descriptor does - * not refer to an open read end of a pipe (errno = EBADF), or if - * non-blocking mode is set and the pipe is empty (errno = EWOULDBLOCK). - */ - -LOCAL int -uxPipeRead(UXPIPE_DEV *pDev, char *buffer, int maxbytes) -{ - UXPIPE *pajp = pDev->pipe; - int nbytes = 0; - - if (pDev != pajp->reader) { - errno = EBADF; - return (ERROR); - } - if (maxbytes == 0) - return (0); - semTake(pDev->semExcl, WAIT_FOREVER); - /* - * Note that semBlock may be full, although there is nothing to read. - * This happens e.g. after the following sequence of operations: a - * reader task blocks, a writer task writes two times (the first - * write unblocks the reader task, the second write makes semBlock - * full). - */ - while (nbytes == 0) { - if (pDev->blocking) - semTake(pDev->semBlock, WAIT_FOREVER); - /* - * Reading and updating of the write end must not be interleaved - * with a write from another task - hence we lock this task. - */ - taskLock(); - nbytes = rngBufGet(pajp->ringId, buffer, maxbytes); - if (nbytes > 0) { - /* Give own semaphore if bytes remain or if write end is closed */ - if ((!rngIsEmpty(pajp->ringId) || pajp->writer == NULL) && - pDev->blocking) - semGive(pDev->semBlock); - /* Inform write end */ - if (pajp->writer != NULL) { - if (pajp->writer->blocking) - semGive(pajp->writer->semBlock); - selWakeupAll(&pajp->writer->wakeupList, SELWRITE); - } - } else if (pajp->writer == NULL) { - nbytes = 0; /* EOF */ - /* Give semaphore when write end is closed */ - if (pDev->blocking) - semGive(pDev->semBlock); - taskUnlock(); - semGive(pDev->semExcl); - return (nbytes); - } else if (!pDev->blocking) { - taskUnlock(); - semGive(pDev->semExcl); - errno = EWOULDBLOCK; - return (ERROR); - } - taskUnlock(); - } - semGive(pDev->semExcl); - return (nbytes); -} - -/*************************************************************************** - * - * uxPipeWrite - write to a pipe. - * - * Writes nbytes bytes to the pipe. Blocks if blocking mode is set, and if - * the pipe is full. - * - * RETURNS: - * number of bytes written, or ERROR if the device descriptor does not - * refer to an open write end of a pipe (errno = EBADF); or if the read end - * of the pipe is closed (errno = EPIPE); or if non-blocking mode is set - * and the pipe is full (errno = EWOULDBLOCK). - * - */ - -LOCAL int -uxPipeWrite(UXPIPE_DEV *pDev, char *buffer, int nbytes) -{ - - UXPIPE *pajp = pDev->pipe; - int sofar = 0, written; - - if (pDev != pajp->writer) { - errno = EBADF; - return (ERROR); - } - if (pajp->reader == NULL) { - errno = EPIPE; - return (ERROR); - } - if (nbytes == 0) - return (0); - semTake(pDev->semExcl, WAIT_FOREVER); - while (sofar < nbytes) { - if (pDev->blocking) - semTake(pDev->semBlock, WAIT_FOREVER); - if (pajp->reader == NULL) { - errno = EPIPE; - semGive(pDev->semBlock); - semGive(pDev->semExcl); - return (ERROR); - } - /* Writing and updating of the read end must not be interleaved - * with a read from another task - hence we lock this task. - */ - taskLock(); - written = rngBufPut(pajp->ringId, buffer + sofar, nbytes - sofar); - sofar += written; - /* Inform the read end if we really wrote something */ - if (written > 0 && pajp->reader != NULL) { - selWakeupAll(&pajp->reader->wakeupList, SELREAD); - if (pajp->reader->blocking) - semGive(pajp->reader->semBlock); - } - taskUnlock(); - if (!pDev->blocking) { - if (sofar == 0) { - errno = EWOULDBLOCK; - sofar = ERROR; - } - break; - } - } - /* Give own semaphore if space remains */ - if (!rngIsFull(pajp->ringId) && pDev->blocking) - semGive(pDev->semBlock); - semGive(pDev->semExcl); - return (sofar); -} - -/*************************************************************************** - * - * uxPipeIoctl - do device specific I/O control - * - * RETURNS: - * OK or ERROR. - */ - -LOCAL STATUS -uxPipeIoctl(FAST UXPIPE_DEV *pDev, FAST int function, int arg) - -{ - UXPIPE *pajp = pDev->pipe; - int status = OK; - - switch (function) { - case FIONBIO: - pDev->blocking = (*(int *)arg) ? 0 : 1; - break; - case FIOSELECT: - taskLock(); - selNodeAdd(&pDev->wakeupList, (SEL_WAKEUP_NODE *) arg); - if (selWakeupType((SEL_WAKEUP_NODE *) arg) == SELREAD && - pDev == pajp->reader && - (!rngIsEmpty(pajp->ringId) || pajp->writer == NULL)) - selWakeup((SEL_WAKEUP_NODE *) arg); - if (selWakeupType((SEL_WAKEUP_NODE *) arg) == SELWRITE && - pDev == pajp->writer && - (!rngIsFull(pajp->ringId) || pajp->reader == NULL)) - selWakeup((SEL_WAKEUP_NODE *) arg); - taskUnlock(); - break; - case FIOUNSELECT: - selNodeDelete(&pDev->wakeupList, (SEL_WAKEUP_NODE *) arg); - break; - default: - status = ERROR; - break; - } - return (status); -} - -/*************************************************************************** - * - * pipe - create an intertask channel - * - * Creates a pipe. fd[0] (fd[1]) is the read (write) file descriptor. - * - * RETURNS: - * OK or ERROR, if the pipe could not be created. - */ - -STATUS -pipe(int fd[2]) -{ - semTake(pipeSem, WAIT_FOREVER); - if ((fd[0] = open(pipeRead, O_RDONLY, 0)) != ERROR) { - if ((fd[1] = open(pipeWrite, O_WRONLY, 0)) != ERROR) { - semGive(pipeSem); - return (OK); - } - (void) close(fd[0]); - } - errno &= 0xFFFF; - if((errno & 0xFFFF) == EINTR) /* Why on earth EINTR??? */ - errno = ENFILE; /* It means we are out of file descriptors...*/ - semGive(pipeSem); - return (ERROR); -} - -/*************************************************************************** - * - * uxPipeShow - display pipe information - * - * RETURNS: - * N/A. - */ - -void -uxPipeShow(int fd) -{ - UXPIPE_DEV *pDev; - UXPIPE *pajp; - int drvValue; - - if ((drvValue = iosFdValue(fd)) == ERROR) { - erts_fprintf(stderr, "Error: file descriptor invalid\n"); - return; - } - pDev = (UXPIPE_DEV *)drvValue; - pajp = pDev->pipe; - if (pajp->drvNum != uxPipeDrvNum) { - erts_fprintf(stderr, "Error: Not a ux pipe device\n"); - return; - } - erts_fprintf(stderr, "Device : 0x%x\n", (int) pDev); - erts_fprintf(stderr, "Buffer size : %d\n", UXPIPE_SIZE); - erts_fprintf(stderr, "Bytes in buffer : %d\n\n", rngNBytes(pajp->ringId)); - erts_fprintf(stderr, "READ END\n\n"); - if (pajp->reader != NULL) { - erts_fprintf(stderr, "Mode : "); - erts_fprintf(stderr, "%s\n", - (pajp->reader->blocking) ? "blocking" : "non-blocking"); - } - erts_fprintf(stderr, "Status : "); - if (pajp->reader != NULL) { - erts_fprintf(stderr, "OPEN\n"); - erts_fprintf(stderr, "Wake-up list : %d\n\n", - selWakeupListLen(&pajp->reader->wakeupList)); - erts_fprintf(stderr, "Exclusion Semaphore\n"); - semShow(pajp->reader->semExcl, 1); - erts_fprintf(stderr, "Blocking Semaphore\n"); - semShow(pajp->reader->semBlock, 1); - } else - erts_fprintf(stderr, "CLOSED\n\n"); - erts_fprintf(stderr, "WRITE END\n\n"); - if (pajp->writer != NULL) { - erts_fprintf(stderr, "Mode : "); - erts_fprintf(stderr, "%s\n", - (pajp->writer->blocking) ? "blocking" : "non-blocking"); - } - erts_fprintf(stderr, "Status : "); - if (pajp->writer != NULL) { - erts_fprintf(stderr, "OPEN\n"); - erts_fprintf(stderr, "Wake-up list : %d\n\n", - selWakeupListLen(&pajp->writer->wakeupList)); - erts_fprintf(stderr, "Exclusion Semaphore\n"); - semShow(pajp->writer->semExcl, 1); - erts_fprintf(stderr, "Blocking Semaphore\n"); - semShow(pajp->writer->semBlock, 1); - } else - erts_fprintf(stderr, "CLOSED\n\n"); -} - -#ifdef DEBUG -void -erl_assert_error(char* expr, char* file, int line) -{ - fflush(stdout); - fprintf(stderr, "Assertion failed: %s in %s, line %d\n", - expr, file, line); - fflush(stderr); - erl_crash_dump(file, line, "Assertion failed: %s\n", expr); - abort(); -} -void -erl_debug(char* fmt, ...) -{ - char sbuf[1024]; /* Temporary buffer. */ - va_list va; - - va_start(va, fmt); - vsprintf(sbuf, fmt, va); - va_end(va); - fprintf(stderr, "%s\n", sbuf); -} -#endif diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index b106f0932d..acbbfc2ce9 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -68,9 +68,9 @@ static int async_write_file(struct async_io* aio, LPVOID buf, DWORD numToWrite); static int get_overlapped_result(struct async_io* aio, LPDWORD pBytesRead, BOOL wait); static BOOL create_child_process(char *, HANDLE, HANDLE, - HANDLE, LPHANDLE, BOOL, - LPVOID, LPTSTR, unsigned, - char **, int *); + HANDLE, LPHANDLE, LPDWORD, BOOL, + LPVOID, LPTSTR, unsigned, + char **, int *); static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL); static int application_type(const char* originalName, char fullPath[MAX_PATH], BOOL search_in_path, BOOL handle_quotes, @@ -1136,6 +1136,7 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) HANDLE hChildStdin = INVALID_HANDLE_VALUE; /* Child's stdin. */ HANDLE hChildStdout = INVALID_HANDLE_VALUE; /* Child's stout. */ HANDLE hChildStderr = INVALID_HANDLE_VALUE; /* Child's sterr. */ + DWORD pid; int close_child_stderr = 0; DriverData* dp; /* Pointer to driver data. */ ErlDrvData retval = ERL_DRV_ERROR_GENERAL; /* Return value. */ @@ -1211,6 +1212,7 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) hChildStdout, hChildStderr, &dp->port_pid, + &pid, opts->hide_window, (LPVOID) envir, (LPTSTR) opts->wd, @@ -1254,6 +1256,9 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) #endif retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write, opts->exit_status); + if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) + /* We assume that this cannot generate a negative number */ + erts_port[port_num].os_pid = (SWord) pid; } if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) @@ -1397,7 +1402,8 @@ create_child_process HANDLE hStdin, /* The standard input handle for child. */ HANDLE hStdout, /* The standard output handle for child. */ HANDLE hStderr, /* The standard error handle for child. */ - LPHANDLE phPid, /* Pointer to variable to received PID. */ + LPHANDLE phPid, /* Pointer to variable to received Process handle. */ + LPDWORD pdwID, /* Pointer to variable to received Process ID */ BOOL hide, /* Hide the window unconditionally. */ LPVOID env, /* Environment for the child */ LPTSTR wd, /* Working dir for the child */ @@ -1629,7 +1635,8 @@ create_child_process } CloseHandle(piProcInfo.hThread); /* Necessary to avoid resource leak. */ *phPid = piProcInfo.hProcess; - + *pdwID = piProcInfo.dwProcessId; + if (applType == APPL_DOS) { WaitForSingleObject(hProcess, 50); } diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index a3dcbc4cf3..7af7e48bbd 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -47,6 +47,7 @@ MODULES= \ busy_port_SUITE \ call_trace_SUITE \ code_SUITE \ + code_parallel_load_SUITE \ crypto_SUITE \ ddll_SUITE \ decode_packet_SUITE \ @@ -137,10 +138,10 @@ TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) EMAKEFILE=Emakefile -TEST_SPEC_FILES = emulator.spec \ - emulator.spec.win \ - emulator.spec.vxworks \ - emulator.spec.ose +TEST_SPEC_FILES= emulator.spec \ + emulator.spec.win \ + emulator_bench.spec + # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -196,13 +197,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(EMAKEFILE) $(TEST_SPEC_FILES) \ - $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(NO_OPT_ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(NATIVE_ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(NO_OPT_ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(NATIVE_ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index a21b055596..e2442861c7 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -25,7 +25,9 @@ init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, display/1, display_huge/0, - types/1, + erl_bif_types/1,guard_bifs_in_erl_bif_types/1, + shadow_comments/1, + specs/1,improper_bif_stubs/1,auto_imports/1, t_list_to_existing_atom/1,os_env/1,otp_7526/1, binary_to_atom/1,binary_to_existing_atom/1, atom_to_binary/1,min_max/1, erlang_halt/1]). @@ -33,7 +35,9 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [types, t_list_to_existing_atom, os_env, otp_7526, + [erl_bif_types, guard_bifs_in_erl_bif_types, shadow_comments, + specs, improper_bif_stubs, auto_imports, + t_list_to_existing_atom, os_env, otp_7526, display, atom_to_binary, binary_to_atom, binary_to_existing_atom, min_max, erlang_halt]. @@ -70,7 +74,7 @@ display(doc) -> display(Config) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), {ok, Node} = test_server:start_node(display_huge_term,peer, - [{args, "-pa "++Pa}]), + [{args, "-pa \""++Pa++"\""}]), true = rpc:call(Node,?MODULE,display_huge,[]), test_server:stop_node(Node), ok. @@ -86,33 +90,20 @@ deeep(N,Acc) -> deeep(N) -> deeep(N,[hello]). +erl_bif_types(Config) when is_list(Config) -> + ensure_erl_bif_types_compiled(), -types(Config) when is_list(Config) -> - c:l(erl_bif_types), - case erlang:function_exported(erl_bif_types, module_info, 0) of - false -> - %% Fail cleanly. - ?line ?t:fail("erl_bif_types not compiled"); - true -> - types_1() - end. - -types_1() -> - ?line List0 = erlang:system_info(snifs), + List0 = erlang:system_info(snifs), %% Ignore missing type information for hipe BIFs. - ?line List = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs], + List = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs], - case [MFA || MFA <- List, not known_types(MFA)] of - [] -> - types_2(List); - BadTypes -> - io:put_chars("No type information:\n"), - io:format("~p\n", [lists:sort(BadTypes)]), - ?line ?t:fail({length(BadTypes),bifs_without_types}) - end. + KnownTypes = [MFA || MFA <- List, known_types(MFA)], + io:format("There are ~p BIFs with type information in erl_bif_types.", + [length(KnownTypes)]), + erl_bif_types_2(KnownTypes). -types_2(List) -> +erl_bif_types_2(List) -> BadArity = [MFA || {M,F,A}=MFA <- List, begin Types = erl_bif_types:arg_types(M, F, A), @@ -120,14 +111,14 @@ types_2(List) -> end], case BadArity of [] -> - types_3(List); + erl_bif_types_3(List); [_|_] -> io:put_chars("Bifs with bad arity\n"), io:format("~p\n", [BadArity]), ?line ?t:fail({length(BadArity),bad_arity}) end. -types_3(List) -> +erl_bif_types_3(List) -> BadSmokeTest = [MFA || {M,F,A}=MFA <- List, begin try erl_bif_types:type(M, F, A) of @@ -151,9 +142,220 @@ types_3(List) -> ?line ?t:fail({length(BadSmokeTest),bad_smoke_test}) end. +guard_bifs_in_erl_bif_types(_Config) -> + ensure_erl_bif_types_compiled(), + + List0 = erlang:system_info(snifs), + List = [{F,A} || {erlang,F,A} <- List0, + erl_internal:guard_bif(F, A)], + Not = [FA || {F,A}=FA <- List, + not erl_bif_types:is_known(erlang, F, A)], + case Not of + [] -> + ok; + [_|_] -> + io:put_chars( + ["Dialyzer requires that all guard BIFs " + "have type information in erl_bif_types.\n\n" + "The following guard BIFs have no type information " + "in erl_bif_types:\n\n", + [io_lib:format(" ~p/~p\n", [F,A]) || {F,A} <- Not]]), + ?t:fail() + end. + +shadow_comments(_Config) -> + ensure_erl_bif_types_compiled(), + + List0 = erlang:system_info(snifs), + List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs], + List = [MFA || MFA <- List1, not is_operator(MFA)], + HasTypes = [MFA || {M,F,A}=MFA <- List, + erl_bif_types:is_known(M, F, A)], + Path = get_code_path(), + BifRel = sofs:relation(HasTypes, [{m,f,a}]), + BifModules = sofs:to_external(sofs:projection(1, BifRel)), + AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules], + Specs0 = [extract_specs(Mod, Abstr) || + {Mod,Abstr} <- AbstrByModule], + Specs = lists:append(Specs0), + SpecFuns0 = [F || {F,_} <- Specs], + SpecFuns = sofs:relation(SpecFuns0, [{m,f,a}]), + HasTypesAndSpecs = sofs:intersection(BifRel, SpecFuns), + Commented0 = lists:append([extract_comments(Mod, Path) || + Mod <- BifModules]), + Commented = sofs:relation(Commented0, [{m,f,a}]), + {NoComments0,_,NoBifSpecs0} = + sofs:symmetric_partition(HasTypesAndSpecs, Commented), + NoComments = sofs:to_external(NoComments0), + NoBifSpecs = sofs:to_external(NoBifSpecs0), + + case NoComments of + [] -> + ok; + [_|_] -> + io:put_chars( + ["If a BIF stub has both a spec and has type information in " + "erl_bif_types, there *must*\n" + "be a comment in the source file to make that immediately " + "obvious.\n\nThe following comments are missing:\n\n", + [io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n", + [M,F,A]) || {M,F,A} <- NoComments]]), + ?t:fail() + end, + + case NoBifSpecs of + [] -> + ok; + [_|_] -> + io:put_chars( + ["The following functions have \"shadowed\" comments " + "claiming that there is type information in erl_bif_types,\n" + "but actually there is no such type information.\n\n" + "Therefore, the following comments should be removed:\n\n", + [io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n", + [M,F,A]) || {M,F,A} <- NoBifSpecs]]), + ?t:fail() + end. + +extract_comments(Mod, Path) -> + Beam = which(Mod, Path), + SrcDir = filename:join(filename:dirname(filename:dirname(Beam)), "src"), + Src = filename:join(SrcDir, atom_to_list(Mod) ++ ".erl"), + {ok,Bin} = file:read_file(Src), + Lines0 = binary:split(Bin, <<"\n">>, [global]), + Lines1 = [T || <<"%% Shadowed by erl_bif_types: ",T/binary>> <- Lines0], + {ok,ReMFA} = re:compile("([^:]*):([^/]*)/(\\d*)"), + Lines = [L || L <- Lines1, re:run(L, ReMFA, [{capture,[]}]) =:= match], + [begin + {match,[M,F,A]} = re:run(L, ReMFA, [{capture,all_but_first,list}]), + {list_to_atom(M),list_to_atom(F),list_to_integer(A)} + end || L <- Lines]. + +ensure_erl_bif_types_compiled() -> + c:l(erl_bif_types), + case erlang:function_exported(erl_bif_types, module_info, 0) of + false -> + %% Fail cleanly. + ?t:fail("erl_bif_types not compiled"); + true -> + ok + end. + known_types({M,F,A}) -> erl_bif_types:is_known(M, F, A). +specs(_) -> + List0 = erlang:system_info(snifs), + + %% Ignore missing type information for hipe BIFs. + List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs], + + %% Ignore all operators. + List = [MFA || MFA <- List1, not is_operator(MFA)], + + %% Extract specs from the abstract code for all BIFs. + Path = get_code_path(), + BifRel = sofs:relation(List, [{m,f,a}]), + BifModules = sofs:to_external(sofs:projection(1, BifRel)), + AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules], + Specs0 = [extract_specs(Mod, Abstr) || + {Mod,Abstr} <- AbstrByModule], + Specs = lists:append(Specs0), + BifSet = sofs:set(List, [function]), + SpecRel0 = sofs:relation(Specs, [{function,spec}]), + SpecRel = sofs:restriction(SpecRel0, BifSet), + + %% Find BIFs without specs. + NoSpecs0 = sofs:difference(BifSet, sofs:domain(SpecRel)), + NoSpecs = sofs:to_external(NoSpecs0), + case NoSpecs of + [] -> + ok; + [_|_] -> + io:put_chars("The following BIFs don't have specs:\n"), + [print_mfa(MFA) || MFA <- NoSpecs], + ?t:fail() + end. + +is_operator({erlang,F,A}) -> + erl_internal:arith_op(F, A) orelse + erl_internal:bool_op(F, A) orelse + erl_internal:comp_op(F, A) orelse + erl_internal:list_op(F, A) orelse + erl_internal:send_op(F, A); +is_operator(_) -> false. + +extract_specs(M, Abstr) -> + [{make_mfa(M, Name),Spec} || {attribute,_,spec,{Name,Spec}} <- Abstr]. + +make_mfa(M, {F,A}) -> {M,F,A}; +make_mfa(M, {M,_,_}=MFA) -> MFA. + +improper_bif_stubs(_) -> + Bifs0 = erlang:system_info(snifs), + Bifs = [MFA || {M,_,_}=MFA <- Bifs0, M =/= hipe_bifs], + Path = get_code_path(), + BifRel = sofs:relation(Bifs, [{m,f,a}]), + BifModules = sofs:to_external(sofs:projection(1, BifRel)), + AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules], + Funcs0 = [extract_functions(Mod, Abstr) || + {Mod,Abstr} <- AbstrByModule], + Funcs = lists:append(Funcs0), + BifSet = sofs:set(Bifs, [function]), + FuncRel0 = sofs:relation(Funcs, [{function,code}]), + FuncRel = sofs:restriction(FuncRel0, BifSet), + [check_stub(MFA, Body) || {MFA,Body} <- sofs:to_external(FuncRel)], + ok. + +auto_imports(_Config) -> + Path = get_code_path(), + {erlang,Abstr} = extract_abstract(erlang, Path), + SpecFuns = [Name || {attribute,_,spec,{Name,_}} <- Abstr], + auto_imports(SpecFuns, 0). + +auto_imports([{F,A}|T], Errors) -> + case erl_internal:bif(F, A) of + false -> + io:format("~p/~p: not auto-imported, but spec claims it " + "is auto-imported", [F,A]), + auto_imports(T, Errors+1); + true -> + auto_imports(T, Errors) + end; +auto_imports([{erlang,F,A}|T], Errors) -> + case erl_internal:bif(F, A) of + false -> + auto_imports(T, Errors); + true -> + io:format("~p/~p: auto-imported, but " + "spec claims it is *not* auto-imported", [F,A]), + auto_imports(T, Errors+1) + end; +auto_imports([], 0) -> + ok; +auto_imports([], Errors) -> + ?t:fail({Errors,inconsistencies}). + +extract_functions(M, Abstr) -> + [{{M,F,A},Body} || {function,_,F,A,Body} <- Abstr]. + +check_stub({erlang,apply,3}, _) -> + ok; +check_stub({_,F,A}, B) -> + try + [{clause,_,Args,[],Body}] = B, + A = length(Args), + [{call,_,{remote,_,{atom,_,erlang},{atom,_,nif_error}},[_]}] = Body + catch + _:_ -> + io:put_chars("Invalid body for the following BIF stub:\n"), + Func = {function,0,F,A,B}, + io:put_chars(erl_pp:function(Func)), + io:nl(), + io:put_chars("The body should be: erlang:nif_error(undef)"), + ?t:fail() + end. + t_list_to_existing_atom(Config) when is_list(Config) -> ?line all = list_to_existing_atom("all"), ?line ?MODULE = list_to_existing_atom(?MODULE_STRING), @@ -483,6 +685,35 @@ erlang_halt(Config) when is_list(Config) -> id(I) -> I. +%% Get code path, including the path for the erts application. +get_code_path() -> + case code:lib_dir(erts) of + {error,bad_name} -> + Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]), + [Erts|code:get_path()]; + _ -> + code:get_path() + end. + +which(Mod, Path) -> + which_1(atom_to_list(Mod) ++ ".beam", Path). + +which_1(Base, [D|Ds]) -> + Path = filename:join(D, Base), + case filelib:is_regular(Path) of + true -> Path; + false -> which_1(Base, Ds) + end. +print_mfa({M,F,A}) -> + io:format("~p:~p/~p", [M,F,A]). + +extract_abstract(Mod, Path) -> + Beam = which(Mod, Path), + {ok,{Mod,[{abstract_code,{raw_abstract_v1,Abstr}}]}} = + beam_lib:chunks(Beam, [abstract_code]), + {Mod,Abstr}. + + hostname() -> hostname(atom_to_list(node())). diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index d9fc876482..c9f43bb00b 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1135,15 +1135,8 @@ sleeper() -> ?line receive after infinity -> ok end. -gc_test(doc) -> "Test that binaries are garbage collected properly."; -gc_test(suite) -> []; +%% Test that binaries are garbage collected properly. gc_test(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - private -> gc_test_1(); - hybrid -> {skip,"Hybrid heap"} - end. - -gc_test_1() -> %% Note: This test is only relevant for REFC binaries. %% Therefore, we take care that all binaries are REFC binaries. B = list_to_binary(lists:seq(0, ?heap_binary_size)), diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 7fdf36711b..dea02b1f4b 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -460,7 +460,6 @@ mem_leak(0, _) -> ok; mem_leak(N, B) -> ?line big_bin(B, <<23>>), ?line {'EXIT',{badarg,_}} = (catch big_bin(B, bad)), - maybe_gc(), mem_leak(N-1, B). big_bin(B1, B2) -> @@ -473,13 +472,6 @@ big_bin(B1, B2) -> make_bin(0, Acc) -> Acc; make_bin(N, Acc) -> make_bin(N-1, <<Acc/binary,Acc/binary>>). -maybe_gc() -> - case erlang:system_info(heap_type) of - shared -> erlang:garbage_collect(); - hybrid -> erlang:garbage_collect(); - private -> ok - end. - -define(COF(Int0), ?line (fun(Int) -> true = <<Int:32/float>> =:= <<(float(Int)):32/float>>, diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index 9d80b01748..eaecd32f95 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -25,6 +25,7 @@ init_per_testcase/2,end_per_testcase/2, hipe/1,process_specs/1,basic/1,flags/1,errors/1,pam/1,change_pam/1, return_trace/1,exception_trace/1,on_load/1,deep_exception/1, + upgrade/1, exception_nocatch/1,bit_syntax/1]). %% Helper functions. @@ -46,6 +47,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> Common = [errors, on_load], NotHipe = [process_specs, basic, flags, pam, change_pam, + upgrade, return_trace, exception_trace, deep_exception, exception_nocatch, bit_syntax], Hipe = [hipe], @@ -76,7 +78,13 @@ init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> end_per_testcase(_Func, Config) -> Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog). + ?t:timetrap_cancel(Dog), + + %% Reloading the module will clear all trace patterns, and + %% in a debug-compiled emulator run assertions of the counters + %% for the number of traced exported functions in this module. + + c:l(?MODULE). hipe(Config) when is_list(Config) -> ?line 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true), @@ -185,8 +193,13 @@ basic() -> %% Trace some functions... ?line trace_func({lists,'_','_'}, []), + + %% Make sure that tracing the same functions more than once + %% does not cause any problems. + ?line 3 = trace_func({?MODULE,foo,'_'}, true), ?line 3 = trace_func({?MODULE,foo,'_'}, true), ?line 1 = trace_func({?MODULE,bar,0}, true), + ?line 1 = trace_func({?MODULE,bar,0}, true), ?line {traced,global} = trace_info({?MODULE,bar,0}, traced), ?line 1 = trace_func({erlang,list_to_integer,1}, true), ?line {traced,global} = trace_info({erlang,list_to_integer,1}, traced), @@ -267,6 +280,118 @@ foo() -> foo0. foo(X) -> X+1. foo(X, Y) -> X+Y. + +%% Note that the semantics that this test case verifies +%% are not explicitly specified in the docs (what I could find in R15B). +%% This test case was written to verify that we do not change +%% any behaviour with the introduction of "block-free" upgrade in R16. +%% In short: Do not refer to this test case as an authority of how it must work. +upgrade(doc) -> + "Test tracing on module being upgraded"; +upgrade(Config) when is_list(Config) -> + V1 = compile_version(my_upgrade_test, 1, Config), + V2 = compile_version(my_upgrade_test, 2, Config), + start_tracer(), + upgrade_do(V1, V2, false), + upgrade_do(V1, V2, true). + +upgrade_do(V1, V2, TraceLocalVersion) -> + {module,my_upgrade_test} = erlang:load_module(my_upgrade_test, V1), + + + %% Test that trace is cleared after load_module + + trace_func({my_upgrade_test,'_','_'}, [], [global]), + case TraceLocalVersion of + true -> trace_func({my_upgrade_test,local_version,0}, [], [local]); + _ -> ok + end, + 1 = my_upgrade_test:version(), + 1 = my_upgrade_test:do_local(), + 1 = my_upgrade_test:do_real_local(), + put('F1_exp', my_upgrade_test:make_fun_exp()), + put('F1_loc', my_upgrade_test:make_fun_local()), + 1 = (get('F1_exp'))(), + 1 = (get('F1_loc'))(), + + Self = self(), + expect({trace,Self,call,{my_upgrade_test,version,[]}}), + expect({trace,Self,call,{my_upgrade_test,do_local,[]}}), + expect({trace,Self,call,{my_upgrade_test,do_real_local,[]}}), + case TraceLocalVersion of + true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); + _ -> ok + end, + expect({trace,Self,call,{my_upgrade_test,make_fun_exp,[]}}), + expect({trace,Self,call,{my_upgrade_test,make_fun_local,[]}}), + expect({trace,Self,call,{my_upgrade_test,version,[]}}), % F1_exp + case TraceLocalVersion of + true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F1_loc + _ -> ok + end, + + {module,my_upgrade_test} = erlang:load_module(my_upgrade_test, V2), + 2 = my_upgrade_test:version(), + put('F2_exp', my_upgrade_test:make_fun_exp()), + put('F2_loc', my_upgrade_test:make_fun_local()), + 2 = (get('F1_exp'))(), + 1 = (get('F1_loc'))(), + 2 = (get('F2_exp'))(), + 2 = (get('F2_loc'))(), + expect(), + + put('F1_exp', undefined), + put('F1_loc', undefined), + erlang:garbage_collect(), + erlang:purge_module(my_upgrade_test), + + % Test that trace is cleared after delete_module + + trace_func({my_upgrade_test,'_','_'}, [], [global]), + case TraceLocalVersion of + true -> trace_func({my_upgrade_test,local_version,0}, [], [local]); + _ -> ok + end, + 2 = my_upgrade_test:version(), + 2 = my_upgrade_test:do_local(), + 2 = my_upgrade_test:do_real_local(), + 2 = (get('F2_exp'))(), + 2 = (get('F2_loc'))(), + + expect({trace,Self,call,{my_upgrade_test,version,[]}}), + expect({trace,Self,call,{my_upgrade_test,do_local,[]}}), + expect({trace,Self,call,{my_upgrade_test,do_real_local,[]}}), + case TraceLocalVersion of + true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); + _ -> ok + end, + expect({trace,Self,call,{my_upgrade_test,version,[]}}), % F2_exp + case TraceLocalVersion of + true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F2_loc + _ -> ok + end, + + true = erlang:delete_module(my_upgrade_test), + {'EXIT',{undef,_}} = (catch my_upgrade_test:version()), + {'EXIT',{undef,_}} = (catch ((get('F2_exp'))())), + 2 = (get('F2_loc'))(), + expect(), + + put('F2_exp', undefined), + put('F2_loc', undefined), + erlang:garbage_collect(), + erlang:purge_module(my_upgrade_test), + ok. + +compile_version(Module, Version, Config) -> + Data = ?config(data_dir, Config), + File = filename:join(Data, atom_to_list(Module)), + {ok,Module,Bin} = compile:file(File, [{d,'VERSION',Version}, + binary,report]), + Bin. + + + %% Test flags (arity, timestamp) for call_trace/3. %% Also, test the '{tracer,Pid}' option. flags(_Config) -> @@ -1151,11 +1276,13 @@ trace_info(What, Key) -> Res. trace_func(MFA, MatchSpec) -> - get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA, MatchSpec]}}, + trace_func(MFA, MatchSpec, []). +trace_func(MFA, MatchSpec, Flags) -> + get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA, MatchSpec, Flags]}}, Res = receive {apply_result,Result} -> Result end, - ok = io:format("trace_pattern(~p, ~p) -> ~p", [MFA,MatchSpec,Res]), + ok = io:format("trace_pattern(~p, ~p, ~p) -> ~p", [MFA,MatchSpec,Flags,Res]), Res. trace_pid(Pid, On, Flags) -> diff --git a/erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl b/erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl new file mode 100644 index 0000000000..11b8a95209 --- /dev/null +++ b/erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl @@ -0,0 +1,26 @@ +-module(my_upgrade_test). + +-export([version/0]). +-export([do_local/0]). +-export([do_real_local/0]). +-export([make_fun_exp/0]). +-export([make_fun_local/0]). + + +version() -> + ?VERSION. + +do_local() -> + version(). + +do_real_local() -> + local_version(). + +local_version() -> + ?VERSION. + +make_fun_exp() -> + fun() -> ?MODULE:version() end. + +make_fun_local() -> + fun() -> local_version() end. diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 25ce94096f..74ce5e397a 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -135,14 +135,7 @@ new_binary_types(Config) when is_list(Config) -> bit_sized_binary(Bin))), ok. -t_check_process_code(doc) -> "Test check_process_code/2."; t_check_process_code(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - private -> t_check_process_code_1(Config); - hybrid -> {skip,"Hybrid heap"} - end. - -t_check_process_code_1(Config) -> ?line Priv = ?config(priv_dir, Config), ?line Data = ?config(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), @@ -247,12 +240,10 @@ gc1() -> ok. t_check_process_code_ets(doc) -> "Test check_process_code/2 in combination with a fun obtained from an ets table."; t_check_process_code_ets(Config) when is_list(Config) -> - case {test_server:is_native(?MODULE),erlang:system_info(heap_type)} of - {true,_} -> - {skipped,"Native code"}; - {_,hybrid} -> - {skipped,"Hybrid heap"}; - {false,private} -> + case test_server:is_native(?MODULE) of + true -> + {skip,"Native code"}; + false -> do_check_process_code_ets(Config) end. @@ -397,9 +388,7 @@ module_md5_ok(Code) -> make_stub(Config) when is_list(Config) -> - %% No old code to purge if hybrid heap because of skipped test cases, - %% so we'll need a catch here. - ?line (catch erlang:purge_module(my_code_test)), + catch erlang:purge_module(my_code_test), ?line Data = ?config(data_dir, Config), ?line File = filename:join(Data, "my_code_test"), @@ -433,9 +422,7 @@ make_stub(Config) when is_list(Config) -> ok. make_stub_many_funs(Config) when is_list(Config) -> - %% No old code to purge if hybrid heap because of skipped test cases, - %% so we'll need a catch here. - ?line (catch erlang:purge_module(many_funs)), + catch erlang:purge_module(many_funs), ?line Data = ?config(data_dir, Config), ?line File = filename:join(Data, "many_funs"), diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl new file mode 100644 index 0000000000..aa9e4c96c6 --- /dev/null +++ b/erts/emulator/test/code_parallel_load_SUITE.erl @@ -0,0 +1,198 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% Author: Björn-Egil Dahlberg + +-module(code_parallel_load_SUITE). +-export([ + all/0, + suite/0, + init_per_suite/1, + end_per_suite/1, + init_per_testcase/2, + end_per_testcase/2 + ]). + +-export([ + multiple_load_check_purge_repeat/1, + many_load_distributed_only_once/1 + ]). + +-define(model, code_parallel_load_SUITE_model). +-define(interval, 50). +-define(number_of_processes, 160). +-define(passes, 4). + + +-include_lib("test_server/include/test_server.hrl"). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + multiple_load_check_purge_repeat, + many_load_distributed_only_once + ]. + + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> + Dog=?t:timetrap(?t:minutes(3)), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Func, Config) -> + SConf = ?config(save_config, Config), + Pids = proplists:get_value(purge_pids, SConf), + + case check_old_code(?model) of + true -> check_and_purge_processes_code(Pids, ?model); + _ -> ok + end, + case erlang:delete_module(?model) of + true -> check_and_purge_processes_code(Pids, ?model); + _ -> ok + end, + Dog=?config(watchdog, Config), + ?t:timetrap_cancel(Dog). + + +multiple_load_check_purge_repeat(_Conf) -> + Ts = [v1,v2,v3,v4,v5,v6], + + %% generate code that receives a token, code switches to new code + %% then matches this token against a literal code token + %% should be identical + %% (smoke test for parallel code loading + Codes = [{T, generate(?model, [], [ + format("check(T) -> receive {_Pid, change, T1} -> " + " ~w:check(T1)\n" + " after 0 -> T = f(), check(T) end.\n", [?model]), + format("f() -> ~w.~n", [T]) + ])} || T <- Ts], + + Pids = setup_code_changer(Codes), + {save_config, [{purge_pids,Pids}]}. + +setup_code_changer([{Token,Code}|Cs] = Codes) -> + {module, ?model} = erlang:load_module(?model,Code), + Pids = setup_checkers(Token,?number_of_processes), + code_changer(Cs, Codes, ?interval,Pids,?passes), + Pids. + +code_changer(_, _, _, Pids, 0) -> + [unlink(Pid) || Pid <- Pids], + [exit(Pid, die) || Pid <- Pids], + io:format("done~n"), + ok; +code_changer([], Codes, T, Pids, Ps) -> + code_changer(Codes, Codes, T, Pids, Ps - 1); +code_changer([{Token,Code}|Cs], Codes, T, Pids, Ps) -> + receive after T -> + io:format("load code with token ~4w : pass ~4w~n", [Token, Ps]), + {module, ?model} = erlang:load_module(?model, Code), + % this is second time we call load_module for this module + % so it should have old code + [Pid ! {self(), change, Token} || Pid <- Pids], + % should we wait a moment or just blantantly try to check and purge repeatadly? + receive after 1 -> ok end, + ok = check_and_purge_processes_code(Pids, ?model), + code_changer(Cs, Codes, T, Pids, Ps) + end. + + + +many_load_distributed_only_once(_Conf) -> + Ts = [<<"first version">>, <<"second version">>], + + [{Token1,Code1},{Token2, Code2}] = [{T, generate(?model, [], [ + "check({<<\"second version\">> = V, Pid}) -> V = f(), Pid ! {self(), completed, V}, ok;\n" ++ + format("check(T) -> receive {Pid, change, T1, B} -> " + " Res = erlang:load_module(~w, B), Pid ! {self(), change, Res},\n" + " ~w:check({T1, Pid})\n" + " after 0 -> T = f(), check(T) end.\n", [?model, ?model]), + format("f() -> ~w.~n", [T]) + ])} || T <- Ts], + + + {module, ?model} = erlang:load_module(?model, Code1), + Pids = setup_checkers(Token1,?number_of_processes), + + receive after 1000 -> ok end, % give 'em some time to spin up + [Pid ! {self(), change, Token2, Code2} || Pid <- Pids], + Loads = [receive {Pid, change, Res} -> Res end || Pid <- Pids], + [receive {Pid, completed, Token2} -> ok end || Pid <- Pids], + + ok = ensure_only_one_load(Loads, 0), + {save_config, [{purge_pids,Pids}]}. + +ensure_only_one_load([], 1) -> ok; +ensure_only_one_load([], _) -> too_many_loads; +ensure_only_one_load([{module, ?model}|Loads], N) -> + ensure_only_one_load(Loads, N + 1); +ensure_only_one_load([{error, not_purged}|Loads], N) -> + ensure_only_one_load(Loads, N). +% no other return values are allowed from load_module + + +%% aux + +setup_checkers(_,0) -> []; +setup_checkers(T,N) -> [spawn_link(fun() -> ?model:check(T) end) | setup_checkers(T, N-1)]. + +check_and_purge_processes_code(Pids, M) -> + check_and_purge_processes_code(Pids, M, []). +check_and_purge_processes_code([], M, []) -> + erlang:purge_module(M), + ok; +check_and_purge_processes_code([], M, Pending) -> + io:format("Processes ~w are still executing old code - retrying.~n", [Pending]), + check_and_purge_processes_code(Pending, M, []); +check_and_purge_processes_code([Pid|Pids], M, Pending) -> + case erlang:check_process_code(Pid, M) of + false -> + check_and_purge_processes_code(Pids, M, Pending); + true -> + check_and_purge_processes_code(Pids, M, [Pid|Pending]) + end. + + +generate(Module, Attributes, FunStrings) -> + FunForms = function_forms(FunStrings), + Forms = [ + {attribute,1,module,Module}, + {attribute,2,export,[FA || {FA,_} <- FunForms]} + ] ++ [{attribute, 3, A, V}|| {A, V} <- Attributes] ++ + [ Function || {_, Function} <- FunForms], + {ok, Module, Bin} = compile:forms(Forms), + Bin. + + +function_forms([]) -> []; +function_forms([S|Ss]) -> + {ok, Ts,_} = erl_scan:string(S), + {ok, Form} = erl_parse:parse_form(Ts), + Fun = element(3, Form), + Arity = element(4, Form), + [{{Fun,Arity}, Form}|function_forms(Ss)]. + +format(F,Ts) -> lists:flatten(io_lib:format(F, Ts)). diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 08308629fe..c88218c176 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -98,19 +98,6 @@ end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Dog=?config(watchdog, Config), ?t:timetrap_cancel(Dog). -%%% Don't be too hard on vxworks, the cross server gets nodedown -%%% cause the card is too busy if we don't sleep a little between pings. -sleep() -> - case os:type() of - vxworks -> - receive - after 10 -> - ok - end; - _ -> - ok - end. - ping(doc) -> ["Tests pinging a node in different ways."]; ping(Config) when is_list(Config) -> @@ -122,23 +109,21 @@ ping(Config) when is_list(Config) -> ?line Host = hostname(), ?line BadName = list_to_atom("__pucko__@" ++ Host), ?line io:format("Pinging ~s (assumed to not exist)", [BadName]), - ?line test_server:do_times(Times, - fun() -> pang = net_adm:ping(BadName), - sleep() + ?line test_server:do_times(Times, fun() -> pang = net_adm:ping(BadName) end), %% Pings another node. ?line {ok, OtherNode} = start_node(distribution_SUITE_other), ?line io:format("Pinging ~s (assumed to exist)", [OtherNode]), - ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(OtherNode),sleep() end), + ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(OtherNode) end), ?line stop_node(OtherNode), %% Pings our own node many times. ?line Node = node(), ?line io:format("Pinging ~s (the same node)", [Node]), - ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(Node),sleep() end), + ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(Node) end), ok. @@ -1890,7 +1875,7 @@ start_node(Name, Args, Rel) when is_atom(Name), is_list(Rel) -> end, test_server:start_node(Name, slave, [{args, - Args++" -setcookie "++Cookie++" -pa "++Pa} + Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""} | RelArg]); start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) -> Name = list_to_atom((atom_to_list(?MODULE) diff --git a/erts/emulator/test/driver_SUITE_data/chkio_drv.c b/erts/emulator/test/driver_SUITE_data/chkio_drv.c index 40f1ad4fea..faf1040276 100644 --- a/erts/emulator/test/driver_SUITE_data/chkio_drv.c +++ b/erts/emulator/test/driver_SUITE_data/chkio_drv.c @@ -17,7 +17,7 @@ */ #ifndef UNIX -#if !defined(__WIN32__) && !defined(VXWORKS) +#if !defined(__WIN32__) #define UNIX 1 #endif #endif diff --git a/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c b/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c index e6a3edcd74..1e107309df 100644 --- a/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c +++ b/erts/emulator/test/driver_SUITE_data/io_ready_exit_drv.c @@ -17,7 +17,7 @@ */ #ifndef UNIX -#if !defined(__WIN32__) && !defined(VXWORKS) +#if !defined(__WIN32__) #define UNIX 1 #endif #endif diff --git a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c index b2cc1e785a..d174771629 100644 --- a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c +++ b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c @@ -29,7 +29,7 @@ */ #ifndef UNIX -#if !defined(__WIN32__) && !defined(VXWORKS) +#if !defined(__WIN32__) #define UNIX 1 #endif #endif diff --git a/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c b/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c index e7d9a294fa..851f2c745b 100644 --- a/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c +++ b/erts/emulator/test/driver_SUITE_data/missing_callback_drv.c @@ -17,7 +17,7 @@ */ #ifndef UNIX -#if !defined(__WIN32__) && !defined(VXWORKS) +#if !defined(__WIN32__) #define UNIX 1 #endif #endif diff --git a/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c b/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c index 8e203f74ec..0c86a26604 100644 --- a/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c +++ b/erts/emulator/test/driver_SUITE_data/peek_non_existing_queue_drv.c @@ -28,7 +28,7 @@ */ #ifndef UNIX -#if !defined(__WIN32__) && !defined(VXWORKS) +#if !defined(__WIN32__) #define UNIX 1 #endif #endif diff --git a/erts/emulator/test/driver_SUITE_data/timer_drv.c b/erts/emulator/test/driver_SUITE_data/timer_drv.c index 8c3f203a64..57538e0d57 100644 --- a/erts/emulator/test/driver_SUITE_data/timer_drv.c +++ b/erts/emulator/test/driver_SUITE_data/timer_drv.c @@ -1,11 +1,3 @@ -#ifdef VXWORKS -#include <vxWorks.h> -#include <taskVarLib.h> -#include <taskLib.h> -#include <sysLib.h> -#include <string.h> -#include <ioLib.h> -#endif #include <stdio.h> #include "erl_driver.h" @@ -84,12 +76,8 @@ static void timer_read(ErlDrvData p, char *buf, ErlDrvSizeT len) driver_output(port, reply, 1); } else if (buf[0] == DELAY_START_TIMER) { #ifndef __WIN32__ -#ifdef VXWORKS - taskDelay(sysClkRateGet()); -#else sleep(1); #endif -#endif driver_set_timer(port, get_int32(buf + 1)); } } diff --git a/erts/emulator/test/emulator.spec.vxworks b/erts/emulator/test/emulator.spec.vxworks deleted file mode 100644 index 55675bdc29..0000000000 --- a/erts/emulator/test/emulator.spec.vxworks +++ /dev/null @@ -1,26 +0,0 @@ -{topcase, {dir, "../emulator_test"}}. - -% Added since R11 -{skip,{distribution_SUITE,link_to_dead_new_node,"Does not work in distributed test environments"}}. -{skip,{binary_SUITE,terms_float,"Floats, VxWorks, PPC = Floating points never equal..."}}. -{skip,{system_info_SUITE,process_count,"Fix-allocs starving VxWorks cards"}}. -{skip,{monitor_SUITE,mixer,"Fix-allocs starving VxWorks cards"}}. - -{skip,{node_container_SUITE,"Too memory consuming..."}}. - -{skip,{trace_SUITE,system_monitor_long_gc_1,"Too memory consuming..."}}. -{skip,{trace_SUITE,system_monitor_long_gc_2,"Too memory consuming..."}}. -{skip,{trace_SUITE,system_monitor_large_heap_1,"Too memory consuming..."}}. -{skip,{trace_SUITE,system_monitor_large_heap_2,"Too memory consuming..."}}. -% End added since R11 - -{skip, {distribution_SUITE,stop_dist,"Not written to work on VxWorks."}}. -{skip, {distribution_SUITE,dist_auto_connect_never, - "Not written to work on VxWorks."}}. -{skip, {distribution_SUITE,dist_auto_connect_once, - "Not written to work on VxWorks."}}. -{skip, {trace_SUITE,system_monitor_long_gc, - "Too memory consuming for VxWorks cards."}}. -{skip, {trace_meta_SUITE,stack_grow, - "Too memory consuming for VxWorks cards."}}. -{skip, {obsolete_SUITE, "Not on vxworks"}}. diff --git a/erts/emulator/test/emulator_bench.spec b/erts/emulator/test/emulator_bench.spec new file mode 100644 index 0000000000..f709d913b7 --- /dev/null +++ b/erts/emulator/test/emulator_bench.spec @@ -0,0 +1 @@ +{groups,"../emulator_test",estone_SUITE,[estone_bench]}. diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 2417d4bcfe..21834bfa62 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -19,7 +19,7 @@ -module(estone_SUITE). %% Test functions -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2,estone/1]). + init_per_group/2,end_per_group/2,estone/1,estone_bench/1]). -export([init_per_testcase/2, end_per_testcase/2]). %% Internal exports for EStone tests @@ -46,6 +46,7 @@ -include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct_event.hrl"). %% Test suite defines -define(default_timeout, ?t:minutes(10)). @@ -80,7 +81,7 @@ all() -> [estone]. groups() -> - []. + [{estone_bench, [{repeat,50}],[estone_bench]}]. init_per_suite(Config) -> Config. @@ -108,6 +109,17 @@ estone(Config) when is_list(Config) -> ?line {comment,Mhz ++ " MHz, " ++ integer_to_list(Stones) ++ " ESTONES"}. +estone_bench(Config) -> + DataDir = ?config(data_dir,Config), + L = ?MODULE:macro(?MODULE:micros(),DataDir), + [ct_event:notify( + #event{name = benchmark_data, + data = [{name,proplists:get_value(title,Mark)}, + {value,proplists:get_value(estones,Mark)}]}) + || Mark <- L], + L. + + %% %% Calculate CPU speed %% diff --git a/erts/emulator/test/estone_SUITE_data/estone_cat.c b/erts/emulator/test/estone_SUITE_data/estone_cat.c index 8ed9f8375b..a34bda4384 100644 --- a/erts/emulator/test/estone_SUITE_data/estone_cat.c +++ b/erts/emulator/test/estone_SUITE_data/estone_cat.c @@ -12,11 +12,7 @@ #include <fcntl.h> #include <errno.h> -#ifdef VXWORKS -estone_cat(argc, argv) -#else main(argc, argv) -#endif int argc; char *argv[]; { diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 559e540016..839ad6a4f4 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -539,12 +539,6 @@ bad_md5(Bad) -> {'EXIT',{badarg,_}} = (catch erlang:md5(Bad)). refc(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - private -> refc_1(); - hybrid -> {skip,"Hybrid heap"} - end. - -refc_1() -> ?line F1 = fun_factory(2), ?line {refc,2} = erlang:fun_info(F1, refc), ?line F2 = fun_factory(42), @@ -570,12 +564,6 @@ fun_factory(Const) -> fun(X) -> X + Const end. refc_ets(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - private -> refc_ets_1(); - hybrid -> {skip,"Hybrid heap"} - end. - -refc_ets_1() -> ?line F = fun(X) -> X + 33 end, ?line {refc,2} = erlang:fun_info(F, refc), @@ -622,12 +610,6 @@ refc_ets_bag(F1, Options) -> ok. refc_dist(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - private -> refc_dist_1(); - hybrid -> {skip,"Hybrid heap"} - end. - -refc_dist_1() -> ?line {ok,Node} = start_node(fun_SUITE_refc_dist), ?line process_flag(trap_exit, true), ?line Pid = spawn_link(Node, diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl index 771d2c9a7a..19fa433a53 100644 --- a/erts/emulator/test/gc_SUITE.erl +++ b/erts/emulator/test/gc_SUITE.erl @@ -54,17 +54,12 @@ grow_heap(doc) -> ["Produce a growing list of elements, ", "for X calls, then drop one item per call", "until the list is empty."]; grow_heap(Config) when is_list(Config) -> - ?line Dog=test_server:timetrap(test_server:minutes(40)), - ?line ok=grow_heap1(256), - case os:type() of - vxworks -> - stop_here; - _ -> - ?line ok=grow_heap1(512), - ?line ok=grow_heap1(1024), - ?line ok=grow_heap1(2048) - end, - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:minutes(40)), + ok = grow_heap1(256), + ok = grow_heap1(512), + ok = grow_heap1(1024), + ok = grow_heap1(2048), + test_server:timetrap_cancel(Dog), ok. grow_heap1(Len) -> @@ -82,10 +77,10 @@ grow_heap1(List, MaxLen, CurLen, up) -> grow_heap1([], _MaxLen, _, down) -> ok; grow_heap1([_|List], MaxLen, CurLen, down) -> - ?line {_,_,C}=erlang:now(), - ?line Num=C rem (length(List))+1, - ?line Elem=lists:nth(Num, List), - ?line NewList=lists:delete(Elem, List), + {_,_,C} = erlang:now(), + Num = C rem (length(List))+1, + Elem = lists:nth(Num, List), + NewList = lists:delete(Elem, List), grow_heap1(NewList, MaxLen, CurLen-1, down). @@ -93,16 +88,11 @@ grow_heap1([_|List], MaxLen, CurLen, down) -> grow_stack(doc) -> ["Increase and decrease stack size, and ", "drop off some garbage from time to time."]; grow_stack(Config) when is_list(Config) -> - ?line Dog=test_server:timetrap(test_server:minutes(80)), + Dog = test_server:timetrap(test_server:minutes(80)), show_heap("before:"), - case os:type() of - vxworks -> - ?line grow_stack1(25, 0); - _ -> - ?line grow_stack1(200, 0) - end, + grow_stack1(200, 0), show_heap("after:"), - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. grow_stack1(0, _) -> @@ -123,16 +113,11 @@ grow_stack_heap(doc) -> ["While growing the heap, bounces the size ", "of the stack, and while reducing the heap", "bounces the stack usage."]; grow_stack_heap(Config) when is_list(Config) -> - case os:type() of - vxworks -> - {comment, "Takes too long to run on VxWorks/cpu32"}; - _ -> - ?line Dog=test_server:timetrap(test_server:minutes(40)), - ?line grow_stack_heap1(16), - ?line grow_stack_heap1(32), - ?line test_server:timetrap_cancel(Dog), - ok - end. + Dog = test_server:timetrap(test_server:minutes(40)), + grow_stack_heap1(16), + grow_stack_heap1(32), + test_server:timetrap_cancel(Dog), + ok. grow_stack_heap1(MaxLen) -> io:format("~ngrow_stack_heap with ~p items.",[MaxLen]), @@ -151,10 +136,10 @@ grow_stack_heap1(List, MaxLen, CurLen, up) -> grow_stack_heap1([], _MaxLen, _, down) -> ok; grow_stack_heap1([_|List], MaxLen, CurLen, down) -> grow_stack1(CurLen*2,0), - ?line {_,_,C}=erlang:now(), - ?line Num=C rem (length(List))+1, - ?line Elem=lists:nth(Num, List), - ?line NewList=lists:delete(Elem, List), + {_,_,C}=erlang:now(), + Num=C rem (length(List))+1, + Elem=lists:nth(Num, List), + NewList=lists:delete(Elem, List), grow_stack_heap1(NewList, MaxLen, CurLen-1, down), ok. diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl index 82a0aad189..68bc3434d4 100644 --- a/erts/emulator/test/hibernate_SUITE.erl +++ b/erts/emulator/test/hibernate_SUITE.erl @@ -67,10 +67,7 @@ end_per_testcase(_Func, Config) -> basic(Config) when is_list(Config) -> Ref = make_ref(), Info = {self(),Ref}, - ExpectedHeapSz = case erlang:system_info(heap_type) of - private -> erts_debug:size([Info]); - hybrid -> erts_debug:size([a|b]) - end, + ExpectedHeapSz = erts_debug:size([Info]), ?line Child = spawn_link(fun() -> basic_hibernator(Info) end), ?line hibernate_wake_up(100, ExpectedHeapSz, Child), ?line Child ! please_quit_now, @@ -166,10 +163,7 @@ whats_up_calc(A1, A2, A3, A4, A5, A6, A7, A8, A9, Acc) -> dynamic_call(Config) when is_list(Config) -> Ref = make_ref(), Info = {self(),Ref}, - ExpectedHeapSz = case erlang:system_info(heap_type) of - private -> erts_debug:size([Info]); - hybrid -> erts_debug:size([a|b]) - end, + ExpectedHeapSz = erts_debug:size([Info]), ?line Child = spawn_link(fun() -> ?MODULE:dynamic_call_hibernator(Info, hibernate) end), ?line hibernate_wake_up(100, ExpectedHeapSz, Child), ?line Child ! please_quit_now, diff --git a/erts/emulator/test/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src index b6c843269c..37eb1daa72 100644 --- a/erts/emulator/test/mtx_SUITE_data/Makefile.src +++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src @@ -27,4 +27,11 @@ LIBS = @ERTS_LIBS@ all: $(NIF_LIBS) +mtx_SUITE.c: force_rebuild + touch mtx_SUITE.c + +force_rebuild: + echo "Force rebuild to compensate for emulator type dependencies" + + @SHLIB_RULES@ diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index aa83459ef8..0bf2c03233 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -459,10 +459,6 @@ make_node_garbage(_, _, _, Ps) -> end, lists:foreach(fun (P) -> wait_until(fun () -> ProcIsCleanedUp(P) end) end, Ps), - ?line case erlang:system_info(heap_type) of - shared -> ?line garbage_collect(); - _ -> ?line ok - end, ?line ok. @@ -605,17 +601,7 @@ node_controller_refc(Config) when is_list(Config) -> % Get rid of all references to Node ?line exec(P, fun () -> exit(normal) end), ?line wait_until(fun () -> not is_process_alive(P) end), - ?line case erlang:system_info(heap_type) of - shared -> - ?line garbage_collect(); - hybrid -> - ?line lists:foreach(fun (Proc) -> garbage_collect(Proc) end, - processes()), - ?line erlang:garbage_collect_message_area(); - _ -> - ?line lists:foreach(fun (Proc) -> garbage_collect(Proc) end, - processes()) - end, + lists:foreach(fun (Proc) -> garbage_collect(Proc) end, processes()), ?line false = get_node_references({Node,Creation}), ?line false = get_dist_references(Node), ?line false = lists:member(Node, nodes(known)), diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 0a1ef5a78f..4b555777d7 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -157,16 +157,16 @@ win_massive(Config) when is_list(Config) -> end. do_win_massive() -> - ?line Dog = test_server:timetrap(test_server:seconds(360)), - ?line SuiteDir = filename:dirname(code:which(?MODULE)), - ?line Env = " -env ERL_MAX_PORTS 8192", - ?line {ok, Node} = + Dog = test_server:timetrap(test_server:seconds(360)), + SuiteDir = filename:dirname(code:which(?MODULE)), + Env = " -env ERL_MAX_PORTS 8192", + {ok, Node} = test_server:start_node(win_massive, slave, [{args, " -pa " ++ SuiteDir ++ Env}]), - ?line ok = rpc:call(Node,?MODULE,win_massive_client,[3000]), - ?line test_server:stop_node(Node), - ?line test_server:timetrap_cancel(Dog), + ok = rpc:call(Node,?MODULE,win_massive_client,[3000]), + test_server:stop_node(Node), + test_server:timetrap_cancel(Dog), ok. win_massive_client(N) -> @@ -208,11 +208,11 @@ win_massive_loop(P,N) -> %% We will send only a small amount of data, to avoid deadlock. stream_small(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line stream_ping(Config, 512, "", []), - ?line stream_ping(Config, 1777, "", []), - ?line stream_ping(Config, 1777, "-s512", []), - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(10)), + stream_ping(Config, 512, "", []), + stream_ping(Config, 1777, "", []), + stream_ping(Config, 1777, "-s512", []), + test_server:timetrap_cancel(Dog), ok. %% Send big amounts of data (much bigger than the buffer size in port test). @@ -220,30 +220,22 @@ stream_small(Config) when is_list(Config) -> %% non-blocking reads and writes. stream_big(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(180)), - case os:type() of - vxworks -> - %% Don't stress VxWorks too much - ?line stream_ping(Config, 43755, "", []), - ?line stream_ping(Config, 51255, "", []), - ?line stream_ping(Config, 52345, " -s40000", []); - _ -> - ?line stream_ping(Config, 43755, "", []), - ?line stream_ping(Config, 100000, "", []), - ?line stream_ping(Config, 77777, " -s40000", []) - end, - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(180)), + stream_ping(Config, 43755, "", []), + stream_ping(Config, 100000, "", []), + stream_ping(Config, 77777, " -s40000", []), + test_server:timetrap_cancel(Dog), ok. %% Sends packet with header size of 1, 2, and 4, with packets of various %% sizes. basic_ping(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(120)), - ?line ping(Config, sizes(1), 1, "", []), - ?line ping(Config, sizes(2), 2, "", []), - ?line ping(Config, sizes(4), 4, "", []), - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(120)), + ping(Config, sizes(1), 1, "", []), + ping(Config, sizes(2), 2, "", []), + ping(Config, sizes(4), 4, "", []), + test_server:timetrap_cancel(Dog), ok. %% Let the port program insert delays between characters sent back to @@ -251,30 +243,29 @@ basic_ping(Config) when is_list(Config) -> %% small chunks rather than all at once. slow_writes(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(20)), - ?line ping(Config, [8], 4, "-s1", []), - ?line ping(Config, [10], 2, "-s2", []), - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(20)), + ping(Config, [8], 4, "-s1", []), + ping(Config, [10], 2, "-s2", []), + test_server:timetrap_cancel(Dog), ok. bad_packet(doc) -> ["Test that we get {'EXIT', Port, einval} if we try to send a bigger " "packet than the packet header allows."]; bad_packet(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line PortTest = port_test(Config), - ?line process_flag(trap_exit, true), + Dog = test_server:timetrap(test_server:seconds(10)), + PortTest = port_test(Config), + process_flag(trap_exit, true), - ?line bad_packet(PortTest, 1, 256), - ?line bad_packet(PortTest, 1, 257), - ?line bad_packet(PortTest, 2, 65536), - ?line bad_packet(PortTest, 2, 65537), + bad_packet(PortTest, 1, 256), + bad_packet(PortTest, 1, 257), + bad_packet(PortTest, 2, 65536), + bad_packet(PortTest, 2, 65537), - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. bad_packet(PortTest, HeaderSize, PacketSize) -> - %% Intentionally no ?line macros. P = open_port({spawn, PortTest}, [{packet, HeaderSize}]), P ! {self(), {command, make_zero_packet(PacketSize)}}, receive @@ -292,16 +283,16 @@ make_zero_packet(N) -> %% Test sending bad messages to a port. bad_port_messages(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line PortTest = port_test(Config), - ?line process_flag(trap_exit, true), + Dog = test_server:timetrap(test_server:seconds(10)), + PortTest = port_test(Config), + process_flag(trap_exit, true), - ?line bad_message(PortTest, {a,b}), - ?line bad_message(PortTest, {a}), - ?line bad_message(PortTest, {self(),{command,bad_command}}), - ?line bad_message(PortTest, {self(),{connect,no_pid}}), + bad_message(PortTest, {a,b}), + bad_message(PortTest, {a}), + bad_message(PortTest, {self(),{command,bad_command}}), + bad_message(PortTest, {self(),{connect,no_pid}}), - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. bad_message(PortTest, Message) -> @@ -319,108 +310,97 @@ bad_message(PortTest, Message) -> %% Tests the 'binary' option for a port. t_binary(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(300)), + Dog = test_server:timetrap(test_server:seconds(300)), %% Packet mode. - ?line ping(Config, sizes(1), 1, "", [binary]), - ?line ping(Config, sizes(2), 2, "", [binary]), - ?line ping(Config, sizes(4), 4, "", [binary]), + ping(Config, sizes(1), 1, "", [binary]), + ping(Config, sizes(2), 2, "", [binary]), + ping(Config, sizes(4), 4, "", [binary]), %% Stream mode. - case os:type() of - vxworks -> - %% don't stress VxWorks too much - ?line stream_ping(Config, 435, "", [binary]), - ?line stream_ping(Config, 43755, "", [binary]), - ?line stream_ping(Config, 50000, "", [binary]); - _ -> - ?line stream_ping(Config, 435, "", [binary]), - ?line stream_ping(Config, 43755, "", [binary]), - ?line stream_ping(Config, 100000, "", [binary]) - end, + stream_ping(Config, 435, "", [binary]), + stream_ping(Config, 43755, "", [binary]), + stream_ping(Config, 100000, "", [binary]), - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. name1(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(100)), - ?line PortTest = port_test(Config), - ?line Command = lists:concat([PortTest, " "]), - ?line P = open_port({spawn, Command}, []), - ?line register(myport, P), - ?line P = whereis(myport), + Dog = test_server:timetrap(test_server:seconds(100)), + PortTest = port_test(Config), + Command = lists:concat([PortTest, " "]), + P = open_port({spawn, Command}, []), + register(myport, P), + P = whereis(myport), Text = "hej", - ?line myport ! {self(), {command, Text}}, - ?line receive - {P, {data, Text}} -> - ok - end, - ?line myport ! {self(), close}, - ?line receive - {P, closed} -> ok - end, - ?line undefined = whereis(myport), - ?line test_server:timetrap_cancel(Dog), + myport ! {self(), {command, Text}}, + receive + {P, {data, Text}} -> + ok + end, + myport ! {self(), close}, + receive + {P, closed} -> ok + end, + undefined = whereis(myport), + test_server:timetrap_cancel(Dog), ok. %% Test that the 'eof' option works. eof(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(100)), - ?line PortTest = port_test(Config), - ?line Command = lists:concat([PortTest, " -h0 -q"]), - ?line P = open_port({spawn, Command}, [eof]), - ?line receive - {P, eof} -> - ok - end, - ?line P ! {self(), close}, - ?line receive - {P, closed} -> ok - end, - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(100)), + PortTest = port_test(Config), + Command = lists:concat([PortTest, " -h0 -q"]), + P = open_port({spawn, Command}, [eof]), + receive + {P, eof} -> + ok + end, + P ! {self(), close}, + receive + {P, closed} -> ok + end, + test_server:timetrap_cancel(Dog), ok. %% Tests that the 'in' option for a port works. input_only(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(300)), - ?line expect_input(Config, [0, 1, 10, 13, 127, 128, 255], 1, "", [in]), - ?line expect_input(Config, [0, 1, 255, 2048], 2, "", [in]), - ?line expect_input(Config, [0, 1, 255, 2048], 4, "", [in]), - ?line expect_input(Config, [0, 1, 10, 13, 127, 128, 255], - 1, "", [in, binary]), - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(300)), + expect_input(Config, [0, 1, 10, 13, 127, 128, 255], 1, "", [in]), + expect_input(Config, [0, 1, 255, 2048], 2, "", [in]), + expect_input(Config, [0, 1, 255, 2048], 4, "", [in]), + expect_input(Config, [0, 1, 10, 13, 127, 128, 255], + 1, "", [in, binary]), + test_server:timetrap_cancel(Dog), ok. %% Tests that the 'out' option for a port works. output_only(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(100)), - ?line Dir = ?config(priv_dir, Config), - ?line Filename = filename:join(Dir, "output_only_stream"), - ?line output_and_verify(Config, Filename, "-h0", - random_packet(35777, "echo")), - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(100)), + Dir = ?config(priv_dir, Config), + Filename = filename:join(Dir, "output_only_stream"), + output_and_verify(Config, Filename, "-h0", + random_packet(35777, "echo")), + test_server:timetrap_cancel(Dog), ok. output_and_verify(Config, Filename, Options, Data) -> - ?line PortTest = port_test(Config), - ?line Command = lists:concat([PortTest, " ", - Options, " -o", Filename]), - ?line Port = open_port({spawn, Command}, [out]), - ?line Port ! {self(), {command, Data}}, - ?line Port ! {self(), close}, - ?line receive - {Port, closed} -> ok - end, - Wait_time = case os:type() of - vxworks -> 5000; - _ -> 500 - end, - ?line test_server:sleep(Wait_time), - ?line {ok, Written} = file:read_file(Filename), - ?line Data = binary_to_list(Written), + PortTest = port_test(Config), + Command = lists:concat([PortTest, " ", + Options, " -o", Filename]), + Port = open_port({spawn, Command}, [out]), + Port ! {self(), {command, Data}}, + Port ! {self(), close}, + receive + {Port, closed} -> ok + end, + Wait_time = 500, + test_server:sleep(Wait_time), + {ok, Written} = file:read_file(Filename), + Data = binary_to_list(Written), ok. %% Test that receiving several packages written in the same @@ -430,19 +410,11 @@ output_and_verify(Config, Filename, Options, Data) -> %% Basic test of receiving multiple packages, written in %% one operation by the other end. mul_basic(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(600)), - case os:type() of - vxworks -> - %% don't stress vxworks too much - ?line expect_input(Config, [0, 1, 255, 10, 13], 1, "", []), - ?line expect_input(Config, [0, 10, 13, 1600, 8191, 16383], 2, "", []), - ?line expect_input(Config, [10, 35000], 4, "", []); - _ -> - ?line expect_input(Config, [0, 1, 255, 10, 13], 1, "", []), - ?line expect_input(Config, [0, 10, 13, 1600, 32767, 65535], 2, "", []), - ?line expect_input(Config, [10, 70000], 4, "", []) - end, - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(600)), + expect_input(Config, [0, 1, 255, 10, 13], 1, "", []), + expect_input(Config, [0, 10, 13, 1600, 32767, 65535], 2, "", []), + expect_input(Config, [10, 70000], 4, "", []), + test_server:timetrap_cancel(Dog), ok. %% Test reading a buffer consisting of several packets, some @@ -451,9 +423,9 @@ mul_basic(Config) when is_list(Config) -> %% delays in between.) mul_slow_writes(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(250)), - ?line expect_input(Config, [0, 20, 255, 10, 1], 1, "-s64", []), - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(250)), + expect_input(Config, [0, 20, 255, 10, 1], 1, "-s64", []), + test_server:timetrap_cancel(Dog), ok. %% Runs several port tests in parallell. Each individual test @@ -461,27 +433,27 @@ mul_slow_writes(Config) when is_list(Config) -> %% should also finish in about 5 seconds. parallell(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(300)), - ?line Testers = - [fun() -> stream_ping(Config, 1007, "-s100", []) end, - fun() -> stream_ping(Config, 10007, "-s1000", []) end, - fun() -> stream_ping(Config, 10007, "-s1000", []) end, - - fun() -> expect_input(Config, [21, 22, 23, 24, 25], 1, - "-s10", [in]) end, - - fun() -> ping(Config, [10], 1, "-d", []) end, - fun() -> ping(Config, [20000], 2, "-d", []) end, - fun() -> ping(Config, [101], 1, "-s10", []) end, - fun() -> ping(Config, [1001], 2, "-s100", []) end, - fun() -> ping(Config, [10001], 4, "-s1000", []) end, - - fun() -> ping(Config, [501, 501], 2, "-s100", []) end, - fun() -> ping(Config, [11, 12, 13, 14, 11], 1, "-s5", []) end], - ?line process_flag(trap_exit, true), - ?line Pids = lists:map(fun fun_spawn/1, Testers), - ?line wait_for(Pids), - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(300)), + Testers = [ + fun() -> stream_ping(Config, 1007, "-s100", []) end, + fun() -> stream_ping(Config, 10007, "-s1000", []) end, + fun() -> stream_ping(Config, 10007, "-s1000", []) end, + + fun() -> expect_input(Config, [21, 22, 23, 24, 25], 1, + "-s10", [in]) end, + + fun() -> ping(Config, [10], 1, "-d", []) end, + fun() -> ping(Config, [20000], 2, "-d", []) end, + fun() -> ping(Config, [101], 1, "-s10", []) end, + fun() -> ping(Config, [1001], 2, "-s100", []) end, + fun() -> ping(Config, [10001], 4, "-s1000", []) end, + + fun() -> ping(Config, [501, 501], 2, "-s100", []) end, + fun() -> ping(Config, [11, 12, 13, 14, 11], 1, "-s5", []) end], + process_flag(trap_exit, true), + Pids = lists:map(fun fun_spawn/1, Testers), + wait_for(Pids), + test_server:timetrap_cancel(Dog), ok. wait_for([]) -> @@ -500,29 +472,29 @@ wait_for(Pids) -> dying_port(suite) -> []; dying_port(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(150)), - ?line process_flag(trap_exit, true), + Dog = test_server:timetrap(test_server:seconds(150)), + process_flag(trap_exit, true), - ?line P1 = make_dying_port(Config), - ?line P2 = make_dying_port(Config), - ?line P3 = make_dying_port(Config), - ?line P4 = make_dying_port(Config), - ?line P5 = make_dying_port(Config), + P1 = make_dying_port(Config), + P2 = make_dying_port(Config), + P3 = make_dying_port(Config), + P4 = make_dying_port(Config), + P5 = make_dying_port(Config), %% This should be big enough to be sure to block in the write. - ?line Garbage = random_packet(16384), + Garbage = random_packet(16384), - ?line P1 ! {self(), {command, Garbage}}, - ?line P3 ! {self(), {command, Garbage}}, - ?line P5 ! {self(), {command, Garbage}}, + P1 ! {self(), {command, Garbage}}, + P3 ! {self(), {command, Garbage}}, + P5 ! {self(), {command, Garbage}}, - ?line wait_for_port_exit(P1), - ?line wait_for_port_exit(P2), - ?line wait_for_port_exit(P3), - ?line wait_for_port_exit(P4), - ?line wait_for_port_exit(P5), + wait_for_port_exit(P1), + wait_for_port_exit(P2), + wait_for_port_exit(P3), + wait_for_port_exit(P4), + wait_for_port_exit(P5), - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. wait_for_port_exit(Port) -> @@ -549,9 +521,9 @@ make_dying_port(Config) when is_list(Config) -> port_program_with_path(suite) -> []; port_program_with_path(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(100)), - ?line DataDir = ?config(data_dir, Config), - ?line PrivDir = ?config(priv_dir, Config), + Dog = test_server:timetrap(test_server:seconds(100)), + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), %% Create a copy of the port test program in a directory not %% included in PATH (i.e. in priv_dir), with the name 'my_port_test.exe'. @@ -560,36 +532,31 @@ port_program_with_path(Config) when is_list(Config) -> %% (On Unix, there will be a single file created, which will be %% a copy of the port program.) - ?line PortTest = os:find_executable("port_test", DataDir), + PortTest = os:find_executable("port_test", DataDir), io:format("os:find_executable(~p, ~p) returned ~p", ["port_test", DataDir, PortTest]), - ?line {ok, PortTestPgm} = file:read_file(PortTest), - ?line NewName = filename:join(PrivDir, filename:basename(PortTest)), - ?line RedHerring = filename:rootname(NewName), - ?line ok = file:write_file(RedHerring, "I'm just here to confuse.\n"), - ?line ok = file:write_file(NewName, PortTestPgm), - ?line ok = file:write_file_info(NewName, #file_info{mode=8#111}), - ?line PgmWithPathAndNoExt = filename:rootname(NewName), + {ok, PortTestPgm} = file:read_file(PortTest), + NewName = filename:join(PrivDir, filename:basename(PortTest)), + RedHerring = filename:rootname(NewName), + ok = file:write_file(RedHerring, "I'm just here to confuse.\n"), + ok = file:write_file(NewName, PortTestPgm), + ok = file:write_file_info(NewName, #file_info{mode=8#111}), + PgmWithPathAndNoExt = filename:rootname(NewName), %% Open the port using the path to the copied port test program, %% but without the .exe extension, and verified that it was started. %% %% If the bug is present the open_port call will fail with badarg. - ?line Command = lists:concat([PgmWithPathAndNoExt, " -h2"]), - %% allow VxWorks time to write file - case os:type() of - vxworks -> test_server:sleep(2500); - _ -> time + Command = lists:concat([PgmWithPathAndNoExt, " -h2"]), + P = open_port({spawn, Command}, [{packet, 2}]), + Message = "echo back to me", + P ! {self(), {command, Message}}, + receive + {P, {data, Message}} -> + ok end, - ?line P = open_port({spawn, Command}, [{packet, 2}]), - ?line Message = "echo back to me", - ?line P ! {self(), {command, Message}}, - ?line receive - {P, {data, Message}} -> - ok - end, - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. @@ -597,56 +564,46 @@ port_program_with_path(Config) when is_list(Config) -> %% This used to fail on Windows. open_input_file_port(suite) -> []; open_input_file_port(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line PrivDir = ?config(priv_dir, Config), + Dog = test_server:timetrap(test_server:seconds(10)), + PrivDir = ?config(priv_dir, Config), %% Create a file with the file driver and read it back using %% open_port/2. - ?line MyFile1 = filename:join(PrivDir, "my_input_file"), - ?line FileData1 = "An input file", - ?line ok = file:write_file(MyFile1, FileData1), - case os:type() of - vxworks -> - %% Can't open input file with vanilla driver on VxWorks - ?line process_flag(trap_exit, true), - ?line case catch open_port(MyFile1, [in]) of - {'EXIT', {badarg, _}} -> - ok - end; - _ -> - ?line case open_port(MyFile1, [in]) of - InputPort when is_port(InputPort) -> - ?line receive - {InputPort, {data, FileData1}} -> - ok - end - end + MyFile1 = filename:join(PrivDir, "my_input_file"), + FileData1 = "An input file", + ok = file:write_file(MyFile1, FileData1), + case open_port(MyFile1, [in]) of + InputPort when is_port(InputPort) -> + receive + {InputPort, {data, FileData1}} -> + ok + end end, - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. %% Tests that files can be written using open_port(Filename, [out]). open_output_file_port(suite) -> []; open_output_file_port(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(100)), - ?line PrivDir = ?config(priv_dir, Config), + Dog = test_server:timetrap(test_server:seconds(100)), + PrivDir = ?config(priv_dir, Config), %% Create a file with open_port/2 and read it back with %% the file driver. - ?line MyFile2 = filename:join(PrivDir, "my_output_file"), - ?line FileData2_0 = "A file created ", - ?line FileData2_1 = "with open_port/2.\n", - ?line FileData2 = FileData2_0 ++ FileData2_1, - ?line OutputPort = open_port(MyFile2, [out]), - ?line OutputPort ! {self(), {command, FileData2_0}}, - ?line OutputPort ! {self(), {command, FileData2_1}}, - ?line OutputPort ! {self(), close}, - ?line {ok, Bin} = file:read_file(MyFile2), - ?line FileData2 = binary_to_list(Bin), - - ?line test_server:timetrap_cancel(Dog), + MyFile2 = filename:join(PrivDir, "my_output_file"), + FileData2_0 = "A file created ", + FileData2_1 = "with open_port/2.\n", + FileData2 = FileData2_0 ++ FileData2_1, + OutputPort = open_port(MyFile2, [out]), + OutputPort ! {self(), {command, FileData2_0}}, + OutputPort ! {self(), {command, FileData2_1}}, + OutputPort ! {self(), close}, + {ok, Bin} = file:read_file(MyFile2), + FileData2 = binary_to_list(Bin), + + test_server:timetrap_cancel(Dog), ok. %% @@ -670,17 +627,17 @@ iter_max_ports(Config) when is_list(Config) -> iter_max_ports_test(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(20)), - ?line PortTest = port_test(Config), - ?line Command = lists:concat([PortTest, " -h0 -q"]), - ?line Iters = case os:type() of + Dog = test_server:timetrap(test_server:minutes(20)), + PortTest = port_test(Config), + Command = lists:concat([PortTest, " -h0 -q"]), + Iters = case os:type() of {win32,_} -> 4; _ -> 10 end, - ?line L = do_iter_max_ports(Iters, Command), + L = do_iter_max_ports(Iters, Command), io:format("Result: ~p",[L]), - ?line all_equal(L), - ?line test_server:timetrap_cancel(Dog), + all_equal(L), + test_server:timetrap_cancel(Dog), {comment, "Max ports: " ++ integer_to_list(hd(L))}. do_iter_max_ports(N, Command) when N > 0 -> @@ -695,9 +652,9 @@ all_equal([]) -> ok. max_ports(Command) -> test_server:sleep(500), - ?line Ps = open_ports({spawn, Command}, [eof]), - ?line N = length(Ps), - ?line close_ports(Ps), + Ps = open_ports({spawn, Command}, [eof]), + N = length(Ps), + close_ports(Ps), io:format("Got ~p ports\n",[N]), N. @@ -727,105 +684,105 @@ open_ports(Name, Settings) -> enomem -> []; Other -> - ?line test_server:fail({open_ports, Other}) + test_server:fail({open_ports, Other}) end; Other -> - ?line test_server:fail({open_ports, Other}) + test_server:fail({open_ports, Other}) end. %% Tests that exit(Port, Term) works (has been known to crash the emulator). t_exit(suite) -> []; t_exit(Config) when is_list(Config) -> - ?line process_flag(trap_exit, true), - ?line Pid = fun_spawn(fun suicide_port/1, [Config]), - ?line receive - {'EXIT', Pid, die} -> - ok; - Other -> - test_server:fail({bad_message, Other}) - end. + process_flag(trap_exit, true), + Pid = fun_spawn(fun suicide_port/1, [Config]), + receive + {'EXIT', Pid, die} -> + ok; + Other -> + test_server:fail({bad_message, Other}) + end. suicide_port(Config) when is_list(Config) -> - ?line Port = port_expect(Config, [], 0, "", []), - ?line exit(Port, die), - ?line receive after infinity -> ok end. + Port = port_expect(Config, [], 0, "", []), + exit(Port, die), + receive after infinity -> ok end. tps_16_bytes(doc) -> ""; tps_16_bytes(suite) -> []; tps_16_bytes(Config) when is_list(Config) -> - ?line tps(16, Config). + tps(16, Config). tps_1K(doc) -> ""; tps_1K(suite) -> []; tps_1K(Config) when is_list(Config) -> - ?line tps(1024, Config). + tps(1024, Config). tps(Size, Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(300)), - ?line PortTest = port_test(Config), - ?line Packet = list_to_binary(random_packet(Size, "e")), - ?line Port = open_port({spawn, PortTest}, [binary, {packet, 2}]), - ?line Transactions = 10000, - ?line {Elapsed, ok} = test_server:timecall(?MODULE, tps, + Dog = test_server:timetrap(test_server:seconds(300)), + PortTest = port_test(Config), + Packet = list_to_binary(random_packet(Size, "e")), + Port = open_port({spawn, PortTest}, [binary, {packet, 2}]), + Transactions = 10000, + {Elapsed, ok} = test_server:timecall(?MODULE, tps, [Port, Packet, Transactions]), - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), {comment, integer_to_list(trunc(Transactions/Elapsed+0.5)) ++ " transactions/s"}. tps(_Port, _Packet, 0) -> ok; tps(Port, Packet, N) -> - ?line port_command(Port, Packet), - ?line receive - {Port, {data, Packet}} -> - ?line tps(Port, Packet, N-1); - Other -> - ?line test_server:fail({bad_message, Other}) - end. + port_command(Port, Packet), + receive + {Port, {data, Packet}} -> + tps(Port, Packet, N-1); + Other -> + test_server:fail({bad_message, Other}) + end. %% Line I/O test line(Config) when is_list(Config) -> - ?line Siz = 110, - ?line Dog = test_server:timetrap(test_server:seconds(300)), - ?line Packet1 = random_packet(Siz), - ?line Packet2 = random_packet(Siz div 2), + Siz = 110, + Dog = test_server:timetrap(test_server:seconds(300)), + Packet1 = random_packet(Siz), + Packet2 = random_packet(Siz div 2), %% Test that packets are split into lines - ?line port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2, + port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2, io_lib:nl()]), [{eol, Packet1}, {eol, Packet2}]}], 0, "", [{line,Siz}]), %% Test the same for binaries - ?line port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2, + port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet2, io_lib:nl()]), [{eol, Packet1}, {eol, Packet2}]}], 0, "", [{line,Siz},binary]), %% Test that too long lines get split - ?line port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet1, + port_expect(Config,[{lists:append([Packet1, io_lib:nl(), Packet1, Packet2, io_lib:nl()]), [{eol, Packet1}, {noeol, Packet1}, {eol, Packet2}]}], 0, "", [{line,Siz}]), %% Test that last output from closing port program gets received. - ?line L1 = lists:append([Packet1, io_lib:nl(), Packet2]), - ?line S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])), + L1 = lists:append([Packet1, io_lib:nl(), Packet2]), + S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])), io:format("S1 = ~w, L1 = ~w~n", [S1,L1]), - ?line port_expect(Config,[{L1, + port_expect(Config,[{L1, [{eol, Packet1}, {noeol, Packet2}, eof]}], 0, S1, [{line,Siz},eof]), %% Test that lonely <CR> Don't get treated as newlines - ?line port_expect(Config,[{lists:append([Packet1, [13], Packet2, + port_expect(Config,[{lists:append([Packet1, [13], Packet2, io_lib:nl()]), [{noeol, Packet1}, {eol, [13 |Packet2]}]}], 0, "", [{line,Siz}]), %% Test that packets get built up to lines (delayed output from %% port program) - ?line port_expect(Config,[{Packet2,[]}, + port_expect(Config,[{Packet2,[]}, {lists:append([Packet2, io_lib:nl(), Packet1, io_lib:nl()]), [{eol, lists:append(Packet2, Packet2)}, {eol, Packet1}]}], 0, "-d", [{line,Siz}]), %% Test that we get badarg if trying both packet and line - ?line bad_argument(Config, [{packet, 5}, {line, 5}]), - ?line test_server:timetrap_cancel(Dog), + bad_argument(Config, [{packet, 5}, {line, 5}]), + test_server:timetrap_cancel(Dog), ok. %%% Redirection of stderr test @@ -834,15 +791,15 @@ stderr_to_stdout(suite) -> stderr_to_stdout(doc) -> "Test that redirection of standard error to standard output works."; stderr_to_stdout(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), + Dog = test_server:timetrap(test_server:seconds(60)), %% See that it works - ?line Packet = random_packet(10), - ?line port_expect(Config,[{Packet,[Packet]}], 0, "-e -l10", + Packet = random_packet(10), + port_expect(Config,[{Packet,[Packet]}], 0, "-e -l10", [stderr_to_stdout]), - %% ?line stream_ping(Config, 10, "-e", [stderr_to_stdout]), + %% stream_ping(Config, 10, "-e", [stderr_to_stdout]), %% See that it doesn't always happen (will generate garbage on stderr) - ?line port_expect(Config,[{Packet,[eof]}], 0, "-e -l10", [line,eof]), - ?line test_server:timetrap_cancel(Dog), + port_expect(Config,[{Packet,[eof]}], 0, "-e -l10", [line,eof]), + test_server:timetrap_cancel(Dog), ok. @@ -862,47 +819,39 @@ env(suite) -> env(doc) -> ["Test that the 'env' option works"]; env(Config) when is_list(Config) -> - case os:type() of - vxworks -> - {skipped,"Environments not implemented on VxWorks (could be...)"}; - _ -> - env2(Config) - end. - -env2(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), - ?line Priv = ?config(priv_dir, Config), - ?line Temp = filename:join(Priv, "env_fun.bin"), + Dog = test_server:timetrap(test_server:seconds(60)), + Priv = ?config(priv_dir, Config), + Temp = filename:join(Priv, "env_fun.bin"), PluppVal = "dirty monkey", - ?line env_slave(Temp, [{"plupp",PluppVal}]), + env_slave(Temp, [{"plupp",PluppVal}]), Long = "LongAndBoringEnvName", - ?line os:putenv(Long, "nisse"), - - ?line env_slave(Temp, [{"plupp",PluppVal}, - {"DIR_PLUPP","###glurfrik"}], - fun() -> - PluppVal = os:getenv("plupp"), - "###glurfrik" = os:getenv("DIR_PLUPP"), - "nisse" = os:getenv(Long) - end), + os:putenv(Long, "nisse"), + + env_slave(Temp, [{"plupp",PluppVal}, + {"DIR_PLUPP","###glurfrik"}], + fun() -> + PluppVal = os:getenv("plupp"), + "###glurfrik" = os:getenv("DIR_PLUPP"), + "nisse" = os:getenv(Long) + end), - - ?line env_slave(Temp, [{"must_define_something","some_value"}, - {"certainly_not_existing",false}, - {"ends_with_equal", "value="}, - {Long,false}, - {"glurf","a glorfy string"}]), + + env_slave(Temp, [{"must_define_something","some_value"}, + {"certainly_not_existing",false}, + {"ends_with_equal", "value="}, + {Long,false}, + {"glurf","a glorfy string"}]), %% A lot of non existing variables (mingled with existing) NotExistingList = [{lists:flatten(io_lib:format("V~p_not_existing",[X])),false} || X <- lists:seq(1,150)], ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"} || X <- lists:seq(1,150)], - ?line env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)), + env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)), - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. env_slave(File, Env) -> @@ -946,23 +895,15 @@ env_slave_main([File]) -> %% 'env' option %% Test bad environments. bad_env(Config) when is_list(Config) -> - case os:type() of - vxworks -> - {skipped,"Environments not implemented on VxWorks"}; - _ -> - bad_env_1() - end. - -bad_env_1() -> - ?line try_bad_env([abbb]), - ?line try_bad_env([{"key","value"}|{"another","value"}]), - ?line try_bad_env([{"key","value","value2"}]), - ?line try_bad_env([{"key",[a,b,c]}]), - ?line try_bad_env([{"key",value}]), - ?line try_bad_env({a,tuple}), - ?line try_bad_env(42), - ?line try_bad_env([a|b]), - ?line try_bad_env(self()), + try_bad_env([abbb]), + try_bad_env([{"key","value"}|{"another","value"}]), + try_bad_env([{"key","value","value2"}]), + try_bad_env([{"key",[a,b,c]}]), + try_bad_env([{"key",value}]), + try_bad_env({a,tuple}), + try_bad_env(42), + try_bad_env([a|b]), + try_bad_env(self()), ok. try_bad_env(Env) -> @@ -979,36 +920,29 @@ cd(suite) -> cd(doc) -> ["Test that the 'cd' option works"]; cd(Config) when is_list(Config) -> - case os:type() of - vxworks -> - {skipped,"Task specific directories does not exist on VxWorks"}; - _ -> - cd2(Config) - end. -cd2(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), - - ?line Program = atom_to_list(lib:progname()), - ?line DataDir = ?config(data_dir, Config), - ?line TestDir = filename:join(DataDir, "dir"), - ?line Cmd = Program ++ " -pz " ++ DataDir ++ - " -noshell -s port_test pwd -s erlang halt", - ?line _ = open_port({spawn, Cmd}, - [{cd, TestDir}, - {line, 256}]), - ?line receive - {_, {data, {eol, String}}} -> - case filename_equal(String, TestDir) of - true -> - ok; - false -> - ?line test_server:fail({cd, String}) - end; - Other2 -> - ?line test_server:fail({env, Other2}) - end, + Dog = test_server:timetrap(test_server:seconds(60)), + + Program = atom_to_list(lib:progname()), + DataDir = ?config(data_dir, Config), + TestDir = filename:join(DataDir, "dir"), + Cmd = Program ++ " -pz " ++ DataDir ++ + " -noshell -s port_test pwd -s erlang halt", + _ = open_port({spawn, Cmd}, + [{cd, TestDir}, + {line, 256}]), + receive + {_, {data, {eol, String}}} -> + case filename_equal(String, TestDir) of + true -> + ok; + false -> + test_server:fail({cd, String}) + end; + Other2 -> + test_server:fail({env, Other2}) + end, - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. filename_equal(A, B) -> @@ -1059,8 +993,8 @@ otp_3906(Config) when is_list(Config) -> -define(OTP_3906_MAX_CONC_OSP, 50). otp_3906(Config, OSName) -> - ?line DataDir = filename:dirname(proplists:get_value(data_dir,Config)), - ?line {ok, Variables} = file:consult( + DataDir = filename:dirname(proplists:get_value(data_dir,Config)), + {ok, Variables} = file:consult( filename:join([DataDir,"..","..", "test_server","variables"])), case lists:keysearch('CC', 1, Variables) of @@ -1105,7 +1039,7 @@ otp_3906(Config, OSName) -> succeded -> ok; _ -> - ?line test_server:fail(Result) + test_server:fail(Result) end; _ -> {skipped, "No C compiler found"} @@ -1244,17 +1178,17 @@ otp_3906_forker(N, Parent, Ref, Sup, Prog) -> otp_4389(suite) -> []; otp_4389(doc) -> []; otp_4389(Config) when is_list(Config) -> - case {os:type(),erlang:system_info(heap_type)} of - {{unix, _},private} -> - ?line Dog = test_server:timetrap(test_server:seconds(240)), - ?line TCR = self(), + case os:type() of + {unix, _} -> + Dog = test_server:timetrap(test_server:seconds(240)), + TCR = self(), case get_true_cmd() of True when is_list(True) -> - ?line lists:foreach( + lists:foreach( fun (P) -> - ?line receive - {P, ok} -> ?line ok; - {P, Err} -> ?line ?t:fail(Err) + receive + {P, ok} -> ok; + {P, Err} -> ?t:fail(Err) end end, lists:map( @@ -1283,17 +1217,17 @@ otp_4389(Config) when is_list(Config) -> end) end, lists:duplicate(1000,[]))), - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), {comment, "This test case doesn't always fail when the bug that " "it tests for is present (it is most likely to fail on" " a multi processor machine). If the test case fails it" " will fail by deadlocking the emulator."}; _ -> - ?line {skipped, "\"true\" command not found"} + {skipped, "\"true\" command not found"} end; _ -> - {skip,"Only run on Unix and private heaps"} + {skip,"Only run on Unix"} end. get_true_cmd() -> @@ -1320,11 +1254,11 @@ exit_status(suite) -> exit_status(doc) -> ["Test that the 'exit_status' option works"]; exit_status(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), - ?line port_expect(Config,[{"x", + Dog = test_server:timetrap(test_server:seconds(60)), + port_expect(Config,[{"x", [{exit_status, 5}]}], 1, "", [exit_status]), - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. spawn_driver(suite) -> @@ -1332,36 +1266,36 @@ spawn_driver(suite) -> spawn_driver(doc) -> ["Test spawning a driver specifically"]; spawn_driver(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line Path = ?config(data_dir, Config), - ?line ok = load_driver(Path, "echo_drv"), - ?line Port = erlang:open_port({spawn_driver, "echo_drv"}, []), - ?line Port ! {self(), {command, "Hello port!"}}, - ?line receive + Dog = test_server:timetrap(test_server:seconds(10)), + Path = ?config(data_dir, Config), + ok = load_driver(Path, "echo_drv"), + Port = erlang:open_port({spawn_driver, "echo_drv"}, []), + Port ! {self(), {command, "Hello port!"}}, + receive {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), ok; Other -> test_server:fail({unexpected, Other}) end, - ?line Port ! {self(), close}, - ?line receive {Port, closed} -> ok end, + Port ! {self(), close}, + receive {Port, closed} -> ok end, - ?line Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, + Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, []), - ?line receive + receive {Port2, {data, "Hello port?"}} = Msg2 -> io:format("~p~n", [Msg2]), ok; Other2 -> test_server:fail({unexpected2, Other2}) end, - ?line Port2 ! {self(), close}, - ?line receive {Port2, closed} -> ok end, - ?line {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "ls"}, [])), - ?line {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "cmd"}, [])), - ?line {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, os:find_executable("erl")}, [])), - ?line test_server:timetrap_cancel(Dog), + Port2 ! {self(), close}, + receive {Port2, closed} -> ok end, + {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "ls"}, [])), + {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, "cmd"}, [])), + {'EXIT',{badarg,_}} = (catch erlang:open_port({spawn_driver, os:find_executable("erl")}, [])), + test_server:timetrap_cancel(Dog), ok. spawn_executable(suite) -> @@ -1369,48 +1303,48 @@ spawn_executable(suite) -> spawn_executable(doc) -> ["Test spawning an executable specifically"]; spawn_executable(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line DataDir = ?config(data_dir, Config), - ?line EchoArgs1 = filename:join([DataDir,"echo_args"]), - ?line ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)), - ?line [ExactFile1] = run_echo_args(DataDir,[]), - ?line ["echo_args"] = run_echo_args(DataDir,["echo_args"]), - ?line ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]), - ?line [ExactFile1,"hello world","dlrow olleh"] = + Dog = test_server:timetrap(test_server:seconds(10)), + DataDir = ?config(data_dir, Config), + EchoArgs1 = filename:join([DataDir,"echo_args"]), + ExactFile1 = filename:nativename(os:find_executable(EchoArgs1)), + [ExactFile1] = run_echo_args(DataDir,[]), + ["echo_args"] = run_echo_args(DataDir,["echo_args"]), + ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]), + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]), - ?line [ExactFile1] = run_echo_args(DataDir,[default]), - ?line [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1] = run_echo_args(DataDir,[default]), + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[switch_order,ExactFile1,"hello world", "dlrow olleh"]), - ?line [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[default,"hello world","dlrow olleh"]), - ?line [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""), - ?line PrivDir = ?config(priv_dir, Config), - ?line SpaceDir =filename:join([PrivDir,"With Spaces"]), - ?line file:make_dir(SpaceDir), - ?line Executable = filename:basename(ExactFile1), - ?line file:copy(ExactFile1,filename:join([SpaceDir,Executable])), - ?line ExactFile2 = filename:nativename(filename:join([SpaceDir,Executable])), - ?line chmodplusx(ExactFile2), + PrivDir = ?config(priv_dir, Config), + SpaceDir =filename:join([PrivDir,"With Spaces"]), + file:make_dir(SpaceDir), + Executable = filename:basename(ExactFile1), + file:copy(ExactFile1,filename:join([SpaceDir,Executable])), + ExactFile2 = filename:nativename(filename:join([SpaceDir,Executable])), + chmodplusx(ExactFile2), io:format("|~s|~n",[ExactFile2]), - ?line [ExactFile2] = run_echo_args(SpaceDir,[]), - ?line ["echo_args"] = run_echo_args(SpaceDir,["echo_args"]), - ?line ["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]), - ?line [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2] = run_echo_args(SpaceDir,[]), + ["echo_args"] = run_echo_args(SpaceDir,["echo_args"]), + ["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]), + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]), - ?line [ExactFile2] = run_echo_args(SpaceDir,[default]), - ?line [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2] = run_echo_args(SpaceDir,[default]), + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world", "dlrow olleh"]), - ?line [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]), - ?line [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""), - ?line ExeExt = + ExeExt = case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of "exe" -> ".exe"; @@ -1418,47 +1352,47 @@ spawn_executable(Config) when is_list(Config) -> "" end, Executable2 = "spoky name"++ExeExt, - ?line file:copy(ExactFile1,filename:join([SpaceDir,Executable2])), - ?line ExactFile3 = filename:nativename(filename:join([SpaceDir,Executable2])), - ?line chmodplusx(ExactFile3), - ?line [ExactFile3] = run_echo_args(SpaceDir,Executable2,[]), - ?line ["echo_args"] = run_echo_args(SpaceDir,Executable2,["echo_args"]), - ?line ["echo_arguments"] = run_echo_args(SpaceDir,Executable2,["echo_arguments"]), - ?line [ExactFile3,"hello world","dlrow olleh"] = + file:copy(ExactFile1,filename:join([SpaceDir,Executable2])), + ExactFile3 = filename:nativename(filename:join([SpaceDir,Executable2])), + chmodplusx(ExactFile3), + [ExactFile3] = run_echo_args(SpaceDir,Executable2,[]), + ["echo_args"] = run_echo_args(SpaceDir,Executable2,["echo_args"]), + ["echo_arguments"] = run_echo_args(SpaceDir,Executable2,["echo_arguments"]), + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2,[ExactFile3,"hello world","dlrow olleh"]), - ?line [ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]), - ?line [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]), + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2, [switch_order,ExactFile3,"hello world", "dlrow olleh"]), - ?line [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2, [default,"hello world","dlrow olleh"]), - ?line [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""), - ?line {'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi", + {'EXIT',{enoent,_}} = (catch run_echo_args(SpaceDir,"fnurflmonfi", [default,"hello world", "dlrow olleh"])), NonExec = "kronxfrt"++ExeExt, - ?line file:write_file(filename:join([SpaceDir,NonExec]), + file:write_file(filename:join([SpaceDir,NonExec]), <<"Not an executable">>), - ?line {'EXIT',{eacces,_}} = (catch run_echo_args(SpaceDir,NonExec, + {'EXIT',{eacces,_}} = (catch run_echo_args(SpaceDir,NonExec, [default,"hello world", "dlrow olleh"])), - ?line {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"cmd"},[])), - ?line {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"sh"},[])), + {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"cmd"},[])), + {'EXIT',{enoent,_}} = (catch open_port({spawn_executable,"sh"},[])), case os:type() of {win32,_} -> test_bat_file(SpaceDir); {unix,_} -> test_sh_file(SpaceDir) end, - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. unregister_name(Config) when is_list(Config) -> - ?line true = register(crash, open_port({spawn, "sleep 100"}, [])), - ?line true = unregister(crash). + true = register(crash, open_port({spawn, "sleep 100"}, [])), + true = unregister(crash). test_bat_file(Dir) -> FN = "tf.bat", @@ -1478,16 +1412,16 @@ test_bat_file(Dir) -> <<"\r\n">>, <<":done\r\n">>, <<"\r\n">>], - ?line file:write_file(Full,list_to_binary(D)), - ?line EF = filename:basename(FN), - ?line [DN,"hello","world"] = + file:write_file(Full,list_to_binary(D)), + EF = filename:basename(FN), + [DN,"hello","world"] = run_echo_args(Dir,FN, [default,"hello","world"]), %% The arg0 argumant should be ignored when running batch files - ?line [DN,"hello","world"] = + [DN,"hello","world"] = run_echo_args(Dir,FN, ["knaskurt","hello","world"]), - ?line EF = filename:basename(DN), + EF = filename:basename(DN), ok. test_sh_file(Dir) -> @@ -1501,20 +1435,20 @@ test_sh_file(Dir) -> <<" shift\n">>, <<" i=`expr $i + 1`\n">>, <<"done\n">>], - ?line file:write_file(Full,list_to_binary(D)), - ?line chmodplusx(Full), - ?line [Full,"hello","world"] = + file:write_file(Full,list_to_binary(D)), + chmodplusx(Full), + [Full,"hello","world"] = run_echo_args(Dir,FN, [default,"hello","world"]), - ?line [Full,"hello","world of spaces"] = + [Full,"hello","world of spaces"] = run_echo_args(Dir,FN, [default,"hello","world of spaces"]), - ?line file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>), - ?line file:write_file(filename:join([Dir,"testfile2"]),<<"testdata2">>), - ?line Pattern = filename:join([Dir,"testfile*"]), - ?line L = filelib:wildcard(Pattern), - ?line 2 = length(L), - ?line [Full,"hello",Pattern] = + file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>), + file:write_file(filename:join([Dir,"testfile2"]),<<"testdata2">>), + Pattern = filename:join([Dir,"testfile*"]), + L = filelib:wildcard(Pattern), + 2 = length(L), + [Full,"hello",Pattern] = run_echo_args(Dir,FN, [default,"hello",Pattern]), ok. @@ -1574,25 +1508,25 @@ mix_up_ports(suite) -> mix_up_ports(doc) -> ["Test that the emulator does not mix up ports when the port table wraps"]; mix_up_ports(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line Path = ?config(data_dir, Config), - ?line ok = load_driver(Path, "echo_drv"), - ?line Port = erlang:open_port({spawn, "echo_drv"}, []), - ?line Port ! {self(), {command, "Hello port!"}}, - ?line receive + Dog = test_server:timetrap(test_server:seconds(10)), + Path = ?config(data_dir, Config), + ok = load_driver(Path, "echo_drv"), + Port = erlang:open_port({spawn, "echo_drv"}, []), + Port ! {self(), {command, "Hello port!"}}, + receive {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), ok; Other -> test_server:fail({unexpected, Other}) end, - ?line Port ! {self(), close}, - ?line receive {Port, closed} -> ok end, - ?line loop(start, done, + Port ! {self(), close}, + receive {Port, closed} -> ok end, + loop(start, done, fun(P) -> - ?line Q = + Q = (catch erlang:open_port({spawn, "echo_drv"}, [])), -%% ?line io:format("~p ", [Q]), +%% io:format("~p ", [Q]), if is_port(Q) -> Q; true -> @@ -1600,14 +1534,14 @@ mix_up_ports(Config) when is_list(Config) -> done end end), - ?line Port ! {self(), {command, "Hello again port!"}}, - ?line receive + Port ! {self(), {command, "Hello again port!"}}, + receive Msg2 -> test_server:fail({unexpected, Msg2}) after 1000 -> ok end, - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), ok. loop(Stop, Stop, Fun) when is_function(Fun) -> @@ -1622,45 +1556,45 @@ otp_5112(doc) -> ["Test that link to connected process is taken away when port calls", "driver_exit() also when the port index has wrapped"]; otp_5112(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line Path = ?config(data_dir, Config), - ?line ok = load_driver(Path, "exit_drv"), - ?line Port = otp_5112_get_wrapped_port(), - ?line ?t:format("Max ports: ~p~n",[max_ports()]), - ?line ?t:format("Port: ~p~n",[Port]), - ?line {links, Links1} = process_info(self(),links), - ?line ?t:format("Links1: ~p~n",[Links1]), - ?line true = lists:member(Port, Links1), - ?line Port ! {self(), {command, ""}}, - ?line {links, Links2} = process_info(self(),links), - ?line ?t:format("Links2: ~p~n",[Links2]), - ?line false = lists:member(Port, Links2), %% This used to fail - ?line test_server:timetrap_cancel(Dog), + Dog = test_server:timetrap(test_server:seconds(10)), + Path = ?config(data_dir, Config), + ok = load_driver(Path, "exit_drv"), + Port = otp_5112_get_wrapped_port(), + ?t:format("Max ports: ~p~n",[max_ports()]), + ?t:format("Port: ~p~n",[Port]), + {links, Links1} = process_info(self(),links), + ?t:format("Links1: ~p~n",[Links1]), + true = lists:member(Port, Links1), + Port ! {self(), {command, ""}}, + {links, Links2} = process_info(self(),links), + ?t:format("Links2: ~p~n",[Links2]), + false = lists:member(Port, Links2), %% This used to fail + test_server:timetrap_cancel(Dog), ok. otp_5112_get_wrapped_port() -> - ?line P1 = erlang:open_port({spawn, "exit_drv"}, []), - ?line case port_ix(P1) < max_ports() of + P1 = erlang:open_port({spawn, "exit_drv"}, []), + case port_ix(P1) < max_ports() of true -> - ?line ?t:format("Need to wrap port index (~p)~n", [P1]), - ?line otp_5112_wrap_port_ix([P1]), - ?line P2 = erlang:open_port({spawn, "exit_drv"}, []), - ?line false = port_ix(P2) < max_ports(), - ?line P2; + ?t:format("Need to wrap port index (~p)~n", [P1]), + otp_5112_wrap_port_ix([P1]), + P2 = erlang:open_port({spawn, "exit_drv"}, []), + false = port_ix(P2) < max_ports(), + P2; false -> - ?line ?t:format("Port index already wrapped (~p)~n", [P1]), - ?line P1 + ?t:format("Port index already wrapped (~p)~n", [P1]), + P1 end. otp_5112_wrap_port_ix(Ports) -> - ?line case (catch erlang:open_port({spawn, "exit_drv"}, [])) of + case (catch erlang:open_port({spawn, "exit_drv"}, [])) of Port when is_port(Port) -> - ?line otp_5112_wrap_port_ix([Port|Ports]); + otp_5112_wrap_port_ix([Port|Ports]); _ -> %% Port table now full; empty port table - ?line lists:foreach(fun (P) -> P ! {self(), close} end, + lists:foreach(fun (P) -> P ! {self(), close} end, Ports), - ?line ok + ok end. @@ -1669,106 +1603,105 @@ otp_5119(suite) -> otp_5119(doc) -> ["Test that port index is not unnecessarily wrapped"]; otp_5119(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line Path = ?config(data_dir, Config), - ?line ok = load_driver(Path, "exit_drv"), - ?line PI1 = port_ix(otp_5119_fill_empty_port_tab([])), - ?line PI2 = port_ix(erlang:open_port({spawn, "exit_drv"}, [])), - ?line {PortIx1, PortIx2} - = case PI2 > PI1 of - true -> - ?line {PI1, PI2}; - false -> - ?line {port_ix(otp_5119_fill_empty_port_tab([PI2])), - port_ix(erlang:open_port({spawn, "exit_drv"}, []))} - end, - ?line MaxPorts = max_ports(), - ?line ?t:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]), - ?line ?t:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]), - ?line ?t:format("MaxPorts = ~p~n", [MaxPorts]), - ?line true = PortIx2 > PortIx1, - ?line true = PortIx2 =< PortIx1 + MaxPorts, - ?line test_server:timetrap_cancel(Dog), - ?line ok. + Dog = test_server:timetrap(test_server:seconds(10)), + Path = ?config(data_dir, Config), + ok = load_driver(Path, "exit_drv"), + PI1 = port_ix(otp_5119_fill_empty_port_tab([])), + PI2 = port_ix(erlang:open_port({spawn, "exit_drv"}, [])), + {PortIx1, PortIx2} = case PI2 > PI1 of + true -> + {PI1, PI2}; + false -> + {port_ix(otp_5119_fill_empty_port_tab([PI2])), + port_ix(erlang:open_port({spawn, "exit_drv"}, []))} + end, + MaxPorts = max_ports(), + ?t:format("PortIx1 = ~p ~p~n", [PI1, PortIx1]), + ?t:format("PortIx2 = ~p ~p~n", [PI2, PortIx2]), + ?t:format("MaxPorts = ~p~n", [MaxPorts]), + true = PortIx2 > PortIx1, + true = PortIx2 =< PortIx1 + MaxPorts, + test_server:timetrap_cancel(Dog), + ok. otp_5119_fill_empty_port_tab(Ports) -> - ?line case (catch erlang:open_port({spawn, "exit_drv"}, [])) of + case (catch erlang:open_port({spawn, "exit_drv"}, [])) of Port when is_port(Port) -> - ?line otp_5119_fill_empty_port_tab([Port|Ports]); + otp_5119_fill_empty_port_tab([Port|Ports]); _ -> %% Port table now full; empty port table - ?line lists:foreach(fun (P) -> P ! {self(), close} end, + lists:foreach(fun (P) -> P ! {self(), close} end, Ports), - ?line [LastPort|_] = Ports, - ?line LastPort + [LastPort|_] = Ports, + LastPort end. -define(DEF_MAX_PORTS, 1024). max_ports_env() -> - ?line case os:getenv("ERL_MAX_PORTS") of + case os:getenv("ERL_MAX_PORTS") of EMP when is_list(EMP) -> case catch list_to_integer(EMP) of - Int when is_integer(Int) -> ?line Int; - _ -> ?line false + Int when is_integer(Int) -> Int; + _ -> false end; - _ -> ?line false + _ -> false end. max_ports() -> - ?line PreMaxPorts + PreMaxPorts = case max_ports_env() of - Env when is_integer(Env) -> ?line Env; + Env when is_integer(Env) -> Env; _ -> - ?line case os:type() of + case os:type() of {unix, _} -> - ?line UlimStr = string:strip(os:cmd("ulimit -n") + UlimStr = string:strip(os:cmd("ulimit -n") -- "\n"), - ?line case catch list_to_integer(UlimStr) of - Ulim when is_integer(Ulim) -> ?line Ulim; - _ -> ?line ?DEF_MAX_PORTS + case catch list_to_integer(UlimStr) of + Ulim when is_integer(Ulim) -> Ulim; + _ -> ?DEF_MAX_PORTS end; - _ -> ?line ?DEF_MAX_PORTS + _ -> ?DEF_MAX_PORTS end end, - ?line case PreMaxPorts > ?DEF_MAX_PORTS of - true -> ?line PreMaxPorts; - false -> ?line ?DEF_MAX_PORTS + case PreMaxPorts > ?DEF_MAX_PORTS of + true -> PreMaxPorts; + false -> ?DEF_MAX_PORTS end. port_ix(Port) when is_port(Port) -> - ?line ["#Port",_,PortIxStr] = string:tokens(erlang:port_to_list(Port), + ["#Port",_,PortIxStr] = string:tokens(erlang:port_to_list(Port), "<.>"), - ?line list_to_integer(PortIxStr). + list_to_integer(PortIxStr). otp_6224(doc) -> ["Check that port command failure doesn't crash the emulator"]; otp_6224(suite) -> []; otp_6224(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line Path = ?config(data_dir, Config), - ?line ok = load_driver(Path, "failure_drv"), - ?line Go = make_ref(), - ?line Failer = spawn(fun () -> + Dog = test_server:timetrap(test_server:seconds(10)), + Path = ?config(data_dir, Config), + ok = load_driver(Path, "failure_drv"), + Go = make_ref(), + Failer = spawn(fun () -> receive Go -> ok end, - ?line Port = open_port({spawn, "failure_drv"}, + Port = open_port({spawn, "failure_drv"}, []), Port ! {self(), {command, "Fail, please!"}}, otp_6224_loop() end), - ?line Mon = erlang:monitor(process, Failer), - ?line Failer ! Go, - ?line receive + Mon = erlang:monitor(process, Failer), + Failer ! Go, + receive {'DOWN', Mon, process, Failer, Reason} -> - ?line case Reason of - {driver_failed, _} -> ?line ok; - driver_failed -> ?line ok; - _ -> ?line ?t:fail({unexpected_exit_reason, + case Reason of + {driver_failed, _} -> ok; + driver_failed -> ok; + _ -> ?t:fail({unexpected_exit_reason, Reason}) end end, - ?line test_server:timetrap_cancel(Dog), - ?line ok. + test_server:timetrap_cancel(Dog), + ok. otp_6224_loop() -> receive _ -> ok after 0 -> ok end, @@ -1781,11 +1714,11 @@ otp_6224_loop() -> exit_status_multi_scheduling_block(doc) -> []; exit_status_multi_scheduling_block(suite) -> []; exit_status_multi_scheduling_block(Config) when is_list(Config) -> - ?line Repeat = 3, - ?line case ?t:os_type() of + Repeat = 3, + case ?t:os_type() of {unix, _} -> - ?line Dog = ?t:timetrap(test_server:minutes(2*Repeat)), - ?line SleepSecs = 6, + Dog = ?t:timetrap(test_server:minutes(2*Repeat)), + SleepSecs = 6, try lists:foreach(fun (_) -> exit_status_msb_test(Config, @@ -1799,7 +1732,7 @@ exit_status_multi_scheduling_block(Config) when is_list(Config) -> ?t:timetrap_cancel(Dog), receive after SleepSecs+500 -> ok end end; - _ -> ?line {skip, "Not implemented for this OS"} + _ -> {skip, "Not implemented for this OS"} end. exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> @@ -1808,22 +1741,22 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> %% and we want these port programs to terminate while multi-scheduling %% is blocked. %% - ?line NoSchedsOnln = erlang:system_info(schedulers_online), - ?line Parent = self(), - ?line ?t:format("SleepSecs = ~p~n", [SleepSecs]), - ?line PortProg = "sleep " ++ integer_to_list(SleepSecs), - ?line Start = now(), - ?line NoProcs = case NoSchedsOnln of + NoSchedsOnln = erlang:system_info(schedulers_online), + Parent = self(), + ?t:format("SleepSecs = ~p~n", [SleepSecs]), + PortProg = "sleep " ++ integer_to_list(SleepSecs), + Start = now(), + NoProcs = case NoSchedsOnln of NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS -> NProcs; _ -> ?EXIT_STATUS_MSB_MAX_PROCS end, - ?line NoPortsPerProc = case 20*NoProcs of + NoPortsPerProc = case 20*NoProcs of TNPorts when TNPorts < ?EXIT_STATUS_MSB_MAX_PORTS -> 20; _ -> ?EXIT_STATUS_MSB_MAX_PORTS div NoProcs end, - ?line ?t:format("NoProcs = ~p~nNoPortsPerProc = ~p~n", + ?t:format("NoProcs = ~p~nNoPortsPerProc = ~p~n", [NoProcs, NoPortsPerProc]), ProcFun = fun () -> @@ -1873,32 +1806,32 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> PrtSIds), Parent ! {self(), done} end, - ?line Procs = lists:map(fun (N) -> + Procs = lists:map(fun (N) -> spawn_opt(ProcFun, [link, {scheduler, (N rem NoSchedsOnln)+1}]) end, lists:seq(1, NoProcs)), - ?line SIds = lists:map(fun (P) -> + SIds = lists:map(fun (P) -> receive {P, started, SIds} -> SIds end end, Procs), - ?line StartedTime = timer:now_diff(now(), Start)/1000000, - ?line ?t:format("StartedTime = ~p~n", [StartedTime]), - ?line true = StartedTime < SleepSecs, - ?line erlang:system_flag(multi_scheduling, block), - ?line lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs), - ?line DoneTime = timer:now_diff(now(), Start)/1000000, - ?line ?t:format("DoneTime = ~p~n", [DoneTime]), - ?line true = DoneTime > SleepSecs, - ?line ok = verify_multi_scheduling_blocked(), - ?line erlang:system_flag(multi_scheduling, unblock), - ?line case {length(lists:usort(lists:flatten(SIds))), NoSchedsOnln} of + StartedTime = timer:now_diff(now(), Start)/1000000, + ?t:format("StartedTime = ~p~n", [StartedTime]), + true = StartedTime < SleepSecs, + erlang:system_flag(multi_scheduling, block), + lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs), + DoneTime = timer:now_diff(now(), Start)/1000000, + ?t:format("DoneTime = ~p~n", [DoneTime]), + true = DoneTime > SleepSecs, + ok = verify_multi_scheduling_blocked(), + erlang:system_flag(multi_scheduling, unblock), + case {length(lists:usort(lists:flatten(SIds))), NoSchedsOnln} of {N, N} -> - ?line ok; + ok; {N, M} -> - ?line ?t:fail("Failed to create ports on all" + ?t:fail("Failed to create ports on all" ++ integer_to_list(M) ++ " available" "schedulers. Only created ports on " ++ integer_to_list(N) ++ " schedulers.") @@ -1921,18 +1854,18 @@ sid_proc(SIds) -> end. verify_multi_scheduling_blocked() -> - ?line Procs = lists:map(fun (_) -> + Procs = lists:map(fun (_) -> spawn_link(fun () -> sid_proc([]) end) end, lists:seq(1, 3*erlang:system_info(schedulers_online))), - ?line receive after 1000 -> ok end, - ?line SIds = lists:map(fun (P) -> + receive after 1000 -> ok end, + SIds = lists:map(fun (P) -> P ! {self(), want_sids}, receive {P, sids, PSIds} -> PSIds end end, Procs), - ?line 1 = length(lists:usort(lists:flatten(SIds))), - ?line ok. + 1 = length(lists:usort(lists:flatten(SIds))), + ok. %%% Pinging functions. @@ -1993,30 +1926,30 @@ build_cmd_line(FixedCmdLine, [], Result) -> port_expect(Config, Actions, HSize, CmdLine, Options0) -> % io:format("port_expect(~p, ~p, ~p, ~p)", % [Actions, HSize, CmdLine, Options0]), - ?line PortTest = port_test(Config), - ?line Cmd = lists:concat([PortTest, " -h", HSize, " ", CmdLine]), - ?line PortType = + PortTest = port_test(Config), + Cmd = lists:concat([PortTest, " -h", HSize, " ", CmdLine]), + PortType = case HSize of 0 -> stream; _ -> {packet, HSize} end, - ?line Options = [PortType|Options0], - ?line io:format("open_port({spawn, ~p}, ~p)", [Cmd, Options]), - ?line Port = open_port({spawn, Cmd}, Options), - ?line port_expect(Port, Actions, Options), + Options = [PortType|Options0], + io:format("open_port({spawn, ~p}, ~p)", [Cmd, Options]), + Port = open_port({spawn, Cmd}, Options), + port_expect(Port, Actions, Options), Port. port_expect(Port, [{Send, Expects}|Rest], Options) when is_list(Expects) -> - ?line port_send(Port, Send), - ?line IsBinaryPort = lists:member(binary, Options), - ?line Receiver = + port_send(Port, Send), + IsBinaryPort = lists:member(binary, Options), + Receiver = case {lists:member(stream, Options), line_option(Options)} of {false, _} -> fun receive_all/2; {true,false} -> fun stream_receive_all/2; {_, true} -> fun receive_all/2 end, - ?line Receiver(Port, maybe_to_binary(Expects, IsBinaryPort)), - ?line port_expect(Port, Rest, Options); + Receiver(Port, maybe_to_binary(Expects, IsBinaryPort)), + port_expect(Port, Rest, Options); port_expect(_, [], _) -> ok. @@ -2068,7 +2001,7 @@ receive_all(Port, [Expect|Rest]) -> _ -> %%% io:format("Unexpected message: ~s", [format(Other)]), io:format("Unexpected message: ~w", [Other]), - ?line test_server:fail(unexpected_message) + test_server:fail(unexpected_message) end end, receive_all(Port, Rest); @@ -2157,15 +2090,8 @@ build_packet(Left, Result, NextChar0) -> build_packet(Left-1, [NextChar0|Result], NextChar). sizes() -> - case os:type() of - vxworks -> - % don't stress VxWorks too much - [10, 13, 64, 127, 128, 255, 256, 1023, 1024, - 8191, 8192, 16383, 16384]; - _ -> - [10, 13, 64, 127, 128, 255, 256, 1023, 1024, - 32767, 32768, 65535, 65536] - end. + [10, 13, 64, 127, 128, 255, 256, 1023, 1024, + 32767, 32768, 65535, 65536]. sizes(Header_Size) -> sizes(Header_Size, sizes(), []). @@ -2203,18 +2129,18 @@ fun_spawn(Fun, Args) -> spawn_link(erlang, apply, [Fun, Args]). port_test(Config) when is_list(Config) -> - ?line filename:join(?config(data_dir, Config), "port_test"). + filename:join(?config(data_dir, Config), "port_test"). ports(doc) -> "Test that erlang:ports/0 returns a consistent snapshot of ports"; ports(suite) -> []; ports(Config) when is_list(Config) -> - ?line Path = ?config(data_dir, Config), - ?line ok = load_driver(Path, "exit_drv"), + Path = ?config(data_dir, Config), + ok = load_driver(Path, "exit_drv"), receive after 1000 -> ok end, % Wait for other ports to stabilize - ?line OtherPorts = erlang:ports(), + OtherPorts = erlang:ports(), io:format("Other ports: ~p\n",[OtherPorts]), MaxPorts = 1024 - length(OtherPorts), @@ -2222,7 +2148,7 @@ ports(Config) when is_list(Config) -> ports_snapshots(100, TrafficPid, OtherPorts), TrafficPid ! {self(),die}, - ?line receive {TrafficPid, dead} -> ok end, + receive {TrafficPid, dead} -> ok end, ok. ports_snapshots(0, _, _) -> @@ -2230,12 +2156,12 @@ ports_snapshots(0, _, _) -> ports_snapshots(Iter, TrafficPid, OtherPorts) -> TrafficPid ! start, - ?line receive after 1 -> ok end, + receive after 1 -> ok end, Snapshot = erlang:ports(), TrafficPid ! {self(), stop}, - ?line receive {TrafficPid, EventList, TrafficPorts} -> ok end, + receive {TrafficPid, EventList, TrafficPorts} -> ok end, %%io:format("Snapshot=~p\n", [Snapshot]), ports_verify(Snapshot, OtherPorts ++ TrafficPorts, EventList), @@ -2252,7 +2178,7 @@ ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) -> %%io:format("Traffic started in ~p\n",[self()]), ports_traffic_started(MaxPorts, {PortList, PortCnt}, []); {Pid,die} -> - ?line lists:foreach(fun(Port)-> erlang:port_close(Port) end, + lists:foreach(fun(Port)-> erlang:port_close(Port) end, PortList), Pid ! {self(),dead} end. @@ -2272,15 +2198,15 @@ ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) -> N = uniform(MaxPorts), case N > PortCnt of true -> % Open port - ?line P = open_port({spawn, "exit_drv"}, []), + P = open_port({spawn, "exit_drv"}, []), %%io:format("Created port ~p\n",[P]), ports_traffic_started(MaxPorts, {[P|PortList], PortCnt+1}, [{open,P}|EventList]); false -> % Close port - ?line P = lists:nth(N, PortList), + P = lists:nth(N, PortList), %%io:format("Close port ~p\n",[P]), - ?line true = erlang:port_close(P), + true = erlang:port_close(P), ports_traffic_started(MaxPorts, {lists:delete(P,PortList), PortCnt-1}, [{close,P}|EventList]) end. @@ -2301,7 +2227,7 @@ ports_verify(Ports, PortsAfter, EventList) -> ports_verify(Ports, [P | PortsAfter], Tail); [] -> - ?line test_server:fail("Inconsistent snapshot from erlang:ports()") + test_server:fail("Inconsistent snapshot from erlang:ports()") end end. @@ -2318,27 +2244,27 @@ close_deaf_port(doc) -> ["Send data to port program that does not read it, then "Primary targeting Windows to test threaded_handle_closer in sys.c"]; close_deaf_port(suite) -> []; close_deaf_port(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(100)), - ?line DataDir = ?config(data_dir, Config), - ?line DeadPort = os:find_executable("dead_port", DataDir), - ?line Port = open_port({spawn,DeadPort++" 60"},[]), - ?line erlang:port_command(Port,"Hello, can you hear me!?!?"), - ?line port_close(Port), + Dog = test_server:timetrap(test_server:seconds(100)), + DataDir = ?config(data_dir, Config), + DeadPort = os:find_executable("dead_port", DataDir), + Port = open_port({spawn,DeadPort++" 60"},[]), + erlang:port_command(Port,"Hello, can you hear me!?!?"), + port_close(Port), Res = close_deaf_port_1(0, DeadPort), io:format("Waiting for OS procs to terminate...\n"), receive after 5*1000 -> ok end, - ?line test_server:timetrap_cancel(Dog), + test_server:timetrap_cancel(Dog), Res. close_deaf_port_1(1000, _) -> ok; close_deaf_port_1(N, Cmd) -> Timeout = integer_to_list(random:uniform(5*1000)), - ?line try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of + try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of Port -> - ?line erlang:port_command(Port,"Hello, can you hear me!?!?"), - ?line port_close(Port), + erlang:port_command(Port,"Hello, can you hear me!?!?"), + port_close(Port), close_deaf_port_1(N+1, Cmd) catch _:eagain -> diff --git a/erts/emulator/test/port_SUITE_data/dead_port.c b/erts/emulator/test/port_SUITE_data/dead_port.c index 68e96fbf14..4dd9ee4cc2 100644 --- a/erts/emulator/test/port_SUITE_data/dead_port.c +++ b/erts/emulator/test/port_SUITE_data/dead_port.c @@ -17,15 +17,6 @@ * %CopyrightEnd% */ -#ifdef VXWORKS -#include <vxWorks.h> -#include <taskVarLib.h> -#include <taskLib.h> -#include <sysLib.h> -#include <string.h> -#include <ioLib.h> -#endif - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -37,12 +28,7 @@ #ifndef __WIN32__ #include <unistd.h> -#ifdef VXWORKS -#include "reclaim.h" -#include <sys/times.h> -#else #include <sys/time.h> -#endif #define O_BINARY 0 #define _setmode(fd, mode) @@ -53,13 +39,7 @@ #include "winbase.h" #endif - -#ifdef VXWORKS -#define MAIN(argc, argv) port_test(argc, argv) -#else #define MAIN(argc, argv) main(argc, argv) -#endif - extern int errno; @@ -86,9 +66,6 @@ char *argv[]; static void delay(unsigned ms) { -#ifdef VXWORKS - taskDelay((sysClkRateGet() * ms) / 1000); -#else #ifdef __WIN32__ Sleep(ms); #else @@ -98,5 +75,4 @@ delay(unsigned ms) select(0, NULL, NULL, NULL, &t); #endif -#endif } diff --git a/erts/emulator/test/port_SUITE_data/port_test.c b/erts/emulator/test/port_SUITE_data/port_test.c index 7b4e386d87..7abefab2e3 100644 --- a/erts/emulator/test/port_SUITE_data/port_test.c +++ b/erts/emulator/test/port_SUITE_data/port_test.c @@ -3,15 +3,6 @@ * Purpose: A port program to be used for testing the open_port bif. */ -#ifdef VXWORKS -#include <vxWorks.h> -#include <taskVarLib.h> -#include <taskLib.h> -#include <sysLib.h> -#include <string.h> -#include <ioLib.h> -#endif - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -23,12 +14,7 @@ #ifndef __WIN32__ #include <unistd.h> -#ifdef VXWORKS -#include "reclaim.h" -#include <sys/times.h> -#else #include <sys/time.h> -#endif #define O_BINARY 0 #define _setmode(fd, mode) @@ -40,22 +26,13 @@ #endif -#ifdef VXWORKS -#define REDIR_STDOUT(fd) ioTaskStdSet(0, 1, fd); -#else #define REDIR_STDOUT(fd) if (dup2(fd, 1) == -1) { \ fprintf(stderr, "%s: failed to duplicate handle %d to 1: %d\n", \ port_data->progname, fd, errno); \ exit(1); \ } -#endif -#ifdef VXWORKS -#define MAIN(argc, argv) port_test(argc, argv) -#else #define MAIN(argc, argv) main(argc, argv) -#endif - extern int errno; @@ -101,7 +78,6 @@ static void dump(unsigned char* buf, int sz, int max); static void replace_stdout(char* filename); static void generate_reply(char* spec); -#ifndef VXWORKS #ifndef HAVE_STRERROR extern int sys_nerr; #ifndef sys_errlist /* sys_errlist is sometimes defined to @@ -125,7 +101,6 @@ int err; return msgstr; } #endif -#endif MAIN(argc, argv) @@ -133,12 +108,6 @@ int argc; char *argv[]; { int ret; -#ifdef VXWORKS - if(taskVarAdd(0, (int *)&port_data) != OK) { - fprintf(stderr, "Can't do taskVarAdd in port_test\n"); - exit(1); - } -#endif if((port_data = (PORT_TEST_DATA *) malloc(sizeof(PORT_TEST_DATA))) == NULL) { fprintf(stderr, "Couldn't malloc for port_data"); exit(1); @@ -511,9 +480,6 @@ dump(buf, sz, max) static void delay(unsigned ms) { -#ifdef VXWORKS - taskDelay((sysClkRateGet() * ms) / 1000); -#else #ifdef __WIN32__ Sleep(ms); #else @@ -523,7 +489,6 @@ delay(unsigned ms) select(0, NULL, NULL, NULL, &t); #endif -#endif } /* diff --git a/erts/emulator/test/port_SUITE_data/reclaim.h b/erts/emulator/test/port_SUITE_data/reclaim.h deleted file mode 100644 index 1d57dc5b8a..0000000000 --- a/erts/emulator/test/port_SUITE_data/reclaim.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef __RECLAIM_H__ -#define __RECLAIM_H__ - - -/* The Erlang release for VxWorks includes a simple mechanism for - "resource reclamation" at task exit - it allows replacement of the - functions that open/close "files" and malloc/free memory with versions - that keep track, to be able to "reclaim" file descriptors and memory - when a task exits (regardless of *how* it exits). - - The interface to this mechanism is made available via this file, - with the following caveats: - - - The interface may change (or perhaps even be removed, though that - isn't likely until VxWorks itself provides similar functionality) - in future releases - i.e. you must always use the version of this - file that comes with the Erlang release you are using. - - - Disaster is guaranteed if you use the mechanism incorrectly (see - below for the correct way), e.g. allocate memory with the "tracking" - version of malloc() and free it with the "standard" version of free(). - - - The mechanism (of course) incurs some performance penalty - thus - for a simple program you may be better off with careful programming, - making sure that you do whatever close()/free()/etc calls that are - appropriate at all exit points (though if you need to guard against - taskDelete() etc, things get messy...). - - To use the mechanism, simply program your application normally, i.e. - use open()/close()/malloc()/free() etc as usual, but #include this - file before any usage of the relevant functions. NOTE: To avoid the - "disaster" mentioned above, you *must* #include it in *all* (or none) - of the files that manipulate a particular file descriptor, allocated - memory area, etc. Finally, note that you can obviously not load your - application before the Erlang system when using this interface. -*/ - -/* Sorry, no ANSI prototypes yet... */ -extern int save_open(),save_creat(),save_socket(),save_accept(),save_close(); -#define open save_open -#define creat save_creat -#define socket save_socket -#define accept save_accept -#define close save_close -extern FILE *save_fopen(), *save_fdopen(), *save_freopen(); -extern int save_fclose(); -#define fopen save_fopen -#define fdopen save_fdopen -#define freopen save_freopen -#define fclose save_fclose -/* XXX Should do opendir/closedir too... */ -extern char *save_malloc(), *save_calloc(), *save_realloc(); -extern void save_free(), save_cfree(); -#define malloc save_malloc -#define calloc save_calloc -#define realloc save_realloc -#define free save_free -#define cfree save_cfree - -#endif diff --git a/erts/emulator/test/port_bif_SUITE.erl b/erts/emulator/test/port_bif_SUITE.erl index d9c82aba0e..8feea87d7e 100644 --- a/erts/emulator/test/port_bif_SUITE.erl +++ b/erts/emulator/test/port_bif_SUITE.erl @@ -24,6 +24,7 @@ init_per_group/2,end_per_group/2, command/1, command_e_1/1, command_e_2/1, command_e_3/1, command_e_4/1, port_info1/1, port_info2/1, + port_info_os_pid/1, connect/1, control/1, echo_to_busy/1]). -export([do_command_e_1/1, do_command_e_2/1, do_command_e_4/1]). @@ -41,7 +42,7 @@ all() -> groups() -> [{command_e, [], [command_e_1, command_e_2, command_e_3, command_e_4]}, - {port_info, [], [port_info1, port_info2]}]. + {port_info, [], [port_info1, port_info2, port_info_os_pid]}]. init_per_suite(Config) -> Config. @@ -65,15 +66,15 @@ end_per_testcase(_Func, Config) when is_list(Config) -> test_server:timetrap_cancel(Dog). command(Config) when is_list(Config) -> - ?line load_control_drv(Config), - - ?line P = open_port({spawn, control_drv}, []), - ?line do_command(P, "hello"), - ?line do_command(P, <<"hello">>), - ?line do_command(P, sub_bin(<<"1234kalle">>)), - ?line do_command(P, unaligned_sub_bin(<<"blurf">>)), - ?line do_command(P, ["bl"|unaligned_sub_bin(<<"urf">>)]), - ?line true = erlang:port_close(P), + load_control_drv(Config), + + P = open_port({spawn, control_drv}, []), + do_command(P, "hello"), + do_command(P, <<"hello">>), + do_command(P, sub_bin(<<"1234kalle">>)), + do_command(P, unaligned_sub_bin(<<"blurf">>)), + do_command(P, ["bl"|unaligned_sub_bin(<<"urf">>)]), + true = erlang:port_close(P), ok. do_command(P, Data) -> @@ -94,139 +95,163 @@ do_command(P, Data) -> %% port_command/2: badarg 1st arg command_e_1(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line _ = spawn_link(?MODULE, do_command_e_1, [Program]), - ?line receive - {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> - ok; - Other -> - ?line test_server:fail(Other) - after 10000 -> - ?line test_server:fail(timeout) - end, + _ = spawn_link(?MODULE, do_command_e_1, [Program]), + receive + {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. do_command_e_1(Program) -> - ?line _ = open_port({spawn, Program}, []), - ?line erlang:port_command(apple, "plock"), + _ = open_port({spawn, Program}, []), + erlang:port_command(apple, "plock"), exit(survived). %% port_command/2: badarg 2nd arg command_e_2(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line _ = spawn_link(?MODULE, do_command_e_2, [Program]), - ?line receive - {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> - ok; - Other -> - ?line test_server:fail(Other) - after 10000 -> - ?line test_server:fail(timeout) - end, + _ = spawn_link(?MODULE, do_command_e_2, [Program]), + receive + {'EXIT', Pid, {badarg, _}} when is_pid(Pid) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. do_command_e_2(Program) -> - ?line P = open_port({spawn, Program}, []), - ?line erlang:port_command(P, 1), + P = open_port({spawn, Program}, []), + erlang:port_command(P, 1), exit(survived). %% port_command/2: Posix signals trapped command_e_3(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line P = open_port({spawn, Program}, [{packet, 1}]), - ?line Data = lists:duplicate(257, $a), - ?line erlang:port_command(P, Data), - ?line receive - {'EXIT', Port, einval} when is_port(Port) -> - ok; - Other -> - test_server:fail(Other) - after 10000 -> - test_server:fail(timeout) - end, + P = open_port({spawn, Program}, [{packet, 1}]), + Data = lists:duplicate(257, $a), + erlang:port_command(P, Data), + receive + {'EXIT', Port, einval} when is_port(Port) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. %% port_command/2: Posix exit signals not trapped command_e_4(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Program = filename:join(DataDir, "port_test"), + DataDir = ?config(data_dir, Config), + Program = filename:join(DataDir, "port_test"), process_flag(trap_exit, true), - ?line _ = spawn_link(?MODULE, do_command_e_4, [Program]), - ?line receive - {'EXIT', Pid, {einval, _}} when is_pid(Pid) -> - ok; - Other -> - ?line test_server:fail(Other) - after 10000 -> - ?line test_server:fail(timeout) - end, + _ = spawn_link(?MODULE, do_command_e_4, [Program]), + receive + {'EXIT', Pid, {einval, _}} when is_pid(Pid) -> + ok; + Other -> + test_server:fail(Other) + after 10000 -> + test_server:fail(timeout) + end, ok. do_command_e_4(Program) -> - ?line P = open_port({spawn, Program}, [{packet, 1}]), - ?line Data = lists:duplicate(257, $a), - ?line erlang:port_command(P, Data), + P = open_port({spawn, Program}, [{packet, 1}]), + Data = lists:duplicate(257, $a), + erlang:port_command(P, Data), exit(survived). %% Tests the port_info/1 BIF port_info1(Config) when is_list(Config) -> - ?line load_control_drv(Config), + load_control_drv(Config), Me=self(), - ?line P = open_port({spawn, control_drv}, []), - ?line A1 = erlang:port_info(P), - ?line false = lists:keysearch(registered_name, 1, A1), - ?line register(myport, P), - ?line A = erlang:port_info(P), - ?line {value,{registered_name,myport}}= - lists:keysearch(registered_name, 1, A), - ?line {value,{name,"control_drv"}}=lists:keysearch(name, 1, A), - ?line {value,{links,[Me]}}=lists:keysearch(links, 1, A), - ?line {value,{id,_IdNum}}=lists:keysearch(id, 1, A), - ?line {value,{connected,_}}=lists:keysearch(connected, 1, A), - ?line {value,{input,0}}=lists:keysearch(input, 1, A), - ?line {value,{output,0}}=lists:keysearch(output, 1, A), - ?line true=erlang:port_close(P), + P = open_port({spawn, control_drv}, []), + A1 = erlang:port_info(P), + false = lists:keysearch(registered_name, 1, A1), + register(myport, P), + A = erlang:port_info(P), + {value,{registered_name,myport}}= lists:keysearch(registered_name, 1, A), + {value,{name,"control_drv"}}=lists:keysearch(name, 1, A), + {value,{links,[Me]}}=lists:keysearch(links, 1, A), + {value,{id,_IdNum}}=lists:keysearch(id, 1, A), + {value,{connected,_}}=lists:keysearch(connected, 1, A), + {value,{input,0}}=lists:keysearch(input, 1, A), + {value,{output,0}}=lists:keysearch(output, 1, A), + {value,{os_pid,undefined}}=lists:keysearch(os_pid, 1, A), % linked-in driver doesn't have a OS pid + true=erlang:port_close(P), ok. %% Tests erlang:port_info/2" port_info2(Config) when is_list(Config) -> - ?line load_control_drv(Config), + load_control_drv(Config), - ?line P = open_port({spawn,control_drv}, [binary]), - ?line [] = erlang:port_info(P, registered_name), - ?line register(myport, P), - ?line {registered_name, myport} = erlang:port_info(P, registered_name), + P = open_port({spawn,control_drv}, [binary]), + [] = erlang:port_info(P, registered_name), + register(myport, P), + {registered_name, myport} = erlang:port_info(P, registered_name), - ?line {name, "control_drv"}=erlang:port_info(P, name), - ?line {id, _IdNum} = erlang:port_info(P, id), + {name, "control_drv"}=erlang:port_info(P, name), + {id, _IdNum} = erlang:port_info(P, id), Me=self(), - ?line {links, [Me]} = erlang:port_info(P, links), - ?line {connected, Me} = erlang:port_info(P, connected), - ?line {input, 0}=erlang:port_info(P, input), - ?line {output,0}=erlang:port_info(P, output), - - ?line erlang:port_control(P, $i, "abc"), - ?line receive - {P,{data,<<"abc">>}} -> ok - end, - ?line {input,3} = erlang:port_info(P, input), - ?line {output,0} = erlang:port_info(P, output), - - ?line Bin = list_to_binary(lists:duplicate(2047, 42)), - ?line output_test(P, Bin, 3, 0), + {links, [Me]} = erlang:port_info(P, links), + {connected, Me} = erlang:port_info(P, connected), + {input, 0}=erlang:port_info(P, input), + {output,0}=erlang:port_info(P, output), + {os_pid, undefined}=erlang:port_info(P, os_pid), % linked-in driver doesn't have a OS pid + + erlang:port_control(P, $i, "abc"), + receive + {P,{data,<<"abc">>}} -> ok + end, + {input,3} = erlang:port_info(P, input), + {output,0} = erlang:port_info(P, output), + + Bin = list_to_binary(lists:duplicate(2047, 42)), + output_test(P, Bin, 3, 0), - ?line true = erlang:port_close(P), + true = erlang:port_close(P), + ok. + +%% Tests the port_info/1,2 os_pid option BIF +port_info_os_pid(Config) when is_list(Config) -> + case os:type() of + {unix,_} -> + do_port_info_os_pid(); + _ -> + {skip,"Only on Unix."} + end. + +do_port_info_os_pid() -> + P = open_port({spawn, "echo $$"}, [eof]), + A = erlang:port_info(P), + {os_pid, InfoOSPid} = erlang:port_info(P, os_pid), + EchoPidStr = receive + {P, {data, EchoPidStr0}} -> EchoPidStr0 + after 10000 -> test_server:fail(timeout) + end, + {ok, [EchoPid], []} = io_lib:fread("~u\n", EchoPidStr), + {value,{os_pid, InfoOSPid}}=lists:keysearch(os_pid, 1, A), + EchoPid = InfoOSPid, + true = erlang:port_close(P), ok. output_test(_, _, Input, Output) when Output > 16#1fffffff -> @@ -237,7 +262,7 @@ output_test(P, Bin, Input0, Output0) -> {P,{data,Bin}} -> ok; Other -> io:format("~p", [Other]), - ?line ?t:fail() + ?t:fail() end, Input = Input0 + size(Bin), Output = Output0 + size(Bin), @@ -254,109 +279,106 @@ output_test(P, Bin, Input0, Output0) -> %% Tests the port_connect/2 BIF. connect(Config) when is_list(Config) -> - ?line load_control_drv(Config), + load_control_drv(Config), - ?line P = open_port({spawn, control_drv}, []), + P = open_port({spawn, control_drv}, []), register(myport, P), - ?line true = erlang:port_connect(myport, self()), + true = erlang:port_connect(myport, self()), %% Connect the port to another process. Data = "hello, world", Parent = self(), - ?line Rec = - fun(Me) -> receive - {P,{data,Data}} -> - Parent ! connect_ok, - Me(Me) - end - end, - ?line RecPid = spawn_link(fun() -> Rec(Rec) end), - ?line true = erlang:port_connect(P, RecPid), - ?line unlink(P), + Rec = fun(Me) -> + receive + {P,{data,Data}} -> + Parent ! connect_ok, + Me(Me) + end + end, + RecPid = spawn_link(fun() -> Rec(Rec) end), + true = erlang:port_connect(P, RecPid), + unlink(P), %% Send a command to the port and make sure that the %% other process receives the echo. - ?line erlang:port_command(P, Data), - ?line receive - connect_ok -> ok - end, + erlang:port_command(P, Data), + receive + connect_ok -> ok + end, %% Tests some errors. - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), self())), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), P)), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, P)), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, xxxx)), - ?line {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, [])), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), self())), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(self(), P)), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, P)), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, xxxx)), + {'EXIT',{badarg, _}}=(catch erlang:port_connect(P, [])), - ?line process_flag(trap_exit, true), - ?line exit(P, you_should_die), - ?line receive - {'EXIT',RecPid,you_should_die} -> ok; - Other -> ?line ?t:fail({bad_message,Other}) - end, + process_flag(trap_exit, true), + exit(P, you_should_die), + receive + {'EXIT',RecPid,you_should_die} -> ok; + Other -> ?line ?t:fail({bad_message,Other}) + end, %% Done. ok. %% Tests port_control/3 control(Config) when is_list(Config) -> - ?line load_control_drv(Config), - ?line P = open_port({spawn, control_drv}, []), + load_control_drv(Config), + P = open_port({spawn, control_drv}, []), %% Test invalid (out-of-range) arguments. - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(self(), 1, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(self(), 1, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -1, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -34887348739733833, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 16#100000000, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, a, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 'e', dum)), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, dum)), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, fun(X) -> X end)), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, - [fun(X) -> X end])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, - [1|fun(X) -> X end])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -1, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, -34887348739733833, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 16#100000000, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, a, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 'e', dum)), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, dum)), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, fun(X) -> X end)), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, [fun(X) -> X end])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, $e, [1|fun(X) -> X end])), %% Test errors detected by the driver. - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 177, [])), - ?line {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 155, - random_packet(1024))), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 177, [])), + {'EXIT', {badarg, _}} = (catch erlang:port_control(P, 155, random_packet(1024))), %% Test big op codes. register(myport, P), - ?line test_op(myport, 256), - ?line test_op(P, 256), - ?line test_op(P, 16#0033A837), - ?line test_op(P, 16#0ab37938), - ?line test_op(P, 16#eab37938), - ?line test_op(P, 16#ffffFFFF), + test_op(myport, 256), + test_op(P, 256), + test_op(P, 16#0033A837), + test_op(P, 16#0ab37938), + test_op(P, 16#eab37938), + test_op(P, 16#ffffFFFF), %% Test the echo function of the driver. - ?line echo(P, 0), - ?line echo(P, 1), - ?line echo(P, 10), - ?line echo(P, 13), - ?line echo(P, 63), - ?line echo(P, 64), - ?line echo(P, 65), - ?line echo(P, 127), - ?line echo(P, 1023), - ?line echo(P, 1024), - ?line echo(P, 11243), - ?line echo(P, 70000), + echo(P, 0), + echo(P, 1), + echo(P, 10), + echo(P, 13), + echo(P, 63), + echo(P, 64), + echo(P, 65), + echo(P, 127), + echo(P, 1023), + echo(P, 1024), + echo(P, 11243), + echo(P, 70000), %% Done. - ?line true=erlang:port_close(myport), + true = erlang:port_close(myport), ok. test_op(P, Op) -> @@ -364,23 +386,23 @@ test_op(P, Op) -> <<Op:32>> = list_to_binary(R). echo_to_busy(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line load_control_drv(Config), - ?line P = open_port({spawn, control_drv}, []), - ?line erlang:port_control(P, $b, [1]), % Set to busy. + Dog = test_server:timetrap(test_server:seconds(10)), + load_control_drv(Config), + P = open_port({spawn, control_drv}, []), + erlang:port_control(P, $b, [1]), % Set to busy. Self = self(), - ?line Echoer = spawn(fun() -> echoer(P, Self) end), - ?line receive after 500 -> ok end, - ?line erlang:port_control(P, $b, [0]), % Set to not busy. - ?line receive - {Echoer, done} -> - ok; - {Echoer, Other} -> - test_server:fail(Other); - Other -> - test_server:fail({unexpected_message, Other}) - end, - ?line test_server:timetrap_cancel(Dog), + Echoer = spawn(fun() -> echoer(P, Self) end), + receive after 500 -> ok end, + erlang:port_control(P, $b, [0]), % Set to not busy. + receive + {Echoer, done} -> + ok; + {Echoer, Other} -> + test_server:fail(Other); + Other -> + test_server:fail({unexpected_message, Other}) + end, + test_server:timetrap_cancel(Dog), ok. echoer(P, ReplyTo) -> @@ -405,9 +427,9 @@ echo(P, Size) -> Packet = erlang:port_control(P, $e, [unaligned_sub_bin(Bin)]). load_control_drv(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line erl_ddll:start(), - ?line ok = load_driver(DataDir, "control_drv"). + DataDir = ?config(data_dir, Config), + erl_ddll:start(), + ok = load_driver(DataDir, "control_drv"). load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of @@ -459,4 +481,3 @@ sub_bin(Bin) when is_binary(Bin) -> B. id(I) -> I. - diff --git a/erts/emulator/test/port_bif_SUITE_data/port_test.c b/erts/emulator/test/port_bif_SUITE_data/port_test.c index c6b128df66..28324a56a6 100644 --- a/erts/emulator/test/port_bif_SUITE_data/port_test.c +++ b/erts/emulator/test/port_bif_SUITE_data/port_test.c @@ -3,15 +3,6 @@ * Purpose: A port program to be used for testing the open_port bif. */ -#ifdef VXWORKS -#include <vxWorks.h> -#include <taskVarLib.h> -#include <taskLib.h> -#include <sysLib.h> -#include <string.h> -#include <ioLib.h> -#endif - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -23,12 +14,7 @@ #ifndef __WIN32__ #include <unistd.h> -#ifdef VXWORKS -#include "reclaim.h" -#include <sys/times.h> -#else #include <sys/time.h> -#endif #define O_BINARY 0 #define _setmode(fd, mode) @@ -40,22 +26,13 @@ #endif -#ifdef VXWORKS -#define REDIR_STDOUT(fd) ioTaskStdSet(0, 1, fd); -#else #define REDIR_STDOUT(fd) if (dup2(fd, 1) == -1) { \ fprintf(stderr, "%s: failed to duplicate handle %d to 1: %d\n", \ port_data->progname, fd, errno); \ exit(1); \ } -#endif -#ifdef VXWORKS -#define MAIN(argc, argv) port_test(argc, argv) -#else #define MAIN(argc, argv) main(argc, argv) -#endif - extern int errno; @@ -101,7 +78,6 @@ static void dump(unsigned char* buf, int sz, int max); static void replace_stdout(char* filename); static void generate_reply(char* spec); -#ifndef VXWORKS #ifndef HAVE_STRERROR extern int sys_nerr; #ifndef sys_errlist /* sys_errlist is sometimes defined to @@ -125,20 +101,13 @@ int err; return msgstr; } #endif -#endif - MAIN(argc, argv) int argc; char *argv[]; { int ret; -#ifdef VXWORKS - if(taskVarAdd(0, (int *)&port_data) != OK) { - fprintf(stderr, "Can't do taskVarAdd in port_test\n"); - exit(1); - } -#endif + if((port_data = (PORT_TEST_DATA *) malloc(sizeof(PORT_TEST_DATA))) == NULL) { fprintf(stderr, "Couldn't malloc for port_data"); exit(1); @@ -508,9 +477,6 @@ dump(buf, sz, max) static void delay(unsigned ms) { -#ifdef VXWORKS - taskDelay((sysClkRateGet() * ms) / 1000); -#else #ifdef __WIN32__ Sleep(ms); #else @@ -520,7 +486,6 @@ delay(unsigned ms) select(0, NULL, NULL, NULL, &t); #endif -#endif } /* diff --git a/erts/emulator/test/port_bif_SUITE_data/reclaim.h b/erts/emulator/test/port_bif_SUITE_data/reclaim.h deleted file mode 100644 index 1d57dc5b8a..0000000000 --- a/erts/emulator/test/port_bif_SUITE_data/reclaim.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef __RECLAIM_H__ -#define __RECLAIM_H__ - - -/* The Erlang release for VxWorks includes a simple mechanism for - "resource reclamation" at task exit - it allows replacement of the - functions that open/close "files" and malloc/free memory with versions - that keep track, to be able to "reclaim" file descriptors and memory - when a task exits (regardless of *how* it exits). - - The interface to this mechanism is made available via this file, - with the following caveats: - - - The interface may change (or perhaps even be removed, though that - isn't likely until VxWorks itself provides similar functionality) - in future releases - i.e. you must always use the version of this - file that comes with the Erlang release you are using. - - - Disaster is guaranteed if you use the mechanism incorrectly (see - below for the correct way), e.g. allocate memory with the "tracking" - version of malloc() and free it with the "standard" version of free(). - - - The mechanism (of course) incurs some performance penalty - thus - for a simple program you may be better off with careful programming, - making sure that you do whatever close()/free()/etc calls that are - appropriate at all exit points (though if you need to guard against - taskDelete() etc, things get messy...). - - To use the mechanism, simply program your application normally, i.e. - use open()/close()/malloc()/free() etc as usual, but #include this - file before any usage of the relevant functions. NOTE: To avoid the - "disaster" mentioned above, you *must* #include it in *all* (or none) - of the files that manipulate a particular file descriptor, allocated - memory area, etc. Finally, note that you can obviously not load your - application before the Erlang system when using this interface. -*/ - -/* Sorry, no ANSI prototypes yet... */ -extern int save_open(),save_creat(),save_socket(),save_accept(),save_close(); -#define open save_open -#define creat save_creat -#define socket save_socket -#define accept save_accept -#define close save_close -extern FILE *save_fopen(), *save_fdopen(), *save_freopen(); -extern int save_fclose(); -#define fopen save_fopen -#define fdopen save_fdopen -#define freopen save_freopen -#define fclose save_fclose -/* XXX Should do opendir/closedir too... */ -extern char *save_malloc(), *save_calloc(), *save_realloc(); -extern void save_free(), save_cfree(); -#define malloc save_malloc -#define calloc save_calloc -#define realloc save_realloc -#define free save_free -#define cfree save_cfree - -#endif diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index fdc55a4cc5..6509871a7d 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -280,15 +280,7 @@ kb_128() -> big_binary()}. eight_kb() -> - %%% This is really much more than eight kb, so vxworks platforms - %%% gets away with 1/8 of the other platforms (due to limited - %%% memory resources). - B64 = case os:type() of - vxworks -> - ?line lists:seq(1, 8); - _ -> - ?line lists:seq(1, 64) - end, + B64 = lists:seq(1, 64), ?line B512 = {<<1>>,B64,<<2,3>>,B64,make_unaligned_sub_binary(<<4,5,6,7,8,9>>), B64,make_sub_binary([1,2,3,4,5,6]), B64,make_sub_binary(lists:seq(1, ?heap_binary_size+1)), diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index 7ff7449ff5..c9533d0748 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -71,38 +71,23 @@ end_per_group(_GroupName, Config) -> start_timer_1(doc) -> ["Basic start_timer/3 functionality"]; start_timer_1(Config) when is_list(Config) -> - ?line Ref1 = erlang:start_timer(1000, self(), plopp), - ?line ok = get(1100, {timeout, Ref1, plopp}), - - ?line false = erlang:read_timer(Ref1), - ?line false = erlang:cancel_timer(Ref1), - ?line false = erlang:read_timer(Ref1), - - ?line Ref2 = erlang:start_timer(1000, self(), plapp), - ?line Left2 = erlang:cancel_timer(Ref2), - UpperLimit = case os:type() of - vxworks -> - %% The ticks of vxworks have a far lesser granularity - %% than what is expected in this testcase, in - %% fact the Left2 variable can get a little more than 1000... - 1100; - _ -> - 1000 - end, - ?line RetVal = case os:type() of - vxworks -> - {comment, "VxWorks behaves slightly unexpected, should be fixed,"}; - _ -> - ok - end, - ?line true = (Left2 > 900) and (Left2 =< UpperLimit), - ?line empty = get_msg(), - ?line false = erlang:cancel_timer(Ref2), - - ?line Ref3 = erlang:start_timer(1000, self(), plopp), - ?line no_message = get(900, {timeout, Ref3, plopp}), - - RetVal. + Ref1 = erlang:start_timer(1000, self(), plopp), + ok = get(1100, {timeout, Ref1, plopp}), + + false = erlang:read_timer(Ref1), + false = erlang:cancel_timer(Ref1), + false = erlang:read_timer(Ref1), + + Ref2 = erlang:start_timer(1000, self(), plapp), + Left2 = erlang:cancel_timer(Ref2), + UpperLimit = 1000, + true = (Left2 > 900) and (Left2 =< UpperLimit), + empty = get_msg(), + false = erlang:cancel_timer(Ref2), + + Ref3 = erlang:start_timer(1000, self(), plopp), + no_message = get(900, {timeout, Ref3, plopp}), + ok. send_after_1(doc) -> ["Basic send_after/3 functionality"]; send_after_1(Config) when is_list(Config) -> @@ -153,19 +138,11 @@ send_after_2(Config) when is_list(Config) -> send_after_3(doc) -> ["send_after/3: messages in the right order, worse than send_after_2"]; send_after_3(Config) when is_list(Config) -> - case os:type() of - vxworks -> - {skipped, "VxWorks timer granularity and order is not working good, this is subject to change!"}; - _ -> - do_send_after_3() - end. - -do_send_after_3() -> - ?line _ = erlang:send_after(100, self(), b1), - ?line _ = erlang:send_after(101, self(), b2), - ?line _ = erlang:send_after(102, self(), b3), - ?line _ = erlang:send_after(103, self(), last), - ?line [b1, b2, b3, last] = collect(last), + _ = erlang:send_after(100, self(), b1), + _ = erlang:send_after(101, self(), b2), + _ = erlang:send_after(102, self(), b3), + _ = erlang:send_after(103, self(), last), + [b1, b2, b3, last] = collect(last), % This behaviour is not guaranteed: % ?line _ = erlang:send_after(100, self(), c1), diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl index 32e2a98e3c..54f8cc3c30 100644 --- a/erts/emulator/test/trace_local_SUITE.erl +++ b/erts/emulator/test/trace_local_SUITE.erl @@ -80,6 +80,7 @@ config(priv_dir,_) -> exception_meta_nocatch/1, exception_meta_nocatch_apply/1, exception_meta_nocatch_function/1, exception_meta_nocatch_apply_function/1, + concurrency/1, init_per_testcase/2, end_per_testcase/2]). init_per_testcase(_Case, Config) -> ?line Dog=test_server:timetrap(test_server:minutes(2)), @@ -89,7 +90,15 @@ end_per_testcase(_Case, Config) -> shutdown(), Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), - ok. + + %% Reloading the module will clear all trace patterns, and + %% in a debug-compiled emulator run assertions of the counters + %% for the number of functions with breakpoints. + + c:l(?MODULE). + + + suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -106,7 +115,8 @@ all() -> exception_meta_apply_function, exception_meta_nocatch, exception_meta_nocatch_apply, exception_meta_nocatch_function, - exception_meta_nocatch_apply_function] + exception_meta_nocatch_apply_function, + concurrency] end. groups() -> @@ -350,7 +360,8 @@ same(A, B) -> basic_test() -> ?line setup([call]), - ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]), + NumMatches = erlang:trace_pattern({?MODULE,'_','_'},[],[local]), + NumMatches = erlang:trace_pattern({?MODULE,'_','_'},[],[local]), ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]), ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]), ?line ?CT(?MODULE,exported_wrap,[1]), @@ -703,16 +714,10 @@ exception_test(Opts) -> ?line ok. exceptions() -> - ?line Ref = make_ref(), - ?line N = case os:type() of - vxworks -> - ?line 2000; % Limited memory on themachines, not actually - % VxWorks' fault /PaN - _ -> - ?line 200000 - end, - ?line LiL = seq(1, N-1, N), % Long Improper List - ?line LL = seq(1, N, []), % Long List + Ref = make_ref(), + N = 200000, + LiL = seq(1, N-1, N), % Long Improper List + LL = seq(1, N, []), % Long List [{{erlang,exit}, [done]}, {{erlang,error}, [1.0]}, {{erlang,error}, [Ref,[]]}, @@ -813,6 +818,42 @@ clean_location({crash,{Reason,Stk0}}) -> {crash,{Reason,Stk}}; clean_location(Term) -> Term. +concurrency(_Config) -> + N = erlang:system_info(schedulers), + + %% Spawn 2*N processes that spin in a tight infinite loop, + %% and one process that will turn on and off local call + %% trace on the infinite_loop/0 function. We expect the + %% emulator to crash if there is a memory barrier bug or + %% if an aligned word-sized write is not atomic. + + Ps0 = [spawn_monitor(fun() -> infinite_loop() end) || + _ <- lists:seq(1, 2*N)], + OnAndOff = fun() -> concurrency_on_and_off() end, + Ps1 = [spawn_monitor(OnAndOff)|Ps0], + ?t:sleep(1000), + + %% Now spawn off N more processes that turn on off and off + %% a local trace pattern. + Ps = [spawn_monitor(OnAndOff) || _ <- lists:seq(1, N)] ++ Ps1, + ?t:sleep(1000), + + %% Clean up. + [exit(Pid, kill) || {Pid,_} <- Ps], + [receive + {'DOWN',Ref,process,Pid,killed} -> ok + end || {Pid,Ref} <- Ps], + erlang:trace_pattern({?MODULE,infinite_loop,0}, false, [local]), + ok. + +concurrency_on_and_off() -> + 1 = erlang:trace_pattern({?MODULE,infinite_loop,0}, true, [local]), + 1 = erlang:trace_pattern({?MODULE,infinite_loop,0}, false, [local]), + concurrency_on_and_off(). + +infinite_loop() -> + infinite_loop(). + %%% Tracee target functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl index f81cab3114..cc2eadafbc 100644 --- a/erts/emulator/test/trace_port_SUITE.erl +++ b/erts/emulator/test/trace_port_SUITE.erl @@ -472,14 +472,9 @@ default_tracer(Config) when is_list(Config) -> ?line M = N, ok. - %%% Help functions. -huge_data() -> - case os:type() of - vxworks -> huge_data(4711); - _ -> huge_data(16384) - end. +huge_data() -> huge_data(16384). huge_data(0) -> []; huge_data(N) when N rem 2 == 0 -> P = huge_data(N div 2), diff --git a/erts/emulator/utils/make_tables b/erts/emulator/utils/make_tables index 91efb4c023..a841f26d6a 100755 --- a/erts/emulator/utils/make_tables +++ b/erts/emulator/utils/make_tables @@ -167,7 +167,6 @@ typedef struct bif_entry { extern BifEntry bif_table[]; extern Export* bif_export[]; -extern unsigned char erts_bif_trace_flags[]; #define BIF_SIZE $bif_size @@ -197,7 +196,6 @@ includes("export.h", "sys.h", "erl_vm.h", "erl_process.h", "bif.h", "erl_bif_table.h", "erl_atom_table.h"); print "\nExport* bif_export[BIF_SIZE];\n"; -print "unsigned char erts_bif_trace_flags[BIF_SIZE];\n\n"; print "BifEntry bif_table[] = {\n"; for ($i = 0; $i < @bif; $i++) { diff --git a/erts/emulator/valgrind/suppress.patched.3.6.0 b/erts/emulator/valgrind/suppress.patched.3.6.0 index 8cf4cba2c8..62ba032520 100644 --- a/erts/emulator/valgrind/suppress.patched.3.6.0 +++ b/erts/emulator/valgrind/suppress.patched.3.6.0 @@ -57,18 +57,16 @@ fun:putenv fun:erts_sys_putenv fun:os_putenv_2 - fun:process_main + ... } { -Leak in libc putenv -Memcheck:Leak -fun:malloc -fun:erts_sys_alloc -... -fun:erts_alloc -fun:erts_sys_putenv -fun:os_putenv_2 -fun:process_main + Leak in libc putenv + Memcheck:Leak + ... + fun:erts_alloc + fun:erts_sys_putenv + fun:os_putenv_2 + ... } { erronous warning @@ -348,3 +346,14 @@ fun:erl_start fun:main } +{ +Harmless leak of ErtsThrPrgrData from async threads in exiting emulator +Memcheck:Leak +... +fun:erts_alloc +fun:erts_thr_progress_register_unmanaged_thread +fun:async_thread_init +fun:async_main +... +} + diff --git a/erts/emulator/valgrind/suppress.standard b/erts/emulator/valgrind/suppress.standard index 26e34e3757..5a129bfd10 100644 --- a/erts/emulator/valgrind/suppress.standard +++ b/erts/emulator/valgrind/suppress.standard @@ -54,18 +54,16 @@ fun:putenv fun:erts_sys_putenv fun:os_putenv_2 - fun:process_main + ... } { -Leak in libc putenv -Memcheck:Leak -fun:malloc -fun:erts_sys_alloc -... -fun:erts_alloc -fun:erts_sys_putenv -fun:os_putenv_2 -fun:process_main + Leak in libc putenv + Memcheck:Leak + ... + fun:erts_alloc + fun:erts_sys_putenv + fun:os_putenv_2 + ... } { erronous warning @@ -306,3 +304,14 @@ fun:erl_start fun:main } +{ +Harmless leak of ErtsThrPrgrData from async threads in exiting emulator +Memcheck:Leak +... +fun:erts_alloc +fun:erts_thr_progress_register_unmanaged_thread +fun:async_thread_init +fun:async_main +... +} + |