diff options
33 files changed, 402 insertions, 106 deletions
diff --git a/.gitignore b/.gitignore index bd0e9615f7..789d08fdb0 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ TAGS # vim .*.sw[a-z] +# vscode +.vscode + autom4te.cache *.beam *.asn1db diff --git a/erts/emulator/beam/arith_instrs.tab b/erts/emulator/beam/arith_instrs.tab index 5f23b2c168..f14b376419 100644 --- a/erts/emulator/beam/arith_instrs.tab +++ b/erts/emulator/beam/arith_instrs.tab @@ -51,11 +51,50 @@ plus.fetch(Op1, Op2) { plus.execute(Fail, Dst) { if (ERTS_LIKELY(is_both_small(PlusOp1, PlusOp2))) { +#ifdef HAVE_OVERFLOW_CHECK_BUILTINS + Sint lhs_tagged, rhs_untagged, res; + + /* The value part of immediate integers start right after the tag and + * occupy the rest of the word, so if you squint a bit they look like + * fixed-point integers; as long as you mask the tag away you will get + * correct results from addition/subtraction since they share the same + * notion of zero. It's fairly easy to see that the following holds + * when (a + b) is in range: + * + * (a >> s) + (b >> s) == ((a & ~m) + (b & ~m)) >> s + * + * Where 's' is the tag size and 'm' is the tag mask. + * + * The left-hand side is our fallback in the #else clause and is the + * fastest way to do this safely in plain C. The actual addition will + * never overflow since `Sint` has a much greater range than our + * smalls, so we can use the IS_SSMALL macro to see if the result is + * within range. + * + * What we're doing below is an extension of the right-hand side. By + * treating `a` and `b` as fixed-point integers, all additions whose + * result is out of range will also overflow `Sint` and we can use the + * compiler's overflow intrinsics to check for this condition. + * + * In addition, since the tag lives in the lowest bits we can further + * optimize this by only stripping the tag from either side. The higher + * bits can't influence the tag bits since we bail on overflow, so the + * tag bits from the tagged side will simply appear in the result. */ + lhs_tagged = PlusOp1; + rhs_untagged = PlusOp2 & ~_TAG_IMMED1_MASK; + + if (ERTS_LIKELY(!__builtin_add_overflow(lhs_tagged, rhs_untagged, &res))) { + ASSERT(is_small(res)); + $Dst = res; + $NEXT0(); + } +#else Sint i = signed_val(PlusOp1) + signed_val(PlusOp2); if (ERTS_LIKELY(IS_SSMALL(i))) { $Dst = make_small(i); $NEXT0(); } +#endif } $OUTLINED_ARITH_2($Fail, mixed_plus, BIF_splus_2, PlusOp1, PlusOp2, $Dst); } @@ -73,11 +112,26 @@ minus.fetch(Op1, Op2) { minus.execute(Fail, Dst) { if (ERTS_LIKELY(is_both_small(MinusOp1, MinusOp2))) { +#ifdef HAVE_OVERFLOW_CHECK_BUILTINS + Sint lhs_tagged, rhs_untagged, res; + + /* See plus.execute */ + lhs_tagged = MinusOp1; + rhs_untagged = MinusOp2 & ~_TAG_IMMED1_MASK; + + if (ERTS_LIKELY(!__builtin_sub_overflow(lhs_tagged, rhs_untagged, &res))) { + ASSERT(is_small(res)); + $Dst = res; + $NEXT0(); + } +#else Sint i = signed_val(MinusOp1) - signed_val(MinusOp2); + if (ERTS_LIKELY(IS_SSMALL(i))) { $Dst = make_small(i); $NEXT0(); } +#endif } $OUTLINED_ARITH_2($Fail, mixed_minus, BIF_sminus_2, MinusOp1, MinusOp2, $Dst); } @@ -97,12 +151,27 @@ increment.execute(IncrementVal, Dst) { Eterm result; if (ERTS_LIKELY(is_small(increment_reg_val))) { +#ifdef HAVE_OVERFLOW_CHECK_BUILTINS + Sint lhs_tagged, rhs_untagged, res; + + /* See plus.execute */ + lhs_tagged = increment_reg_val; + rhs_untagged = (Sint)increment_val << _TAG_IMMED1_SIZE; + + if (ERTS_LIKELY(!__builtin_add_overflow(lhs_tagged, rhs_untagged, &res))) { + ASSERT(is_small(res)); + $Dst = res; + $NEXT0(); + } +#else Sint i = signed_val(increment_reg_val) + increment_val; if (ERTS_LIKELY(IS_SSMALL(i))) { $Dst = make_small(i); $NEXT0(); } +#endif } + result = erts_mixed_plus(c_p, increment_reg_val, make_small(increment_val)); ERTS_HOLE_CHECK(c_p); if (ERTS_LIKELY(is_value(result))) { @@ -118,11 +187,15 @@ i_times(Fail, Op1, Op2, Dst) { Eterm op2 = $Op2; #ifdef HAVE_OVERFLOW_CHECK_BUILTINS if (ERTS_LIKELY(is_both_small(op1, op2))) { - Sint a = signed_val(op1); - Sint b = signed_val(op2); - Sint res; - if (ERTS_LIKELY(!__builtin_mul_overflow(a, b, &res) && IS_SSMALL(res))) { - $Dst = make_small(res); + /* See plus.execute */ + Sint lhs_untagged, rhs_actual, res; + + lhs_untagged = op1 & ~_TAG_IMMED1_MASK; + rhs_actual = signed_val(op2); + + if (ERTS_LIKELY(!__builtin_mul_overflow(lhs_untagged, rhs_actual, &res))) { + ASSERT(!(res & _TAG_IMMED1_MASK)); + $Dst = res | _TAG_IMMED1_SMALL; $NEXT0(); } } diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index ea01ce597d..8e93e53003 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -375,44 +375,33 @@ do { \ /* * process_main() is already huge, so we want to avoid inlining - * into it. Especially functions that are seldom used. + * seldom used functions into it. */ -#ifdef __GNUC__ -# define NOINLINE __attribute__((__noinline__)) -#else -# define NOINLINE -#endif - - -/* - * The following functions are called directly by process_main(). - * Don't inline them. - */ -static void init_emulator_finish(void) NOINLINE; -static ErtsCodeMFA *ubif2mfa(void* uf) NOINLINE; +static void init_emulator_finish(void) ERTS_NOINLINE; +static ErtsCodeMFA *ubif2mfa(void* uf) ERTS_NOINLINE; static BeamInstr* handle_error(Process* c_p, BeamInstr* pc, - Eterm* reg, ErtsCodeMFA* bif_mfa) NOINLINE; + Eterm* reg, ErtsCodeMFA* bif_mfa) ERTS_NOINLINE; static BeamInstr* call_error_handler(Process* p, ErtsCodeMFA* mfa, - Eterm* reg, Eterm func) NOINLINE; + Eterm* reg, Eterm func) ERTS_NOINLINE; static BeamInstr* fixed_apply(Process* p, Eterm* reg, Uint arity, - BeamInstr *I, Uint offs) NOINLINE; + BeamInstr *I, Uint offs) ERTS_NOINLINE; static BeamInstr* apply(Process* p, Eterm* reg, - BeamInstr *I, Uint offs) NOINLINE; + BeamInstr *I, Uint offs) ERTS_NOINLINE; static BeamInstr* call_fun(Process* p, int arity, - Eterm* reg, Eterm args) NOINLINE; + Eterm* reg, Eterm args) ERTS_NOINLINE; static BeamInstr* apply_fun(Process* p, Eterm fun, - Eterm args, Eterm* reg) NOINLINE; + Eterm args, Eterm* reg) ERTS_NOINLINE; static Eterm new_fun(Process* p, Eterm* reg, - ErlFunEntry* fe, int num_free) NOINLINE; + ErlFunEntry* fe, int num_free) ERTS_NOINLINE; static int is_function2(Eterm Term, Uint arity); static Eterm erts_gc_new_map(Process* p, Eterm* reg, Uint live, - Uint n, BeamInstr* ptr) NOINLINE; + Uint n, BeamInstr* ptr) ERTS_NOINLINE; static Eterm erts_gc_new_small_map_lit(Process* p, Eterm* reg, Eterm keys_literal, - Uint live, BeamInstr* ptr) NOINLINE; + Uint live, BeamInstr* ptr) ERTS_NOINLINE; static Eterm erts_gc_update_map_assoc(Process* p, Eterm* reg, Uint live, - Uint n, BeamInstr* new_p) NOINLINE; + Uint n, BeamInstr* new_p) ERTS_NOINLINE; static Eterm erts_gc_update_map_exact(Process* p, Eterm* reg, Uint live, - Uint n, Eterm* new_p) NOINLINE; + Uint n, Eterm* new_p) ERTS_NOINLINE; static Eterm get_map_element(Eterm map, Eterm key); static Eterm get_map_element_hash(Eterm map, Eterm key, Uint32 hx); diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 8bbe6450eb..456b6fdac0 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -775,19 +775,25 @@ void init_dist(void) static ERTS_INLINE ErtsDistOutputBuf * alloc_dist_obuf(Uint size, Uint headers) { - int i; + Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers); ErtsDistOutputBuf *obuf; - Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers) + - sizeof(byte)*size; - Binary *bin = erts_bin_drv_alloc(obuf_size); - obuf = (ErtsDistOutputBuf *) &bin->orig_bytes[size]; + Binary *bin; + byte *extp; + int i; + + bin = erts_bin_drv_alloc(obuf_size + size); erts_refc_add(&bin->intern.refc, headers - 1, 1); + + obuf = (ErtsDistOutputBuf *)&bin->orig_bytes[0]; + extp = (byte *)&bin->orig_bytes[obuf_size]; + for (i = 0; i < headers; i++) { obuf[i].bin = bin; - obuf[i].extp = (byte *)&bin->orig_bytes[0]; + obuf[i].extp = extp; #ifdef DEBUG obuf[i].dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN; - obuf[i].alloc_endp = obuf->extp + size; + obuf[i].ext_startp = extp; + obuf[i].alloc_endp = &extp[size]; ASSERT(bin == ErtsDistOutputBuf2Binary(obuf)); #endif } @@ -1360,7 +1366,7 @@ erts_dist_seq_tree_foreach_delete_yielding(DistSeqNode **root, limit); if (res > 0) { if (ysp != &ys) - erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp); + erts_free(ERTS_ALC_T_SEQ_YIELD_STATE, ysp); *vyspp = NULL; } else { @@ -2341,7 +2347,8 @@ erts_dsig_send(ErtsDSigSendContext *ctx) (ctx->fragments-1) * ERTS_DIST_FRAGMENT_HEADER_SIZE, ctx->fragments); ctx->obuf->ext_start = &ctx->obuf->extp[0]; - ctx->obuf->ext_endp = &ctx->obuf->extp[0] + ctx->max_finalize_prepend + ctx->dhdr_ext_size; + ctx->obuf->ext_endp = &ctx->obuf->extp[0] + ctx->max_finalize_prepend + + ctx->dhdr_ext_size; /* Encode internal version of dist header */ ctx->obuf->extp = erts_encode_ext_dist_header_setup( @@ -2380,8 +2387,8 @@ erts_dsig_send(ErtsDSigSendContext *ctx) case ERTS_DSIG_SEND_PHASE_FIN: { ASSERT(ctx->obuf->extp < ctx->obuf->ext_endp); - ASSERT(((byte*)&ctx->obuf->bin->orig_bytes[0]) <= ctx->obuf->extp - ctx->max_finalize_prepend); - ASSERT(ctx->obuf->ext_endp <= ((byte*)ctx->obuf->bin->orig_bytes) + ctx->data_size + ctx->dhdr_ext_size); + ASSERT(ctx->obuf->ext_startp <= ctx->obuf->extp - ctx->max_finalize_prepend); + ASSERT(ctx->obuf->ext_endp <= (byte*)ctx->obuf->ext_startp + ctx->data_size + ctx->dhdr_ext_size); ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp; @@ -3457,6 +3464,7 @@ dist_ctrl_get_data_1(BIF_ALIST_1) pb->bytes = (byte*) obuf->extp; pb->flags = 0; res = make_binary(pb); + hp += PROC_BIN_SIZE; } else { hp = HAlloc(BIF_P, PROC_BIN_SIZE * 2 + 4 + hsz); pb = (ProcBin *) (char *) hp; diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index a7424bbcb8..39d42d9757 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2579,6 +2579,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) /* Need to be the only thread running... */ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_P->scheduler_data->current_process = NULL; erts_thr_progress_block(); if (BIF_ARG_1 == am_info) @@ -2592,6 +2593,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) erts_thr_progress_unblock(); erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_P->scheduler_data->current_process = BIF_P; ASSERT(dsbufp && dsbufp->str); res = new_binary(BIF_P, (byte *) dsbufp->str, dsbufp->str_len); diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index c434926142..fc3e117463 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -95,6 +95,7 @@ enum dist_entry_state { struct ErtsDistOutputBuf_ { #ifdef DEBUG Uint dbg_pattern; + byte *ext_startp; byte *alloc_endp; #endif ErtsDistOutputBuf *next; diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index 4e9f177e51..f58a606d57 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -1019,6 +1019,8 @@ send_gen_exit_signal(Process *c_p, Eterm from_tag, ref_sz = size_object(ref); hsz += ref_sz; + reason_sz = 0; /* Set to silence gcc warning */ + /* The reason was part of the control message, just use copy it into the xsigd */ if (is_value(reason)) { diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 9e662632b4..2b45d2d353 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -12141,10 +12141,9 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds) reason); switch (code) { case ERTS_DSIG_SEND_CONTINUE: + case ERTS_DSIG_SEND_YIELD: erts_set_gc_state(c_p, 0); ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx); - /* fall-through */ - case ERTS_DSIG_SEND_YIELD: break; case ERTS_DSIG_SEND_OK: break; @@ -12388,11 +12387,10 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds) reason, SEQ_TRACE_TOKEN(c_p)); switch (code) { + case ERTS_DSIG_SEND_YIELD: case ERTS_DSIG_SEND_CONTINUE: erts_set_gc_state(c_p, 0); ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx); - /* fall-through */ - case ERTS_DSIG_SEND_YIELD: break; case ERTS_DSIG_SEND_OK: break; diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 471c1c3938..fa8314c115 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -699,6 +699,7 @@ dist_ext_size(ErtsDistExternal *edep) } else { sz -= sizeof(ErtsAtomTranslationTable); } + ASSERT(sz % 4 == 0); return sz; } @@ -706,8 +707,9 @@ Uint erts_dist_ext_size(ErtsDistExternal *edep) { Uint sz = dist_ext_size(edep); + sz += 4; /* may need to pad to 8-byte-align ErtsDistExternalData */ sz += edep->data[0].frag_id * sizeof(ErtsDistExternalData); - return sz + ERTS_EXTRA_DATA_ALIGN_SZ(sz); + return sz; } Uint @@ -749,6 +751,8 @@ erts_make_dist_ext_copy(ErtsDistExternal *edep, ErtsDistExternal *new_edep) erts_ref_dist_entry(new_edep->dep); ep += dist_ext_sz; + ep += (UWord)ep & 4; /* 8-byte alignment for ErtsDistExternalData */ + ASSERT((UWord)ep % 8 == 0); new_edep->data = (ErtsDistExternalData*)ep; sys_memzero(new_edep->data, sizeof(ErtsDistExternalData) * edep->data->frag_id); diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index 396cd9f802..f2cc9bf98f 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -144,14 +144,6 @@ typedef struct erl_dist_external { ErtsAtomTranslationTable attab; } ErtsDistExternal; -#define ERTS_DIST_EXT_SIZE(EDEP) \ - (sizeof(ErtsDistExternal) \ - - (((EDEP)->flags & ERTS_DIST_EXT_ATOM_TRANS_TAB) \ - ? (ASSERT(0 <= (EDEP)->attab.size \ - && (EDEP)->attab.size <= ERTS_ATOM_CACHE_SIZE), \ - sizeof(Eterm)*(ERTS_ATOM_CACHE_SIZE - (EDEP)->attab.size)) \ - : sizeof(ErtsAtomTranslationTable))) - typedef struct { byte *extp; int exttmp; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index f9bbe4167f..4c8d3d3dbe 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1216,10 +1216,11 @@ Uint64 erts_timestamp_millis(void); Export* erts_find_function(Eterm, Eterm, unsigned int, ErtsCodeIndex); -void *erts_calc_stacklimit(char *prev_c, UWord stacksize); -int erts_check_below_limit(char *ptr, char *limit); -int erts_check_above_limit(char *ptr, char *limit); -void *erts_ptr_id(void *ptr); +/* ERTS_NOINLINE prevents link-time optimization across modules */ +void *erts_calc_stacklimit(char *prev_c, UWord stacksize) ERTS_NOINLINE; +int erts_check_below_limit(char *ptr, char *limit) ERTS_NOINLINE; +int erts_check_above_limit(char *ptr, char *limit) ERTS_NOINLINE; +void *erts_ptr_id(void *ptr) ERTS_NOINLINE; Eterm store_external_or_ref_in_proc_(Process *, Eterm); Eterm store_external_or_ref_(Uint **, ErlOffHeap*, Eterm); diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 7a125b0f67..10ca74cd60 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1690,9 +1690,14 @@ i_plus S1=c S2=c Fail Dst => move S1 x | i_plus x S2 Fail Dst i_plus xy xyc j? d -i_minus x x j? d -i_minus c x j? d -i_minus s s j? d +# A minus instruction with a constant right operand will be +# converted to an i_increment instruction, except in guards or +# when the negated value of the constant won't fit in a guard. +# Therefore, it very rare. +i_minus S1 S2=c Fail Dst => move S2 x | i_minus S1 x Fail Dst + +i_minus xy xy j? d +i_minus c xy j? d i_times j? s s d diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index a6312293cc..c261c8e117 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -63,6 +63,14 @@ # endif #endif +#ifndef ERTS_NOINLINE +# if ERTS_AT_LEAST_GCC_VSN__(3,1,1) +# define ERTS_NOINLINE __attribute__((__noinline__)) +# else +# define ERTS_NOINLINE +# endif +#endif + #if defined(DEBUG) || defined(ERTS_ENABLE_LOCK_CHECK) # undef ERTS_CAN_INLINE # define ERTS_CAN_INLINE 0 diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 80e8030d74..98be50815c 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -842,6 +842,8 @@ driver_select(ErlDrvPort ix, ErlDrvEvent e, int mode, int on) ret = 0; goto done_unknown; } + /* For some reason (don't know why), we do not clean all + events when doing ERL_DRV_USE_NO_CALLBACK. */ else if ((mode&ERL_DRV_USE_NO_CALLBACK) == ERL_DRV_USE) { mode |= (ERL_DRV_READ | ERL_DRV_WRITE); } @@ -2491,6 +2493,10 @@ drvmode2str(int mode) { case ERL_DRV_WRITE|ERL_DRV_USE: return "WRITE|USE"; case ERL_DRV_READ|ERL_DRV_WRITE|ERL_DRV_USE: return "READ|WRITE|USE"; case ERL_DRV_USE: return "USE"; + case ERL_DRV_READ|ERL_DRV_USE_NO_CALLBACK: return "READ|USE_NO_CB"; + case ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK: return "WRITE|USE_NO_CB"; + case ERL_DRV_READ|ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK: return "READ|WRITE|USE_NO_CB"; + case ERL_DRV_USE_NO_CALLBACK: return "USE_NO_CB"; case ERL_DRV_READ: return "READ"; case ERL_DRV_WRITE: return "WRITE"; case ERL_DRV_READ|ERL_DRV_WRITE: return "READ|WRITE"; diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 042a091db1..664d677ebd 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -1006,10 +1006,8 @@ static void clear_fd_data(ErtsSysFdData *fdd) static void nbio_stop_fd(ErlDrvPort prt, ErtsSysFdData *fdd, int use) { - driver_select(prt, abs(fdd->fd), use ? ERL_DRV_USE_NO_CALLBACK : 0|DO_READ|DO_WRITE, 0); clear_fd_data(fdd); SET_BLOCKING(abs(fdd->fd)); - } static void fd_stop(ErlDrvData ev) /* Does not close the fds */ @@ -1026,10 +1024,12 @@ static void fd_stop(ErlDrvData ev) /* Does not close the fds */ if (dd->ifd) { sz += sizeof(ErtsSysFdData); + driver_select(prt, abs(dd->ifd->fd), ERL_DRV_USE_NO_CALLBACK|DO_READ|DO_WRITE, 0); nbio_stop_fd(prt, dd->ifd, 1); } if (dd->ofd && dd->ofd != dd->ifd) { sz += sizeof(ErtsSysFdData); + driver_select(prt, abs(dd->ofd->fd), ERL_DRV_USE_NO_CALLBACK|DO_WRITE, 0); nbio_stop_fd(prt, dd->ofd, 1); } diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 8c2054cb51..28775b6f02 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -124,6 +124,7 @@ MODULES= \ send_term_SUITE \ sensitive_SUITE \ signal_SUITE \ + small_SUITE \ smoke_test_SUITE \ $(SOCKET_MODULES) \ statistics_SUITE \ diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 449821e5ad..58194cf167 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -39,6 +39,8 @@ -define(Line,). -export([all/0, suite/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, ping/1, bulk_send_small/1, group_leader/1, optimistic_dflags/1, @@ -119,6 +121,28 @@ groups() -> message_latency_large_exit2]} ]. +init_per_suite(Config) -> + {ok, Apps} = application:ensure_all_started(os_mon), + [{started_apps, Apps} | Config]. + +end_per_suite(Config) -> + Apps = proplists:get_value(started_apps, Config), + [application:stop(App) || App <- lists:reverse(Apps)], + Config. + +init_per_group(message_latency, Config) -> + Free = free_memory(), + if Free < 2048 -> + {skip, "Not enough memory"}; + true -> + Config + end; +init_per_group(_, Config) -> + Config. + +end_per_group(_, Config) -> + Config. + %% Tests pinging a node in different ways. ping(Config) when is_list(Config) -> Times = 1024, @@ -2845,3 +2869,23 @@ uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 -> Uint band 16#ff; uint8(Uint) -> exit({badarg, uint8, [Uint]}). + +free_memory() -> + %% Free memory in MB. + try + SMD = memsup:get_system_memory_data(), + {value, {free_memory, Free}} = lists:keysearch(free_memory, 1, SMD), + TotFree = (Free + + case lists:keysearch(cached_memory, 1, SMD) of + {value, {cached_memory, Cached}} -> Cached; + false -> 0 + end + + case lists:keysearch(buffered_memory, 1, SMD) of + {value, {buffered_memory, Buffed}} -> Buffed; + false -> 0 + end), + TotFree div (1024*1024) + catch + error : undef -> + ct:fail({"os_mon not built"}) + end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index bb0f3498ab..cbed71cedd 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -998,7 +998,9 @@ chkio_test({erts_poll_info, Before}, During = get_check_io_total(erlang:system_info(check_io)), erlang:display(During), - 0 = element(1, erts_debug:get_internal_state(check_io_debug)), + [0 = element(1, erts_debug:get_internal_state(check_io_debug)) || + %% The pollset is not stable when running the fallback testcase + Test /= ?CHKIO_USE_FALLBACK_POLLSET], io:format("During test: ~p~n", [During]), chk_chkio_port(Port), case erlang:port_control(Port, ?CHKIO_STOP, "") of diff --git a/erts/emulator/test/small_SUITE.erl b/erts/emulator/test/small_SUITE.erl new file mode 100644 index 0000000000..00a02e5560 --- /dev/null +++ b/erts/emulator/test/small_SUITE.erl @@ -0,0 +1,115 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(small_SUITE). + +-export([all/0, suite/0]). +-export([edge_cases/1]). + +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. + +all() -> + [edge_cases]. + +edge_cases(Config) when is_list(Config) -> + {MinSmall, MaxSmall} = Limits = determine_small_limits(0), + ct:pal("Limits = ~p", [Limits]), + + true = (MaxSmall + 1) =:= MaxSmall + id(1), + true = (MinSmall - 1) =:= MinSmall - id(1), + true = (MaxSmall + 1) > id(MaxSmall), + true = (MinSmall - 1) < id(MinSmall), + -1 = MinSmall + id(MaxSmall), + -1 = MaxSmall + id(MinSmall), + + false = is_small(MinSmall * -1), + false = is_small(MinSmall - id(1)), + false = is_small(MinSmall - 1), + false = is_small(MaxSmall + id(1)), + + Lower = lists:seq(MinSmall, MinSmall + 128), + Upper = lists:seq(MaxSmall, MaxSmall - 128, -1), + Pow2 = seq_pow2(MinSmall, MaxSmall), + NearZero = lists:seq(-128, 128), + + ok = test_combinations([Lower, Upper, Pow2, NearZero], MinSmall, MaxSmall), + + ok. + +test_combinations([As | Rest]=TestVectors, MinS, MaxS) -> + [begin + _ = [arith_test(A, B, MinS, MaxS) || B <- Bs] + end || A <- As, Bs <- TestVectors], + test_combinations(Rest, MinS, MaxS); +test_combinations([], _MinS, _MaxS) -> + ok. + +%% Builds a sequence of all powers of 2 between MinSmall and MaxSmall +seq_pow2(MinSmall, MaxSmall) -> + sp2_1(MinSmall, MinSmall, MaxSmall). + +sp2_1(N, _MinS, MaxS) when N >= MaxS -> + []; +sp2_1(-1, MinS, MaxS) -> + [-1 | sp2_1(1, MinS, MaxS)]; +sp2_1(N, MinS, MaxS) when N < 0 -> + [N | sp2_1(N bsr 1, MinS, MaxS)]; +sp2_1(N, MinS, MaxS) when N > 0 -> + [N | sp2_1(N bsl 1, MinS, MaxS)]. + +arith_test(A, B, MinS, MaxS) -> + verify_kind(A + B, MinS, MaxS), + verify_kind(B + A, MinS, MaxS), + verify_kind(A - B, MinS, MaxS), + verify_kind(B - A, MinS, MaxS), + verify_kind(A * B, MinS, MaxS), + verify_kind(B * A, MinS, MaxS), + + true = A + B =:= apply(erlang, id('+'), [A, B]), + true = A - B =:= apply(erlang, id('-'), [A, B]), + true = A * B =:= apply(erlang, id('*'), [A, B]), + + true = A + B =:= B + id(A), + true = A - B =:= A + id(-B), + true = B - A =:= B + id(-A), + true = A * B =:= B * id(A), + + true = B =:= 0 orelse ((A * B) div id(B) =:= A), + true = A =:= 0 orelse ((B * A) div id(A) =:= B), + + ok. + +%% Verifies that N is a small when it should be +verify_kind(N, MinS, MaxS) -> + true = is_small(N) =:= (N >= MinS andalso N =< MaxS). + +is_small(N) when is_integer(N) -> + 0 =:= erts_debug:flat_size(N). + +determine_small_limits(N) -> + case is_small(-1 bsl N) of + true -> determine_small_limits(N + 1); + false -> {-1 bsl (N - 1), (1 bsl (N - 1)) - 1} + end. + +id(I) -> I. diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index ae3099633a..d278ac86c7 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -93,25 +93,34 @@ wall_clock_zero_diff1(0) -> %% statistics(wall_clock) are compatible, and are within a small number %% of ms of the amount of real time we waited for. wall_clock_update(Config) when is_list(Config) -> - wall_clock_update1(6). + N = 10, + Inc = 200, + TotalTime = wall_clock_update1(N, Inc, 0), + Overhead = TotalTime - N * Inc, + IsDebug = test_server:is_debug(), -wall_clock_update1(N) when N > 0 -> - {T1_wc_time, _} = statistics(wall_clock), - receive after 1000 -> ok end, - {T2_wc_time, Wc_Diff} = statistics(wall_clock), - - Wc_Diff = T2_wc_time - T1_wc_time, - io:format("Wall clock diff = ~p; should be = 1000..1040~n", [Wc_Diff]), - case test_server:is_debug() of - false -> - true = Wc_Diff =< 1040; + %% Check that the average overhead is reasonable. + if + Overhead < N * 100 -> + ok; + IsDebug, Overhead < N * 1000 -> + ok; true -> - true = Wc_Diff =< 2000 %Be more tolerant in debug-compiled emulator. - end, - true = Wc_Diff >= 1000, - wall_clock_update1(N-1); -wall_clock_update1(0) -> - ok. + io:format("There was an overhead of ~p ms during ~p rounds.", + [Overhead,N]), + ct:fail(too_much_overhead) + end. + +wall_clock_update1(N, Inc, Total) when N > 0 -> + {Time1, _} = statistics(wall_clock), + receive after Inc -> ok end, + {Time2, WcDiff} = statistics(wall_clock), + WcDiff = Time2 - Time1, + io:format("Wall clock diff = ~p (expected at least ~p)\n", [WcDiff,Inc]), + true = WcDiff >= Inc, + wall_clock_update1(N-1, Inc, Total + WcDiff); +wall_clock_update1(0, _, Total) -> + Total. %%% Test statistics(runtime). diff --git a/erts/include/internal/ethr_internal.h b/erts/include/internal/ethr_internal.h index ac27ff2ed0..17ec84c52b 100644 --- a/erts/include/internal/ethr_internal.h +++ b/erts/include/internal/ethr_internal.h @@ -90,7 +90,7 @@ int ethr_init_common__(ethr_init_data *id); int ethr_late_init_common__(ethr_late_init_data *lid); void ethr_run_exit_handlers__(void); void ethr_ts_event_destructor__(void *vtsep); -void ethr_set_stacklimit__(char *prev_c, size_t stacksize); +void ethr_set_stacklimit__(char *prev_c, size_t stacksize) ETHR_NOINLINE; #if defined(ETHR_X86_RUNTIME_CONF__) void ethr_x86_cpuid__(int *eax, int *ebx, int *ecx, int *edx); diff --git a/erts/include/internal/ethread_inline.h b/erts/include/internal/ethread_inline.h index 8e6bcfc4a8..791d7fa0ff 100644 --- a/erts/include/internal/ethread_inline.h +++ b/erts/include/internal/ethread_inline.h @@ -62,12 +62,15 @@ # define ETHR_INLINE __inline__ # if ETHR_AT_LEAST_GCC_VSN__(3, 1, 1) # define ETHR_FORCE_INLINE __inline__ __attribute__((__always_inline__)) +# define ETHR_NOINLINE __attribute__((__noinline__)) # else # define ETHR_FORCE_INLINE __inline__ +# define ETHR_NOINLINE # endif #elif defined(__WIN32__) # define ETHR_INLINE __forceinline # define ETHR_FORCE_INLINE __forceinline +# define ETHR_NOINLINE #endif #endif /* #ifndef ETHREAD_INLINE_H__ */ diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl index 7aaf33839f..69a7de1431 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl @@ -271,7 +271,7 @@ no_client_hello(Config) -> %% Tell server to receive a get request and then die without %% replying since no hello has been received. (is this correct - %% behavoiur??) + %% behaviour??) ?NS:expect_do(get,close), {error,closed} = ct_netconfc:get(Client,whatever), ok. diff --git a/lib/crypto/c_src/otp_test_engine.c b/lib/crypto/c_src/otp_test_engine.c index fd26b7cb5d..4a155becf8 100644 --- a/lib/crypto/c_src/otp_test_engine.c +++ b/lib/crypto/c_src/otp_test_engine.c @@ -404,7 +404,7 @@ int test_rsa_sign(int dtype, } */ if ((sizeof(fake_flag) == m_len) - && bcmp(m,fake_flag,m_len) == 0) { + && memcmp(m,fake_flag,m_len) == 0) { int slen; printf("To be faked\r\n"); @@ -432,7 +432,7 @@ int test_rsa_verify(int dtype, printf("test_rsa_verify (dtype=%i) called m_len=%u siglen=%u\r\n", dtype, m_len, siglen); if ((sizeof(fake_flag) == m_len) - && bcmp(m,fake_flag,m_len) == 0) { + && memcmp(m,fake_flag,m_len) == 0) { int size; if ((size = RSA_size(rsa)) < 0) diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_responsecontrol.erl b/lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_responsecontrol.erl index a997db6880..53eeedc29f 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_responsecontrol.erl +++ b/lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_responsecontrol.erl @@ -71,7 +71,7 @@ do_responsecontrol(Info) -> %% If a client sends more then one of the if-XXXX fields in a request -%% The standard says it does not specify the behaviuor so I specified it :-) +%% The standard says it does not specify the behaviour so I specified it :-) %% The priority between the fields is %% 1.If-modified %% 2.If-Unmodified diff --git a/lib/inets/src/http_server/mod_responsecontrol.erl b/lib/inets/src/http_server/mod_responsecontrol.erl index 07129940a5..a32ba65c22 100644 --- a/lib/inets/src/http_server/mod_responsecontrol.erl +++ b/lib/inets/src/http_server/mod_responsecontrol.erl @@ -71,7 +71,7 @@ do_responsecontrol(Info) -> %% If a client sends more then one of the if-XXXX fields in a request -%% The standard says it does not specify the behaviuor so I specified it :-) +%% The standard says it does not specify the behaviour so I specified it :-) %% The priority between the fields is %% 1.If-modified %% 2.If-Unmodified diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index 111d103df2..bfa091a036 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -68,7 +68,7 @@ config_change(Changed, New, Removed) -> %%% auth, ...) ...) %%% %%% The rectangular boxes are supervisors. All supervisors except -%%% for kernel_safe_sup terminates the enitre erlang node if any of +%%% for kernel_safe_sup terminates the entire erlang node if any of %%% their children dies. Any child that can't be restarted in case %%% of failure must be placed under one of these supervisors. Any %%% other child must be placed under safe_sup. These children may diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 97878431a6..850dee7d4f 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -838,8 +838,7 @@ effective_key_bits(Cipher) when Cipher == aes_256_cbc; 256. iv_size(Cipher) when Cipher == null; - Cipher == rc4_128; - Cipher == chacha20_poly1305-> + Cipher == rc4_128 -> 0; iv_size(Cipher) when Cipher == aes_128_gcm; Cipher == aes_256_gcm; @@ -848,6 +847,8 @@ iv_size(Cipher) when Cipher == aes_128_gcm; Cipher == aes_128_ccm_8; Cipher == aes_256_ccm_8 -> 4; +iv_size(chacha20_poly1305) -> + 12; iv_size(Cipher) -> block_size(Cipher). diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 9cc131c3cb..867d2cfc5a 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -395,7 +395,7 @@ decipher_aead(Type, #cipher_state{key = Key} = CipherState, AAD0, CipherFragment try Nonce = decrypt_nonce(Type, CipherState, CipherFragment), {AAD, CipherText, CipherTag} = aead_ciphertext_split(Type, CipherState, CipherFragment, AAD0), - case ssl_cipher:aead_decrypt(Type, Key, Nonce, CipherText, CipherTag, AAD) of + case ssl_cipher:aead_decrypt(Type, Key, Nonce, CipherText, CipherTag, AAD) of Content when is_binary(Content) -> Content; _ -> @@ -473,7 +473,7 @@ initial_security_params(ConnectionEnd) -> do_cipher_aead(?CHACHA20_POLY1305 = Type, Fragment, #cipher_state{key=Key, tag_len = TagLen} = CipherState, AAD0) -> AAD = ?end_additional_data(AAD0, erlang:iolist_size(Fragment)), - Nonce = encrypt_nonce(Type, CipherState), + Nonce = chacha_nonce(CipherState), {Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD, TagLen), {<<Content/binary, CipherTag/binary>>, CipherState}; do_cipher_aead(Type, Fragment, #cipher_state{key=Key, tag_len = TagLen, nonce = ExplicitNonce} = CipherState, AAD0) -> @@ -482,16 +482,18 @@ do_cipher_aead(Type, Fragment, #cipher_state{key=Key, tag_len = TagLen, nonce = {Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD, TagLen), {<<ExplicitNonce:64/integer, Content/binary, CipherTag/binary>>, CipherState#cipher_state{nonce = ExplicitNonce + 1}}. -encrypt_nonce(?CHACHA20_POLY1305, #cipher_state{nonce = Nonce, iv = IV}) -> - crypto:exor(<<?UINT32(0), Nonce/binary>>, IV); + +chacha_nonce(#cipher_state{nonce = Nonce, iv = IV}) -> + crypto:exor(<<?UINT32(0), Nonce/binary>>, IV). + encrypt_nonce(Type, #cipher_state{iv = IV, nonce = ExplicitNonce}) when Type == ?AES_GCM; Type == ?AES_CCM; Type == ?AES_CCM_8 -> <<Salt:4/bytes, _/binary>> = IV, <<Salt/binary, ExplicitNonce:64/integer>>. -decrypt_nonce(?CHACHA20_POLY1305, #cipher_state{nonce = Nonce, iv = IV}, _) -> - crypto:exor(<<Nonce:96/unsigned-big-integer>>, IV); +decrypt_nonce(?CHACHA20_POLY1305, CipherState, _) -> + chacha_nonce(CipherState); decrypt_nonce(Type, #cipher_state{iv = <<Salt:4/bytes, _/binary>>}, <<ExplicitNonce:8/bytes, _/binary>>) when Type == ?AES_GCM; Type == ?AES_CCM; diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index ada3ff5de3..3e68c1b225 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -808,12 +808,6 @@ cr_clause({clause,_,[T],G,B}, Opts) -> try_clauses(Cs, Opts) -> clauses(fun try_clause/2, Opts, Cs). -try_clause({clause,_,[{tuple,_,[{atom,_,throw},V,S]}],G,B}, Opts) -> - El = lexpr(V, 0, Opts), - Sl = stack_backtrace(S, [El], Opts), - Gl = guard_when(Sl, G, Opts), - Bl = body(B, Opts), - {step,Gl,Bl}; try_clause({clause,_,[{tuple,_,[C,V,S]}],G,B}, Opts) -> Cs = lexpr(C, 0, Opts), El = lexpr(V, 0, Opts), diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index f5d80e7e68..e5d1910070 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -51,7 +51,7 @@ otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1, otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1, otp_10302/1, otp_10820/1, otp_11100/1, otp_11861/1, pr_1014/1, - otp_13662/1, otp_14285/1, otp_15592/1]). + otp_13662/1, otp_14285/1, otp_15592/1, otp_15751/1]). %% Internal export. -export([ehook/6]). @@ -81,7 +81,7 @@ groups() -> [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238, otp_8473, otp_8522, otp_8567, otp_8664, otp_9147, otp_10302, otp_10820, otp_11100, otp_11861, pr_1014, otp_13662, - otp_14285, otp_15592]}]. + otp_14285, otp_15592, otp_15751]}]. init_per_suite(Config) -> Config. @@ -1172,6 +1172,39 @@ otp_15592(_Config) -> "56789012345678901234:f(<<>>)">>), ok. +otp_15751(_Config) -> + ok = pp_expr(<<"try foo:bar() + catch + Reason : Stacktrace -> + {Reason, Stacktrace} + end">>), + ok = pp_expr(<<"try foo:bar() + catch + throw: Reason : Stacktrace -> + {Reason, Stacktrace} + end">>), + ok = pp_expr(<<"try foo:bar() + catch + Reason : _ -> + Reason + end">>), + ok = pp_expr(<<"try foo:bar() + catch + throw: Reason : _ -> + Reason + end">>), + ok = pp_expr(<<"try foo:bar() + catch + Reason -> + Reason + end">>), + ok = pp_expr(<<"try foo:bar() + catch + throw: Reason -> + Reason + end">>), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% compile(Config, Tests) -> diff --git a/lib/stdlib/test/stdlib_bench_SUITE_data/generic_fsm.erl b/lib/stdlib/test/stdlib_bench_SUITE_data/generic_fsm.erl index 50f7df7a2a..1abd9b1f2f 100644 --- a/lib/stdlib/test/stdlib_bench_SUITE_data/generic_fsm.erl +++ b/lib/stdlib/test/stdlib_bench_SUITE_data/generic_fsm.erl @@ -24,7 +24,7 @@ -export([init/1, terminate/3]). -export([state1/3, state2/3]). --behaivour(gen_fsm). +-behaviour(gen_fsm). %% API diff --git a/lib/wx/examples/simple/hello2.erl b/lib/wx/examples/simple/hello2.erl index 656c056d9a..07a9a56b7d 100644 --- a/lib/wx/examples/simple/hello2.erl +++ b/lib/wx/examples/simple/hello2.erl @@ -33,7 +33,7 @@ init/1, handle_info/2, handle_event/2, handle_call/3, code_change/3, terminate/2]). --behavoiur(wx_object). +-behaviour(wx_object). -record(state, {win}). |