diff options
134 files changed, 4385 insertions, 3006 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/doc/src/erl.xml b/erts/doc/src/erl.xml index 88ddb03e97..471d7caa5a 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -636,6 +636,29 @@ produces a crash dump. On Unix systems, sending an emulator process a <c>SIGUSR1</c> signal also forces a crash dump.</p> </item> + <tag><marker id="+dcg"/><c><![CDATA[+rg DecentralizedCounterGroupsLimit]]></c></tag> + <item> + <p>Limits the number of decentralized counter groups used by + decentralized counters optimized for update operations in the + Erlang runtime system. By default, the limit is 256.</p> + <p>When the number of schedulers is less than or equal to the + limit, each scheduler has its own group. When the + number of schedulers is larger than the groups limit, + schedulers share groups. Shared groups degrade + the performance for updating counters while many reader groups + degrade the performance for reading counters. So, the limit is a tradeoff + between performance for update operations and performance for + read operations. Each group consumes 64 bytes in each + counter.</p> + <p>Notice that a runtime system using decentralized + counter groups benefits from <seealso marker="#+sbt">binding + schedulers to logical processors</seealso>, as the groups are + distributed better between schedulers with this option.</p> + <p>This option only affects decentralized counters used for + the counters that are keeping track of the memory consumption + and the number of terms in ETS tables of type ordered_set with + the write_concurrency option activated.</p> + </item> <tag><marker id="+e"/><c><![CDATA[+e Number]]></c></tag> <item> <p>Sets the maximum number of ETS tables. This limit is diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml index 1951d498cd..185c75fe84 100644 --- a/erts/doc/src/erl_dist_protocol.xml +++ b/erts/doc/src/erl_dist_protocol.xml @@ -109,8 +109,7 @@ <title>Register a Node in EPMD</title> <p>When a distributed node is started it registers itself in the EPMD. The message <c>ALIVE2_REQ</c> described below is sent from the node to - the EPMD. The response from the EPMD is <c>ALIVE2_X_RESP</c> (or - <c>ALIVE2_RESP</c>).</p> + the EPMD. The response from the EPMD is <c>ALIVE2_RESP</c>.</p> <table align="left"> <row> @@ -156,12 +155,12 @@ <tag><c>HighestVersion</c></tag> <item> <p>The highest distribution version that this node can handle. - The value in OTP 22 and later is 6.</p> + The value in Erlang/OTP R6B and later is 5.</p> </item> <tag><c>LowestVersion</c></tag> <item> <p>The lowest distribution version that this node can handle. - The value in OTP 22 and later is 5.</p> + The value in Erlang/OTP R6B and later is 5.</p> </item> <tag><c>Nlen</c></tag> <item> @@ -185,24 +184,7 @@ node is a distributed node. When the connection is closed, the node is automatically unregistered from the EPMD.</p> - <p>The response message is either <c>ALIVE2_X_RESP</c> or - <c>ALIVE2_RESP</c> depending on distribution version. If both the node - and EPMD support distribution version 6 then response is - <c>ALIVE2_X_RESP</c> otherwise it is the older <c>ALIVE2_RESP</c>:</p> - - <table align="left"> - <row> - <cell align="center">1</cell> - <cell align="center">1</cell> - <cell align="center">4</cell> - </row> - <row> - <cell align="center"><c>118</c></cell> - <cell align="center"><c>Result</c></cell> - <cell align="center"><c>Creation</c></cell> - </row> - <tcaption>ALIVE2_X_RESP (118) with 32 bit creation</tcaption> - </table> + <p>The response message <c>ALIVE2_RESP</c> is as follows:</p> <table align="left"> <row> @@ -215,7 +197,7 @@ <cell align="center"><c>Result</c></cell> <cell align="center"><c>Creation</c></cell> </row> - <tcaption>ALIVE2_RESP (121) with 16-bit creation</tcaption> + <tcaption>ALIVE2_RESP (121)</tcaption> </table> <p>Result = 0 -> ok, result > 0 -> error.</p> diff --git a/erts/doc/src/erl_ext_fig.gif b/erts/doc/src/erl_ext_fig.gif Binary files differindex 40dd17bd5e..14d6bbc871 100644 --- a/erts/doc/src/erl_ext_fig.gif +++ b/erts/doc/src/erl_ext_fig.gif diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 21351df656..448f41b523 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -894,7 +894,7 @@ RUN_OBJS += \ $(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o \ $(OBJDIR)/erl_msacc.o $(OBJDIR)/erl_lock_flags.o \ $(OBJDIR)/erl_io_queue.o $(OBJDIR)/erl_db_catree.o \ - $(ESOCK_RUN_OBJS) + $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o 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/bif.c b/erts/emulator/beam/bif.c index d0e2d9afc2..b81056c774 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1915,7 +1915,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp, erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Discarding message %T from %T to %T in an old " - "incarnation (%u) of this node (%u)\n", + "incarnation (%d) of this node (%d)\n", msg, p->common.id, to, @@ -1959,7 +1959,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp, erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Discarding message %T from %T to %T in an old " - "incarnation (%u) of this node (%u)\n", + "incarnation (%d) of this node (%d)\n", msg, p->common.id, to, diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 7666f23a4f..522f50287a 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -2176,24 +2176,6 @@ term_to_Uint64(Eterm term, Uint64 *up) #endif } -int -term_to_Uint32(Eterm term, Uint32 *up) -{ -#if ERTS_SIZEOF_ETERM == 4 - return term_to_Uint(term,up); -#else - if (is_small(term)) { - Sint i = signed_val(term); - if (i >= 0) { - *up = (Uint32) i; - return 1; - } - } - *up = BADARG; - return 0; -#endif -} - int term_to_Sint(Eterm term, Sint *sp) { diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 3fed076419..ad19cce395 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -168,8 +168,6 @@ Eterm erts_uint64_array_to_big(Uint **, int, int, Uint64 *); int term_to_Uint64(Eterm, Uint64*); int term_to_Sint64(Eterm, Sint64*); #endif -int term_to_Uint32(Eterm, Uint32*); - Uint32 big_to_uint32(Eterm b); int term_equals_2pow32(Eterm); diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 8bbe6450eb..30fe13fad3 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; @@ -3748,10 +3756,12 @@ int distribution_info(fmtfn_t to, void *arg) /* Called by break handler */ BIF_RETTYPE setnode_2(BIF_ALIST_2) { Process *net_kernel; - Uint32 creation; + Uint creation; /* valid creation ? */ - if(!term_to_Uint32(BIF_ARG_2, &creation)) + if(!term_to_Uint(BIF_ARG_2, &creation)) + goto error; + if(creation > 3) goto error; /* valid node name ? */ @@ -3795,7 +3805,7 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2) erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); inc_no_nodes(); - erts_set_this_node(BIF_ARG_1, creation); + erts_set_this_node(BIF_ARG_1, (Uint32) creation); erts_is_alive = 1; send_nodes_mon_msgs(NULL, am_nodeup, BIF_ARG_1, am_visible, NIL); erts_thr_progress_unblock(); diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index f953a2ab8c..067028634b 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -54,12 +54,11 @@ #define DFLAG_DIST_MANDATORY (DFLAG_EXTENDED_REFERENCES \ | DFLAG_EXTENDED_PIDS_PORTS \ | DFLAG_UTF8_ATOMS \ - | DFLAG_NEW_FUN_TAGS \ - | DFLAG_BIG_CREATION) + | DFLAG_NEW_FUN_TAGS) /* * Additional optimistic flags when encoding toward pending connection. - * If remote node (erl_interface) does not support these then we may need + * If remote node (erl_interface) does not supporting these then we may need * to transcode messages enqueued before connection setup was finished. */ #define DFLAG_DIST_HOPEFULLY (DFLAG_EXPORT_PTR_TAG \ @@ -76,6 +75,7 @@ | DFLAG_SMALL_ATOM_TAGS \ | DFLAG_UTF8_ATOMS \ | DFLAG_MAP_TAG \ + | DFLAG_BIG_CREATION \ | DFLAG_SEND_SENDER \ | DFLAG_BIG_SEQTRACE_LABELS \ | DFLAG_EXIT_PAYLOAD \ diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index a7424bbcb8..7ff345a54b 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); @@ -2797,10 +2799,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) } else if (BIF_ARG_1 == am_threads) { return am_true; } else if (BIF_ARG_1 == am_creation) { - Uint hsz = 0; - erts_bld_uint(NULL, &hsz, erts_this_node->creation); - hp = hsz ? HAlloc(BIF_P, hsz) : NULL; - BIF_RET(erts_bld_uint(&hp, NULL, erts_this_node->creation)); + return make_small(erts_this_node->creation); } else if (BIF_ARG_1 == am_break_ignored) { extern int ignore_break; if (ignore_break) @@ -3026,6 +3025,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(erts_nif_taints(BIF_P)); } else if (ERTS_IS_ATOM_STR("reader_groups_map", BIF_ARG_1)) { BIF_RET(erts_get_reader_groups_map(BIF_P)); + } else if (ERTS_IS_ATOM_STR("decentralized_counter_groups_map", BIF_ARG_1)) { + BIF_RET(erts_get_decentralized_counter_groups_map(BIF_P)); } else if (ERTS_IS_ATOM_STR("dist_buf_busy_limit", BIF_ARG_1)) { Uint hsz = 0; diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c index 6f8d2f8c35..6a4f43297e 100644 --- a/erts/emulator/beam/erl_cpu_topology.c +++ b/erts/emulator/beam/erl_cpu_topology.c @@ -34,6 +34,7 @@ #include "error.h" #include "bif.h" #include "erl_cpu_topology.h" +#include "erl_flxctr.h" #define ERTS_MAX_READER_GROUPS 64 @@ -58,6 +59,7 @@ static erts_cpu_info_t *cpuinfo; static int max_main_threads; static int reader_groups; +static int decentralized_counter_groups; static ErtsCpuBindData *scheduler2cpu_map; static erts_rwmtx_t cpuinfo_rwmtx; @@ -127,6 +129,8 @@ static erts_cpu_groups_map_t *cpu_groups_maps; static erts_cpu_groups_map_t *reader_groups_map; +static erts_cpu_groups_map_t *decentralized_counter_groups_map; + #define ERTS_TOPOLOGY_CG ERTS_TOPOLOGY_MAX_DEPTH #define ERTS_MAX_CPU_TOPOLOGY_ID ((int) 0xffff) @@ -138,6 +142,7 @@ static void cpu_bind_order_sort(erts_cpu_topology_t *cpudata, static void write_schedulers_bind_change(erts_cpu_topology_t *cpudata, int size); static void reader_groups_callback(int, ErtsSchedulerData *, int, void *); +static void flxctr_groups_callback(int, ErtsSchedulerData *, int, void *); static erts_cpu_groups_map_t *add_cpu_groups(int groups, erts_cpu_groups_callback_t callback, void *arg); @@ -1646,7 +1651,8 @@ erts_get_logical_processors(int *conf, int *onln, int *avail) } void -erts_pre_early_init_cpu_topology(int *max_rg_p, +erts_pre_early_init_cpu_topology(int *max_dcg_p, + int *max_rg_p, int *conf_p, int *onln_p, int *avail_p) @@ -1654,6 +1660,7 @@ erts_pre_early_init_cpu_topology(int *max_rg_p, cpu_groups_maps = NULL; no_cpu_groups_callbacks = 0; *max_rg_p = ERTS_MAX_READER_GROUPS; + *max_dcg_p = ERTS_MAX_FLXCTR_GROUPS; cpuinfo = erts_cpu_info_create(); get_logical_processors(conf_p, onln_p, avail_p); } @@ -1662,7 +1669,9 @@ void erts_early_init_cpu_topology(int no_schedulers, int *max_main_threads_p, int max_reader_groups, - int *reader_groups_p) + int *reader_groups_p, + int max_decentralized_counter_groups, + int *decentralized_counter_groups_p) { user_cpudata = NULL; user_cpudata_size = 0; @@ -1687,6 +1696,12 @@ erts_early_init_cpu_topology(int no_schedulers, max_main_threads = no_schedulers; *max_main_threads_p = max_main_threads; + decentralized_counter_groups = max_main_threads; + if (decentralized_counter_groups <= 1 || max_decentralized_counter_groups <= 1) + decentralized_counter_groups = 1; + if (decentralized_counter_groups > max_decentralized_counter_groups) + decentralized_counter_groups = max_decentralized_counter_groups; + *decentralized_counter_groups_p = decentralized_counter_groups; reader_groups = max_main_threads; if (reader_groups <= 1 || max_reader_groups <= 1) reader_groups = 0; @@ -1718,6 +1733,9 @@ erts_init_cpu_topology(void) reader_groups_map = add_cpu_groups(reader_groups, reader_groups_callback, NULL); + decentralized_counter_groups_map = add_cpu_groups(decentralized_counter_groups, + flxctr_groups_callback, + NULL); if (cpu_bind_order == ERTS_CPU_BIND_NONE) erts_rwmtx_rwunlock(&cpuinfo_rwmtx); @@ -1789,6 +1807,15 @@ reader_groups_callback(int suspending, erts_rwmtx_set_reader_group(suspending ? 0 : group+1); } +void +flxctr_groups_callback(int suspending, + ErtsSchedulerData *esdp, + int group, + void *unused) +{ + erts_flxctr_set_slot(suspending ? 0 : group+1); +} + static Eterm get_cpu_groups_map(Process *c_p, erts_cpu_groups_map_t *map, int offset); @@ -1821,6 +1848,16 @@ erts_get_reader_groups_map(Process *c_p) return res; } +Eterm +erts_get_decentralized_counter_groups_map(Process *c_p) +{ + Eterm res; + erts_rwmtx_rlock(&cpuinfo_rwmtx); + res = get_cpu_groups_map(c_p, decentralized_counter_groups_map, 1); + erts_rwmtx_runlock(&cpuinfo_rwmtx); + return res; +} + /* * CPU groups */ diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h index 88bcad79ab..4a428d7972 100644 --- a/erts/emulator/beam/erl_cpu_topology.h +++ b/erts/emulator/beam/erl_cpu_topology.h @@ -27,14 +27,19 @@ #ifndef ERL_CPU_TOPOLOGY_H__ #define ERL_CPU_TOPOLOGY_H__ -void erts_pre_early_init_cpu_topology(int *max_rg_p, - int *conf_p, - int *onln_p, - int *avail_p); -void erts_early_init_cpu_topology(int no_schedulers, - int *max_main_threads_p, - int max_reader_groups, - int *reader_groups_p); +void +erts_pre_early_init_cpu_topology(int *max_dcg_p, + int *max_rg_p, + int *conf_p, + int *onln_p, + int *avail_p); +void +erts_early_init_cpu_topology(int no_schedulers, + int *max_main_threads_p, + int max_reader_groups, + int *reader_groups_p, + int max_decentralized_counter_groups, + int *decentralized_counter_groups_p); void erts_init_cpu_topology(void); @@ -70,6 +75,7 @@ Eterm erts_bind_schedulers(Process *c_p, Eterm how); Eterm erts_get_schedulers_binds(Process *c_p); Eterm erts_get_reader_groups_map(Process *c_p); +Eterm erts_get_decentralized_counter_groups_map(Process *c_p); Eterm erts_set_cpu_topology(Process *c_p, Eterm term); Eterm erts_get_cpu_topology_term(Process *c_p, Eterm which); diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 0a50af4d1a..c0f5c506f4 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -42,6 +42,7 @@ #include "bif.h" #include "big.h" #include "erl_binary.h" +#include "bif.h" erts_atomic_t erts_ets_misc_mem_size; @@ -64,6 +65,11 @@ do { \ } \ }while(0) +#define DB_GET_APPROX_NITEMS(DB) \ + erts_flxctr_read_approx(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define DB_GET_APPROX_MEM_CONSUMED(DB) \ + erts_flxctr_read_approx(&(DB)->common.counters, ERTS_DB_TABLE_MEM_COUNTER_ID) + static BIF_RETTYPE db_bif_fail(Process* p, Uint freason, Uint bif_ix, Export* bif_exp) { @@ -398,8 +404,9 @@ static void free_dbtable(void *vtb) { DbTable *tb = (DbTable *) vtb; - - ASSERT(erts_atomic_read_nob(&tb->common.memory_size) == sizeof(DbTable)); + ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) || + sizeof(DbTable) == erts_flxctr_read_approx(&tb->common.counters, + ERTS_DB_TABLE_MEM_COUNTER_ID)); erts_rwmtx_destroy(&tb->common.rwlock); erts_mtx_destroy(&tb->common.fixlock); @@ -408,7 +415,8 @@ free_dbtable(void *vtb) if (tb->common.btid) erts_bin_release(tb->common.btid); - erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable)); + erts_flxctr_destroy(&tb->common.counters, ERTS_ALC_T_DB_TABLE); + erts_free(ERTS_ALC_T_DB_TABLE, tb); } static void schedule_free_dbtable(DbTable* tb) @@ -1731,12 +1739,16 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) */ { DbTable init_tb; - - erts_atomic_init_nob(&init_tb.common.memory_size, 0); + erts_flxctr_init(&init_tb.common.counters, 0, 2, ERTS_ALC_T_DB_TABLE); tb = (DbTable*) erts_db_alloc(ERTS_ALC_T_DB_TABLE, &init_tb, sizeof(DbTable)); - erts_atomic_init_nob(&tb->common.memory_size, - erts_atomic_read_nob(&init_tb.common.memory_size)); + erts_flxctr_init(&tb->common.counters, + status & DB_CA_ORDERED_SET, + 2, + ERTS_ALC_T_DB_TABLE); + erts_flxctr_add(&tb->common.counters, + ERTS_DB_TABLE_MEM_COUNTER_ID, + DB_GET_APPROX_MEM_CONSUMED(&init_tb)); } tb->common.meth = meth; @@ -1750,8 +1762,6 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) tb->common.owner = BIF_P->common.id; set_heir(BIF_P, tb, heir, heir_data); - erts_atomic_init_nob(&tb->common.nitems, 0); - tb->common.fixing_procs = NULL; tb->common.compress = is_compressed; #ifdef ETS_DBG_FORCE_TRAP @@ -2128,19 +2138,18 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2) { SWord initial_reds = ERTS_BIF_REDS_LEFT(BIF_P); SWord reds = initial_reds; - Eterm nitems; + Eterm nitems_holder = THE_NON_VALUE; DbTable* tb; - CHECK_TABLES(); DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE, BIF_ets_internal_delete_all_2); if (BIF_ARG_2 == am_undefined) { - nitems = erts_make_integer(erts_atomic_read_nob(&tb->common.nitems), - BIF_P); - - reds = tb->common.meth->db_delete_all_objects(BIF_P, tb, reds); - + reds = tb->common.meth->db_delete_all_objects(BIF_P, + tb, + reds, + &nitems_holder); + ASSERT(nitems_holder != THE_NON_VALUE); ASSERT(!(tb->common.status & DB_BUSY)); if (reds < 0) { @@ -2159,7 +2168,7 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2) db_unlock(tb, LCK_WRITE); BUMP_ALL_REDS(BIF_P); BIF_TRAP2(bif_export[BIF_ets_internal_delete_all_2], BIF_P, - BIF_ARG_1, nitems); + BIF_ARG_1, nitems_holder); } else { /* Done, no trapping needed */ @@ -2169,15 +2178,19 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2) } else { /* - * The table lookup succeeded and second argument is nitems + * The table lookup succeeded and second argument is nitems_holder * and not 'undefined', which means we have trapped at least once * and are now done. */ - nitems = BIF_ARG_2; + nitems_holder = BIF_ARG_2; } - db_unlock(tb, LCK_WRITE); + { + Eterm nitems = + tb->common.meth->db_delete_all_objects_get_nitems_from_holder(BIF_P, + nitems_holder); BIF_RET(nitems); + } } static void delete_all_objects_continue(Process* p, DbTable* tb) @@ -2190,7 +2203,7 @@ static void delete_all_objects_continue(Process* p, DbTable* tb) if ((tb->common.status & (DB_DELETE|DB_BUSY)) != DB_BUSY) return; - reds = tb->common.meth->db_delete_all_objects(p, tb, reds); + reds = tb->common.meth->db_delete_all_objects(p, tb, reds, NULL); if (reds < 0) { BUMP_ALL_REDS(p); @@ -3277,13 +3290,29 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1) int i; Eterm* hp; Uint freason; + Sint size = -1; + Sint memory = -1; + Eterm table; + int is_ctrs_read_result_set = 0; /*Process* rp = NULL;*/ /* If/when we implement lockless private tables: Eterm owner; */ - - if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ, &freason)) == NULL) { - if (freason == BADARG && (is_atom(BIF_ARG_1) || is_ref(BIF_ARG_1))) + if(is_tuple(BIF_ARG_1) && + is_tuple_arity(BIF_ARG_1, 2) && + erts_flxctr_is_snapshot_result(tuple_val(BIF_ARG_1)[1])) { + Eterm counter_read_result = tuple_val(BIF_ARG_1)[1]; + table = tuple_val(BIF_ARG_1)[2]; + size = erts_flxctr_get_snapshot_result_after_trap(counter_read_result, + ERTS_DB_TABLE_NITEMS_COUNTER_ID); + memory = erts_flxctr_get_snapshot_result_after_trap(counter_read_result, + ERTS_DB_TABLE_MEM_COUNTER_ID); + is_ctrs_read_result_set = 1; + } else { + table = BIF_ARG_1; + } + if ((tb = db_get_table(BIF_P, table, DB_INFO, LCK_READ, &freason)) == NULL) { + if (freason == BADARG && (is_atom(table) || is_ref(table))) BIF_RET(am_undefined); else return db_bif_fail(BIF_P, freason, BIF_ets_info_1, NULL); @@ -3314,9 +3343,35 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); } }*/ + + if (!is_ctrs_read_result_set) { + ErtsFlxCtrSnapshotResult res = + erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P); + if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) { + Eterm tuple; + db_unlock(tb, LCK_READ); + hp = HAlloc(BIF_P, 3); + tuple = TUPLE2(hp, res.trap_resume_state, table); + BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, tuple); + } else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) { + db_unlock(tb, LCK_READ); + BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, table); + } else { + size = res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID]; + memory = res.result[ERTS_DB_TABLE_MEM_COUNTER_ID]; + is_ctrs_read_result_set = 1; + } + } for (i = 0; i < sizeof(fields)/sizeof(Eterm); i++) { - results[i] = table_info(BIF_P, tb, fields[i]); - ASSERT(is_value(results[i])); + if (is_ctrs_read_result_set && am_size == fields[i]) { + results[i] = erts_make_integer(size, BIF_P); + } else if (is_ctrs_read_result_set && am_memory == fields[i]) { + Sint words = (Sint) ((memory + sizeof(Sint) - 1) / sizeof(Sint)); + results[i] = erts_make_integer(words, BIF_P); + } else { + results[i] = table_info(BIF_P, tb, fields[i]); + ASSERT(is_value(results[i])); + } } db_unlock(tb, LCK_READ); @@ -3344,14 +3399,43 @@ BIF_RETTYPE ets_info_2(BIF_ALIST_2) DbTable* tb; Eterm ret = THE_NON_VALUE; Uint freason; - + if (erts_flxctr_is_snapshot_result(BIF_ARG_1)) { + Sint res; + if (am_memory == BIF_ARG_2) { + res = erts_flxctr_get_snapshot_result_after_trap(BIF_ARG_1, + ERTS_DB_TABLE_MEM_COUNTER_ID); + res = (Sint) ((res + sizeof(Sint) - 1) / sizeof(Sint)); + } else { + res = erts_flxctr_get_snapshot_result_after_trap(BIF_ARG_1, + ERTS_DB_TABLE_NITEMS_COUNTER_ID); + } + BIF_RET(erts_make_integer(res, BIF_P)); + } if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ, &freason)) == NULL) { if (freason == BADARG && (is_atom(BIF_ARG_1) || is_ref(BIF_ARG_1))) BIF_RET(am_undefined); else return db_bif_fail(BIF_P, freason, BIF_ets_info_2, NULL); } - ret = table_info(BIF_P, tb, BIF_ARG_2); + if (BIF_ARG_2 == am_size || BIF_ARG_2 == am_memory) { + ErtsFlxCtrSnapshotResult res = + erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P); + if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) { + db_unlock(tb, LCK_READ); + BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, res.trap_resume_state, BIF_ARG_2); + } else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) { + db_unlock(tb, LCK_READ); + BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2); + } else if (BIF_ARG_2 == am_size) { + ret = erts_make_integer(res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID], BIF_P); + } else { /* BIF_ARG_2 == am_memory */ + Sint r = res.result[ERTS_DB_TABLE_MEM_COUNTER_ID]; + r = (Sint) ((r + sizeof(Sint) - 1) / sizeof(Sint)); + ret = erts_make_integer(r, BIF_P); + } + } else { + ret = table_info(BIF_P, tb, BIF_ARG_2); + } db_unlock(tb, LCK_READ); if (is_non_value(ret)) { BIF_ERROR(BIF_P, BADARG); @@ -4121,7 +4205,8 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What) int use_monotonic; if (What == am_size) { - ret = make_small(erts_atomic_read_nob(&tb->common.nitems)); + Uint size = (Uint) (DB_GET_APPROX_NITEMS(tb)); + ret = erts_make_integer(size, p); } else if (What == am_type) { if (tb->common.status & DB_SET) { ret = am_set; @@ -4136,7 +4221,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What) ret = am_bag; } } else if (What == am_memory) { - Uint words = (Uint) ((erts_atomic_read_nob(&tb->common.memory_size) + Uint words = (Uint) ((DB_GET_APPROX_MEM_CONSUMED(tb) + sizeof(Uint) - 1) / sizeof(Uint)); @@ -4294,9 +4379,9 @@ static void print_table(fmtfn_t to, void *to_arg, int show, DbTable* tb) tb->common.meth->db_print(to, to_arg, show, tb); - erts_print(to, to_arg, "Objects: %d\n", (int)erts_atomic_read_nob(&tb->common.nitems)); + erts_print(to, to_arg, "Objects: %d\n", (int)DB_GET_APPROX_NITEMS(tb)); erts_print(to, to_arg, "Words: %bpu\n", - (Uint) ((erts_atomic_read_nob(&tb->common.memory_size) + (Uint) ((DB_GET_APPROX_MEM_CONSUMED(tb) + sizeof(Uint) - 1) / sizeof(Uint))); diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h index dc77fbb60c..b22f35a5ef 100644 --- a/erts/emulator/beam/erl_db.h +++ b/erts/emulator/beam/erl_db.h @@ -160,7 +160,9 @@ do { \ erts_aint_t sz__ = (((erts_aint_t) (ALLOC_SZ)) \ - ((erts_aint_t) (FREE_SZ))); \ ASSERT((TAB)); \ - erts_atomic_add_nob(&(TAB)->common.memory_size, sz__); \ + erts_flxctr_add(&(TAB)->common.counters, \ + ERTS_DB_TABLE_MEM_COUNTER_ID, \ + sz__); \ } while (0) #define ERTS_ETS_MISC_MEM_ADD(SZ) \ @@ -305,10 +307,10 @@ erts_db_free(ErtsAlcType_t type, DbTable *tab, void *ptr, Uint size) ASSERT(ptr != 0); ASSERT(size == ERTS_ALC_DBG_BLK_SZ(ptr)); ERTS_DB_ALC_MEM_UPDATE_(tab, size, 0); - - ASSERT(((void *) tab) != ptr - || erts_atomic_read_nob(&tab->common.memory_size) == 0); - + ASSERT(((void *) tab) != ptr || + tab->common.counters.is_decentralized || + 0 == erts_flxctr_read_centralized(&tab->common.counters, + ERTS_DB_TABLE_MEM_COUNTER_ID)); erts_free(type, ptr); } diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c index 0402c6b7b4..962fe4c4f8 100644 --- a/erts/emulator/beam/erl_db_catree.c +++ b/erts/emulator/beam/erl_db_catree.c @@ -149,7 +149,12 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord); static void db_foreach_offheap_catree(DbTable *, void (*)(ErlOffHeap *, void *), void *); -static SWord db_delete_all_objects_catree(Process* p, DbTable* tbl, SWord reds); +static SWord db_delete_all_objects_catree(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb); +static Eterm db_delete_all_objects_get_nitems_from_holder_catree(Process* p, + Eterm nitems_holder); static int db_lookup_dbterm_catree(Process *, DbTable *, Eterm key, Eterm obj, DbUpdateHandle*); @@ -191,6 +196,7 @@ DbTableMethod db_catree = db_select_replace_continue_catree, db_take_catree, db_delete_all_objects_catree, + db_delete_all_objects_get_nitems_from_holder_catree, db_free_table_catree, db_free_table_continue_catree, db_print_catree, @@ -1357,6 +1363,8 @@ static SWord do_free_base_node_cont(DbTableCATree *tb, SWord num_left) PUSH_NODE(&tb->free_stack_elems, root); root = p; } else { + DEC_NITEMS((DbTable*)tb); + tb->nr_of_deleted_items++; free_term((DbTable*)tb, root); if (--num_left >= 0) { break; @@ -1397,6 +1405,7 @@ int db_create_catree(Process *p, DbTable *tbl) root = create_base_node(tb, NULL); tb->deletion = 0; tb->base_nodes_to_free_list = NULL; + tb->nr_of_deleted_items = 0; erts_atomic_init_relb(&(tb->root), (erts_aint_t)root); return DB_ERROR_NONE; } @@ -2050,6 +2059,7 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord reds) PUSH_NODE(&tb->free_stack_rnodes, GET_ROOT(tb)); tb->is_routing_nodes_freed = 0; tb->base_nodes_to_free_list = NULL; + tb->nr_of_deleted_items = 0; } if ( ! tb->is_routing_nodes_freed ) { reds = do_free_routing_nodes_catree_cont(tb, reds); @@ -2079,13 +2089,57 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord reds) return 1; } -static SWord db_delete_all_objects_catree(Process* p, DbTable* tbl, SWord reds) +static +int db_catree_nr_of_items_deleted_wb_dtor(Binary *context_bin) { + (void)context_bin; + return 1; +} + +typedef struct { + Uint nr_of_deleted_items; +} DbCATreeNrOfItemsDeletedWb; + +static Eterm +create_and_install_num_of_deleted_items_wb_bin(Process *p, DbTableCATree *tb) +{ + Binary* bin = + erts_create_magic_binary(sizeof(DbCATreeNrOfItemsDeletedWb), + db_catree_nr_of_items_deleted_wb_dtor); + DbCATreeNrOfItemsDeletedWb* data = ERTS_MAGIC_BIN_DATA(bin); + Eterm* hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE); + Eterm mref = erts_mk_magic_ref(&hp, &MSO(p), bin); + data->nr_of_deleted_items = 0; + tb->nr_of_deleted_items_wb = bin; + erts_refc_inctest(&bin->intern.refc, 2); + return mref; +} + +static Eterm db_delete_all_objects_get_nitems_from_holder_catree(Process* p, + Eterm mref) { + Binary* bin = erts_magic_ref2bin(mref); + DbCATreeNrOfItemsDeletedWb* data = ERTS_MAGIC_BIN_DATA(bin); + return erts_make_integer(data->nr_of_deleted_items, p); +} + +static SWord db_delete_all_objects_catree(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb) +{ + DbTableCATree *tb = &tbl->catree; + DbCATreeNrOfItemsDeletedWb* data; + if (!tb->deletion) { + *nitems_holder_wb = + create_and_install_num_of_deleted_items_wb_bin(p, tb); + } reds = db_free_table_continue_catree(tbl, reds); if (reds < 0) return reds; + data = ERTS_MAGIC_BIN_DATA(tb->nr_of_deleted_items_wb); + data->nr_of_deleted_items = tb->nr_of_deleted_items; + erts_bin_release(tb->nr_of_deleted_items_wb); db_create_catree(p, tbl); - erts_atomic_set_nob(&tbl->catree.common.nitems, 0); return reds; } diff --git a/erts/emulator/beam/erl_db_catree.h b/erts/emulator/beam/erl_db_catree.h index 418837be8e..fde442eaf5 100644 --- a/erts/emulator/beam/erl_db_catree.h +++ b/erts/emulator/beam/erl_db_catree.h @@ -87,6 +87,10 @@ typedef struct db_table_catree { CATreeNodeStack free_stack_rnodes; DbTableCATreeNode *base_nodes_to_free_list; int is_routing_nodes_freed; + /* The fields below are used by delete_all_objects and + select_delete(DeleteAll)*/ + Uint nr_of_deleted_items; + Binary* nr_of_deleted_items_wb; } DbTableCATree; typedef struct { @@ -104,7 +108,6 @@ void db_initialize_catree(void); int db_create_catree(Process *p, DbTable *tbl); - TreeDbTerm** catree_find_root(Eterm key, CATreeRootIterator*); TreeDbTerm** catree_find_next_from_pb_key_root(Eterm key, CATreeRootIterator*); diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index f225730029..ceaccf7e44 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -85,6 +85,14 @@ #include "erl_db_hash.h" +#define ADD_NITEMS(DB, TO_ADD) \ + erts_flxctr_add(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID, TO_ADD) +#define INC_NITEMS(DB) \ + erts_flxctr_inc_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define DEC_NITEMS(DB) \ + erts_flxctr_dec_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define RESET_NITEMS(DB) \ + erts_flxctr_reset(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) /* * The following symbols can be manipulated to "tune" the linear hash array */ @@ -121,7 +129,9 @@ : ((struct segment**) erts_atomic_read_nob(&(tb)->segtab))) #endif #define NACTIVE(tb) ((int)erts_atomic_read_nob(&(tb)->nactive)) -#define NITEMS(tb) ((int)erts_atomic_read_nob(&(tb)->common.nitems)) +#define NITEMS(tb) \ + ((Sint)erts_flxctr_read_centralized(&(tb)->common.counters, \ + ERTS_DB_TABLE_NITEMS_COUNTER_ID)) #define SLOT_IX_TO_SEG_IX(i) (((i)+(EXT_SEGSZ-FIRST_SEGSZ)) >> EXT_SEGSZ_EXP) @@ -444,7 +454,12 @@ static void db_foreach_offheap_hash(DbTable *, void (*)(ErlOffHeap *, void *), void *); -static SWord db_delete_all_objects_hash(Process* p, DbTable* tbl, SWord reds); +static SWord db_delete_all_objects_hash(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb); +static Eterm db_delete_all_objects_get_nitems_from_holder_hash(Process* p, + Eterm nitems_holder); #ifdef HARDDEBUG static void db_check_table_hash(DbTableHash *tb); #endif @@ -548,6 +563,7 @@ DbTableMethod db_hash = db_select_replace_continue_hash, db_take_hash, db_delete_all_objects_hash, + db_delete_all_objects_get_nitems_from_holder_hash, db_free_empty_table_hash, db_free_table_continue_hash, db_print_hash, @@ -806,7 +822,7 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail) if (tb->common.status & DB_SET) { HashDbTerm* bnext = b->next; if (is_pseudo_deleted(b)) { - erts_atomic_inc_nob(&tb->common.nitems); + INC_NITEMS(tb); b->pseudo_deleted = 0; } else if (key_clash_fail) { @@ -835,7 +851,7 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail) do { if (db_eq(&tb->common,obj,&q->dbterm)) { if (is_pseudo_deleted(q)) { - erts_atomic_inc_nob(&tb->common.nitems); + INC_NITEMS(tb); q->pseudo_deleted = 0; ASSERT(q->hvalue == hval); if (q != b) { /* must move to preserve key insertion order */ @@ -858,7 +874,7 @@ Lnew: q->pseudo_deleted = 0; q->next = b; *bp = q; - nitems = erts_atomic_inc_read_nob(&tb->common.nitems); + nitems = INC_NITEMS(tb); WUNLOCK_HASH(lck); { int nactive = NACTIVE(tb); @@ -1056,7 +1072,7 @@ int db_erase_hash(DbTable *tbl, Eterm key, Eterm *ret) } WUNLOCK_HASH(lck); if (nitems_diff) { - erts_atomic_add_nob(&tb->common.nitems, nitems_diff); + ADD_NITEMS(tb, nitems_diff); try_shrink(tb); } free_term_list(tb, free_us); @@ -1117,7 +1133,7 @@ static int db_erase_object_hash(DbTable *tbl, Eterm object, Eterm *ret) } WUNLOCK_HASH(lck); if (nitems_diff) { - erts_atomic_add_nob(&tb->common.nitems, nitems_diff); + ADD_NITEMS(tb, nitems_diff); try_shrink(tb); } free_term_list(tb, free_us); @@ -2023,7 +2039,7 @@ static int select_delete_on_match_res(traverse_context_t* ctx_base, Sint slot_ix del->next = ctx->free_us; ctx->free_us = del; } - erts_atomic_dec_nob(&ctx->base.tb->common.nitems); + DEC_NITEMS(ctx->base.tb); return 1; } @@ -2300,7 +2316,7 @@ static int db_take_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret) } WUNLOCK_HASH(lck); if (nitems_diff) { - erts_atomic_add_nob(&tb->common.nitems, nitems_diff); + ADD_NITEMS(tb, nitems_diff); try_shrink(tb); } free_term_list(tb, free_us); @@ -2360,7 +2376,7 @@ static SWord db_mark_all_deleted_hash(DbTable *tbl, SWord reds) fixdel->slot = NACTIVE(tb) - 1; fixdel->all = 1; fixdel->trap = 0; - erts_atomic_set_nob(&tb->common.nitems, 0); + RESET_NITEMS(tb); return loops < 0 ? 0 : loops / LOOPS_PER_REDUCTION; } @@ -2468,7 +2484,8 @@ static SWord db_free_table_continue_hash(DbTable *tbl, SWord reds) (void*)tb->locks, sizeof(DbTableHashFineLocks)); tb->locks = NULL; } - ASSERT(erts_atomic_read_nob(&tb->common.memory_size) == sizeof(DbTable)); + ASSERT(sizeof(DbTable) == erts_flxctr_read_approx(&tb->common.counters, + ERTS_DB_TABLE_MEM_COUNTER_ID)); return reds; /* Done */ } @@ -3080,7 +3097,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj, ASSERT(q->hvalue == hval); q->pseudo_deleted = 0; *bp = b = q; - erts_atomic_inc_nob(&tb->common.nitems); + INC_NITEMS(tb); } HRelease(p, hend, htop); @@ -3123,7 +3140,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) } WUNLOCK_HASH(lck); - erts_atomic_dec_nob(&tb->common.nitems); + DEC_NITEMS(tb); try_shrink(tb); } else { if (handle->flags & DB_MUST_RESIZE) { @@ -3132,7 +3149,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) } if (handle->flags & DB_INC_TRY_GROW) { int nactive; - int nitems = erts_atomic_inc_read_nob(&tb->common.nitems); + int nitems = INC_NITEMS(tb); WUNLOCK_HASH(lck); nactive = NACTIVE(tb); @@ -3153,8 +3170,17 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) return; } -static SWord db_delete_all_objects_hash(Process* p, DbTable* tbl, SWord reds) +static SWord db_delete_all_objects_hash(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb) { + if (nitems_holder_wb != NULL) { + Uint nr_of_items = + erts_flxctr_read_centralized(&tbl->common.counters, + ERTS_DB_TABLE_NITEMS_COUNTER_ID); + *nitems_holder_wb = erts_make_integer(nr_of_items, p); + } if (IS_FIXED(tbl)) { reds = db_mark_all_deleted_hash(tbl, reds); } else { @@ -3163,11 +3189,16 @@ static SWord db_delete_all_objects_hash(Process* p, DbTable* tbl, SWord reds) return reds; db_create_hash(p, tbl); - erts_atomic_set_nob(&tbl->hash.common.nitems, 0); + RESET_NITEMS(tbl); } return reds; } +static Eterm db_delete_all_objects_get_nitems_from_holder_hash(Process* p, + Eterm nitems_holder){ + return nitems_holder; +} + void db_foreach_offheap_hash(DbTable *tbl, void (*func)(ErlOffHeap *, void *), void * arg) diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index f9ba04f399..492ea81b63 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -51,9 +51,20 @@ #include "erl_db_tree_util.h" #define GETKEY_WITH_POS(Keypos, Tplp) (*((Tplp) + Keypos)) -#define NITEMS(tb) ((int)erts_atomic_read_nob(&(tb)->common.nitems)) -#define TREE_MAX_ELEMENTS 0xFFFFFFFFUL +#define NITEMS_CENTRALIZED(tb) \ + ((Sint)erts_flxctr_read_centralized(&(tb)->common.counters, \ + ERTS_DB_TABLE_NITEMS_COUNTER_ID)) +#define ADD_NITEMS(DB, TO_ADD) \ + erts_flxctr_add(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID, TO_ADD) +#define INC_NITEMS(DB) \ + erts_flxctr_inc(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define INC_NITEMS_CENTRALIZED(DB) \ + erts_flxctr_inc_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define RESET_NITEMS(DB) \ + erts_flxctr_reset(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define IS_CENTRALIZED_CTR(tb) (!(tb)->common.counters.is_decentralized) +#define APPROX_MEM_CONSUMED(tb) erts_flxctr_read_approx(&(tb)->common.counters, ERTS_DB_TABLE_MEM_COUNTER_ID) #define TOPN_NODE(Dtt, Pos) \ (((Pos) < Dtt->pos) ? \ @@ -296,7 +307,7 @@ int tree_balance_right(TreeDbTerm **this); static int delsub(TreeDbTerm **this); static TreeDbTerm *slot_search(Process *p, TreeDbTerm *root, Sint slot, DbTable *tb, DbTableTree *stack_container, - CATreeRootIterator *iter); + CATreeRootIterator *iter, int* is_EOT); static TreeDbTerm *find_node(DbTableCommon *tb, TreeDbTerm *root, Eterm key, DbTableTree *stack_container); static TreeDbTerm **find_node2(DbTableCommon *tb, TreeDbTerm **root, Eterm key); @@ -433,8 +444,12 @@ static void db_foreach_offheap_tree(DbTable *, void (*)(ErlOffHeap *, void *), void *); -static SWord db_delete_all_objects_tree(Process* p, DbTable* tbl, SWord reds); - +static SWord db_delete_all_objects_tree(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb); +static Eterm db_delete_all_objects_get_nitems_from_holder_tree(Process* p, + Eterm nitems_holder); #ifdef HARDDEBUG static void db_check_table_tree(DbTable *tbl); #endif @@ -478,6 +493,7 @@ DbTableMethod db_tree = db_select_replace_continue_tree, db_take_tree, db_delete_all_objects_tree, + db_delete_all_objects_get_nitems_from_holder_tree, db_free_empty_table_tree, db_free_table_continue_tree, db_print_tree, @@ -595,7 +611,8 @@ int db_last_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, } if (stack) { PUSH_NODE(stack, this); - stack->slot = NITEMS(tbl); + /* Always centralized counters when static stack is used */ + stack->slot = NITEMS_CENTRALIZED(tbl); release_stack(tbl,stack_container,stack); } *ret = db_copy_key(p, tbl, &this->dbterm); @@ -661,10 +678,7 @@ int db_put_tree_common(DbTableCommon *tb, TreeDbTerm **root, Eterm obj, for (;;) if (!*this) { /* Found our place */ state = 1; - if (erts_atomic_inc_read_nob(&tb->nitems) >= TREE_MAX_ELEMENTS) { - erts_atomic_dec_nob(&tb->nitems); - return DB_ERROR_SYSRES; - } + INC_NITEMS(((DbTable*)tb)); *this = new_dbterm(tb, obj); (*this)->balance = 0; (*this)->left = (*this)->right = NULL; @@ -888,7 +902,7 @@ int db_slot_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, TreeDbTerm *st; Eterm *hp, *hend; Eterm copy; - + int is_EOT = 0; /* * The notion of a "slot" is not natural in a tree, but we try to * simulate it by giving the n'th node in the tree instead. @@ -899,10 +913,10 @@ int db_slot_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, if (is_not_small(slot_term) || ((slot = signed_val(slot_term)) < 0) || - (slot > NITEMS(tbl))) + (IS_CENTRALIZED_CTR(tbl) && slot > NITEMS_CENTRALIZED(tbl))) return DB_ERROR_BADPARAM; - if (slot == NITEMS(tbl)) { + if (IS_CENTRALIZED_CTR(tbl) && slot == NITEMS_CENTRALIZED(tbl)) { *ret = am_EOT; return DB_ERROR_NONE; } @@ -912,7 +926,11 @@ int db_slot_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, * are counted from 1 and up. */ ++slot; - st = slot_search(p, root, slot, tbl, stack_container, iter); + st = slot_search(p, root, slot, tbl, stack_container, iter, &is_EOT); + if (is_EOT) { + *ret = am_EOT; + return DB_ERROR_NONE; + } if (st == NULL) { *ret = am_false; return DB_ERROR_UNSPEC; @@ -2244,7 +2262,8 @@ void db_print_tree_common(fmtfn_t to, void *to_arg, erts_print(to, to_arg, "\n" "------------------------------------------------\n"); #else - erts_print(to, to_arg, "Ordered set (AVL tree), Elements: %d\n", NITEMS(tbl)); + erts_print(to, to_arg, "Ordered set (AVL tree), Elements: %d\n", + erts_flxctr_read_approx(&tbl->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)); #endif } @@ -2281,24 +2300,41 @@ static SWord db_free_table_continue_tree(DbTable *tbl, SWord reds) (DbTable *) tb, (void *) tb->static_stack.array, sizeof(TreeDbTerm *) * STACK_NEED); - ASSERT((erts_atomic_read_nob(&tb->common.memory_size) - == sizeof(DbTable)) || - (erts_atomic_read_nob(&tb->common.memory_size) - == (sizeof(DbTable) + sizeof(DbFixation)))); + ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) || + ((APPROX_MEM_CONSUMED(tb) + == sizeof(DbTable)) || + (APPROX_MEM_CONSUMED(tb) + == (sizeof(DbTable) + sizeof(DbFixation))))); } return reds; } -static SWord db_delete_all_objects_tree(Process* p, DbTable* tbl, SWord reds) +static SWord db_delete_all_objects_tree(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb) { + if (nitems_holder_wb != NULL) { + Uint nr_of_items = + erts_flxctr_read_centralized(&tbl->common.counters, + ERTS_DB_TABLE_NITEMS_COUNTER_ID); + *nitems_holder_wb = erts_make_integer(nr_of_items, p); + } reds = db_free_table_continue_tree(tbl, reds); if (reds < 0) return reds; db_create_tree(p, tbl); - erts_atomic_set_nob(&tbl->tree.common.nitems, 0); + RESET_NITEMS(tbl); return reds; } +static Eterm db_delete_all_objects_get_nitems_from_holder_tree(Process* p, + Eterm holder) +{ + (void)p; + return holder; +} + static void do_db_tree_foreach_offheap(TreeDbTerm *, void (*)(ErlOffHeap *, void *), void *); @@ -2383,7 +2419,7 @@ static TreeDbTerm *linkout_tree(DbTableCommon *tb, TreeDbTerm **root, tstack[tpos++] = this; state = delsub(this); } - erts_atomic_dec_nob(&tb->nitems); + DEC_NITEMS(((DbTable*)tb)); break; } } @@ -2450,7 +2486,7 @@ static TreeDbTerm *linkout_object_tree(DbTableCommon *tb, TreeDbTerm **root, tstack[tpos++] = this; state = delsub(this); } - erts_atomic_dec_nob(&tb->nitems); + DEC_NITEMS(((DbTable*)tb)); break; } } @@ -2745,7 +2781,8 @@ static int delsub(TreeDbTerm **this) static TreeDbTerm *slot_search(Process *p, TreeDbTerm *root, Sint slot, DbTable *tb, DbTableTree *stack_container, - CATreeRootIterator *iter) + CATreeRootIterator *iter, + int* is_EOT) { TreeDbTerm *this; TreeDbTerm *tmp; @@ -2837,8 +2874,12 @@ static TreeDbTerm *slot_search(Process *p, TreeDbTerm *root, break; next_root: - if (!iter) + if (!iter) { + if (stack->slot == (slot-1)) { + *is_EOT = 1; + } break; /* EOT */ + } ASSERT(slot > stack->slot); if (lastobj) { @@ -2846,8 +2887,12 @@ next_root: lastobj = NULL; } pp = catree_find_next_root(iter, &lastkey); - if (!pp) + if (!pp) { + if (stack->slot == (slot-1)) { + *is_EOT = 1; + } break; /* EOT */ + } root = *pp; stack->pos = 0; find_next(&tb->common, root, stack, lastkey); diff --git a/erts/emulator/beam/erl_db_tree_util.h b/erts/emulator/beam/erl_db_tree_util.h index 02df74678d..ba4a8f79e5 100644 --- a/erts/emulator/beam/erl_db_tree_util.h +++ b/erts/emulator/beam/erl_db_tree_util.h @@ -25,6 +25,8 @@ ** Internal functions and macros used by both the CA tree and the AVL tree */ + +#if defined(ARCH_32) /* ** A stack of this size is enough for an AVL tree with more than ** 0xFFFFFFFF elements. May be subject to change if @@ -34,8 +36,19 @@ ** Where n denotes the number of nodes, h(n) the height of the tree ** with n nodes and log is the binary logarithm. */ - #define STACK_NEED 50 +#elif defined(ARCH_64) +/* +** A stack of this size is enough for an AVL tree with more than +** 2^61 elements. +** The Maximal height of an AVL tree is calculated as above. +*/ +#define STACK_NEED 90 +#else +#error "Unsported architecture" +#endif + + #define PUSH_NODE(Dtt, Tdt) \ ((Dtt)->array[(Dtt)->pos++] = Tdt) @@ -50,6 +63,9 @@ #define EMPTY_NODE(Dtt) (TOP_NODE(Dtt) == NULL) +#define DEC_NITEMS(DB) \ + erts_flxctr_dec(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) + static ERTS_INLINE void free_term(DbTable *tb, TreeDbTerm* p) { db_free_term(tb, p, offsetof(TreeDbTerm, dbterm)); diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index e3d3c0e804..97f2848679 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -21,6 +21,7 @@ #ifndef _DB_UTIL_H #define _DB_UTIL_H +#include "erl_flxctr.h" #include "global.h" #include "erl_message.h" #include "erl_bif_unique.h" @@ -207,8 +208,12 @@ typedef struct db_table_method enum DbIterSafety*); int (*db_take)(Process *, DbTable *, Eterm, Eterm *); - SWord (*db_delete_all_objects)(Process* p, DbTable* db, SWord reds); - + SWord (*db_delete_all_objects)(Process* p, + DbTable* db, + SWord reds, + Eterm* nitems_holder_wb); + Eterm (*db_delete_all_objects_get_nitems_from_holder)(Process* p, + Eterm nitems_holder); int (*db_free_empty_table)(DbTable* db); SWord (*db_free_table_continue)(DbTable* db, SWord reds); @@ -257,6 +262,9 @@ typedef struct { DbTable *prev; } DbTableList; +#define ERTS_DB_TABLE_NITEMS_COUNTER_ID 0 +#define ERTS_DB_TABLE_MEM_COUNTER_ID 1 + /* * This structure contains data for all different types of database * tables. Note that these fields must match the same fields @@ -281,8 +289,11 @@ typedef struct db_table_common { Eterm the_name; /* an atom */ Binary *btid; DbTableMethod* meth; /* table methods */ - erts_atomic_t nitems; /* Total number of items in table */ - erts_atomic_t memory_size;/* Total memory size. NOTE: in bytes! */ + /* The ErtsFlxCtr below contains: + * - Total number of items in table + * - Total memory size (NOTE: in bytes!) */ + ErtsFlxCtr counters; + char extra_for_flxctr[ERTS_FLXCTR_NR_OF_EXTRA_BYTES(2)]; struct { /* Last fixation time */ ErtsMonotonicTime monotonic; ErtsMonotonicTime offset; diff --git a/erts/emulator/beam/erl_flxctr.c b/erts/emulator/beam/erl_flxctr.c new file mode 100644 index 0000000000..35f4a21508 --- /dev/null +++ b/erts/emulator/beam/erl_flxctr.c @@ -0,0 +1,370 @@ +/* + * %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% + */ + +/* + * Author: Kjell Winblad + */ + +#include "erl_flxctr.h" + +static int reader_groups_array_size = 0; +#define ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS (reader_groups_array_size) + +static int erts_flxctr_read_ctx_bin_dtor(Binary *context_bin); +static int erts_flxctr_wait_dtor(Binary *context_bin); + +typedef struct { + ErtsThrPrgrLaterOp later_op; + Process* process; + ErtsFlxCtrDecentralizedCtrArray* array; + ErtsFlxCtrDecentralizedCtrArray* next_array; + ErtsAlcType_t alloc_type; + int nr_of_counters; + Sint result[ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE]; +} DecentralizedReadSnapshotInfo; + +typedef enum { + ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING = 0, + ERTS_FLXCTR_SNAPSHOT_ONGOING = 1, + ERTS_FLXCTR_SNAPSHOT_ONGOING_TP_THREAD_DO_FREE = 2 +} erts_flxctr_snapshot_status; + +static void +thr_prg_wake_up_and_count(void* bin_p) +{ + Binary* bin = bin_p; + DecentralizedReadSnapshotInfo* info = ERTS_MAGIC_BIN_DATA(bin); + Process* p = info->process; + ErtsFlxCtrDecentralizedCtrArray* array = info->array; + ErtsFlxCtrDecentralizedCtrArray* next = info->next_array; + int i, sched; + /* Reset result array */ + for (i = 0; i < info->nr_of_counters; i++) { + info->result[i] = 0; + } + /* Read result from snapshot */ + for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) { + for (i = 0; i < info->nr_of_counters; i++) { + info->result[i] = info->result[i] + + erts_atomic_read_nob(&array->array[sched].counters[i]); + } + } + /* Update the next decentralized counter array */ + for (i = 0; i < info->nr_of_counters; i++) { + erts_atomic_add_nob(&next->array[0].counters[i], info->result[i]); + } + /* Announce that the snapshot is done */ + { + Sint expected = ERTS_FLXCTR_SNAPSHOT_ONGOING; + if (expected != erts_atomic_cmpxchg_mb(&next->snapshot_status, + ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING, + expected)) { + /* The CAS failed which means that this thread need to free the next array. */ + erts_free(info->alloc_type, next->block_start); + } + } + /* Resume the process that requested the snapshot */ + erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); + if (!ERTS_PROC_IS_EXITING(p)) { + erts_resume(p, ERTS_PROC_LOCK_STATUS); + } + /* Free the memory that is no longer needed */ + erts_free(info->alloc_type, array->block_start); + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + erts_proc_dec_refc(p); + erts_bin_release(bin); +} + +typedef struct { + ErtsThrPrgrLaterOp later_op; + Process* process; +} ErtsFlxCtrWakeUpLaterInfo; + +static void +thr_prg_wake_up_later(void* bin_p) +{ + Binary* bin = bin_p; + ErtsFlxCtrWakeUpLaterInfo* info = ERTS_MAGIC_BIN_DATA(bin); + Process* p = info->process; + /* Resume the requesting process */ + erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); + if (!ERTS_PROC_IS_EXITING(p)) { + erts_resume(p, ERTS_PROC_LOCK_STATUS); + } + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + /* Free data */ + erts_proc_dec_refc(p); + erts_bin_release(bin); +} + +static +int erts_flxctr_read_ctx_bin_dtor(Binary *context_bin) { + (void)context_bin; + return 1; +} + +static +int erts_flxctr_wait_dtor(Binary *context_bin) { + (void)context_bin; + return 1; +} + +static void suspend_until_thr_prg(Process* p) +{ + Binary* state_bin; + ErtsFlxCtrWakeUpLaterInfo* info; + state_bin = erts_create_magic_binary(sizeof(ErtsFlxCtrWakeUpLaterInfo), + erts_flxctr_wait_dtor); + info = ERTS_MAGIC_BIN_DATA(state_bin); + info->process = p; + erts_refc_inctest(&state_bin->intern.refc, 1); + erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL); + erts_proc_inc_refc(p); + ERTS_VBUMP_ALL_REDS(p); + erts_schedule_thr_prgr_later_op(thr_prg_wake_up_later, state_bin, &info->later_op); +} + + +static ErtsFlxCtrDecentralizedCtrArray* +create_decentralized_ctr_array(ErtsAlcType_t alloc_type, Uint nr_of_counters) { + /* Allocate an ErtsFlxCtrDecentralizedCtrArray and make sure that + the array field is located at the start of a cache line */ + char* bytes = + erts_alloc(alloc_type, + sizeof(ErtsFlxCtrDecentralizedCtrArray) + + (sizeof(ErtsFlxCtrDecentralizedCtrArrayElem) * + ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS) + + ERTS_CACHE_LINE_SIZE); + void* block_start = bytes; + int bytes_to_next_cacheline_border; + ErtsFlxCtrDecentralizedCtrArray* array; + int i, sched; + bytes = &bytes[offsetof(ErtsFlxCtrDecentralizedCtrArray, array)]; + bytes_to_next_cacheline_border = + ERTS_CACHE_LINE_SIZE - (((Uint)bytes) % ERTS_CACHE_LINE_SIZE); + array = (ErtsFlxCtrDecentralizedCtrArray*) + (&bytes[bytes_to_next_cacheline_border - + (int)offsetof(ErtsFlxCtrDecentralizedCtrArray, array)]); + ASSERT(((Uint)array->array) % ERTS_CACHE_LINE_SIZE == 0); + ASSERT(((Uint)array - (Uint)block_start) <= ERTS_CACHE_LINE_SIZE); + /* Initialize fields */ + erts_atomic_init_nob(&array->snapshot_status, ERTS_FLXCTR_SNAPSHOT_ONGOING); + for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) { + for (i = 0; i < nr_of_counters; i++) { + erts_atomic_init_nob(&array->array[sched].counters[i], 0); + } + } + array->block_start = block_start; + return array; +} + +void erts_flxctr_setup(int decentralized_counter_groups) +{ + reader_groups_array_size = decentralized_counter_groups+1; +} + +void erts_flxctr_init(ErtsFlxCtr* c, + int is_decentralized, + Uint nr_of_counters, + ErtsAlcType_t alloc_type) +{ + ASSERT(nr_of_counters <= ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE); + c->is_decentralized = is_decentralized; + c->nr_of_counters = nr_of_counters; + if (c->is_decentralized) { + ErtsFlxCtrDecentralizedCtrArray* array = + create_decentralized_ctr_array(alloc_type, nr_of_counters); + erts_atomic_set_nob(&array->snapshot_status, + ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING); + erts_atomic_init_nob(&c->u.counters_ptr, (Sint)array); + ASSERT(((Uint)array->array) % ERTS_CACHE_LINE_SIZE == 0); + } else { + int i; + for (i = 0; i < nr_of_counters; i++) { + erts_atomic_init_nob(&c->u.counters[i], 0); + } + } +} + +void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t type) +{ + if (c->is_decentralized) { + if (erts_flxctr_is_snapshot_ongoing(c)) { + ErtsFlxCtrDecentralizedCtrArray* array = + ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c); + /* Try to delegate the resposibilty of freeing to + thr_prg_wake_up_and_count */ + Sint expected = ERTS_FLXCTR_SNAPSHOT_ONGOING; + if (expected != + erts_atomic_cmpxchg_mb(&array->snapshot_status, + ERTS_FLXCTR_SNAPSHOT_ONGOING_TP_THREAD_DO_FREE, + expected)) { + /* The delegation was unsuccessful which means that no + snapshot is ongoing anymore and the freeing needs + to be done here */ + ERTS_ASSERT(!erts_flxctr_is_snapshot_ongoing(c)); + erts_free(type, array->block_start); + } + } else { + erts_free(type, ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c)->block_start); + } + } +} + +ErtsFlxCtrSnapshotResult +erts_flxctr_snapshot(ErtsFlxCtr* c, + ErtsAlcType_t alloc_type, + Process* p) +{ + if (c->is_decentralized) { + ErtsFlxCtrDecentralizedCtrArray* array = ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c); + if (erts_flxctr_is_snapshot_ongoing(c)) { + /* Let the caller try again later */ + ErtsFlxCtrSnapshotResult res = + {.type = ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP}; + suspend_until_thr_prg(p); + return res; + } else { + Eterm* hp; + Binary* state_bin; + Eterm state_mref; + DecentralizedReadSnapshotInfo* info; + ErtsFlxCtrDecentralizedCtrArray* new_array = + create_decentralized_ctr_array(alloc_type, c->nr_of_counters); + int success = + ((Sint)array) == erts_atomic_cmpxchg_mb(&c->u.counters_ptr, + (Sint)new_array, + (Sint)array); + if (!success) { + /* Let the caller try again later */ + ErtsFlxCtrSnapshotResult res = + {.type = ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP}; + suspend_until_thr_prg(p); + erts_free(alloc_type, new_array->block_start); + return res; + } + /* Create binary with info about the operation that can be + sent to the caller and to a thread progress function */ + state_bin = + erts_create_magic_binary(sizeof(DecentralizedReadSnapshotInfo), + erts_flxctr_read_ctx_bin_dtor); + hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE); + state_mref = erts_mk_magic_ref(&hp, &MSO(p), state_bin); + info = ERTS_MAGIC_BIN_DATA(state_bin); + info->alloc_type = alloc_type; + info->array = array; + info->next_array = new_array; + info->process = p; + info->nr_of_counters = c->nr_of_counters; + erts_proc_inc_refc(p); + erts_refc_inctest(&state_bin->intern.refc, 2); + erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL); + ERTS_VBUMP_ALL_REDS(p); + erts_schedule_thr_prgr_later_op(thr_prg_wake_up_and_count, + state_bin, + &info->later_op); + { + ErtsFlxCtrSnapshotResult res = { + .type = ERTS_FLXCTR_GET_RESULT_AFTER_TRAP, + .trap_resume_state = state_mref}; + return res; + } + } + } else { + ErtsFlxCtrSnapshotResult res; + int i; + res.type = ERTS_FLXCTR_DONE; + for (i = 0; i < c->nr_of_counters; i++){ + res.result[i] = erts_flxctr_read_centralized(c, i); + } + return res; + } +} + + +Sint erts_flxctr_get_snapshot_result_after_trap(Eterm result_holder, + Uint counter_nr) +{ + Binary* bin = erts_magic_ref2bin(result_holder); + DecentralizedReadSnapshotInfo* data = ERTS_MAGIC_BIN_DATA(bin);; + return data->result[counter_nr]; +} + +int erts_flxctr_is_snapshot_result(Eterm term) +{ + if (is_internal_magic_ref(term)) { + Binary* bin = erts_magic_ref2bin(term); + return ERTS_MAGIC_BIN_DESTRUCTOR(bin) == erts_flxctr_read_ctx_bin_dtor; + } else return 0; +} + +Sint erts_flxctr_read_approx(ErtsFlxCtr* c, + Uint counter_nr) +{ + if (c->is_decentralized) { + ErtsFlxCtrDecentralizedCtrArray* counter = ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c); + Sint sum = 0; + int sched; + for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) { + sum = sum + erts_atomic_read_nob(&counter->array[sched].counters[counter_nr]); + } + return sum; + } else { + return erts_flxctr_read_centralized(c, counter_nr); + } +} + +int erts_flxctr_is_snapshot_ongoing(ErtsFlxCtr* c) +{ + return c->is_decentralized && + (ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING != + erts_atomic_read_acqb(&ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c)->snapshot_status)); +} + +int erts_flxctr_suspend_until_thr_prg_if_snapshot_ongoing(ErtsFlxCtr* c, Process* p) +{ + if (erts_flxctr_is_snapshot_ongoing(c)) { + suspend_until_thr_prg(p); + return 1; + } else { + return 0; + } +} + +void erts_flxctr_reset(ErtsFlxCtr* c, + Uint counter_nr) +{ + if (c->is_decentralized) { + int sched; + ErtsFlxCtrDecentralizedCtrArray* counter = + ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c); + for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) { + erts_atomic_set_nob(&counter->array[sched].counters[counter_nr], 0); + } + } else { + erts_atomic_set_nob(&c->u.counters[counter_nr], 0); + } +} + + +void erts_flxctr_set_slot(int group) { + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + esdp->flxctr_slot_no = group; +} diff --git a/erts/emulator/beam/erl_flxctr.h b/erts/emulator/beam/erl_flxctr.h new file mode 100644 index 0000000000..5cab02b9eb --- /dev/null +++ b/erts/emulator/beam/erl_flxctr.h @@ -0,0 +1,406 @@ +/* + * %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% + */ + +/** + * @file erl_flxctr.h + * + * @brief This file contains the API of a flexible counter. The + * counter can be configured during its initialization to be + * centralized or decentralized. The centralized configuration makes + * it possible to read the counter value extremely efficiently, but + * updates of the counter value can easily cause contention. The + * decentralized configuration has the reverse trade-off (i.e., + * updates are efficient and scalable but reading the counter value is + * slow and may cause contention). + * + * @author Kjell Winblad + */ + +#ifndef ERL_FLXCTR_H__ +#define ERL_FLXCTR_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "sys.h" +#include "erl_vm.h" +#include "global.h" +#include "error.h" +#include "bif.h" +#include "big.h" +#include "erl_binary.h" +#include "bif.h" +#include <stddef.h> + +/* Public Interface */ + +#define ERTS_MAX_FLXCTR_GROUPS 256 +#define ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE (ERTS_CACHE_LINE_SIZE / sizeof(erts_atomic_t)) + +typedef struct { + int nr_of_counters; + int is_decentralized; + union { + erts_atomic_t counters_ptr; + erts_atomic_t counters[1]; + } u; +} ErtsFlxCtr; + +#define ERTS_FLXCTR_NR_OF_EXTRA_BYTES(NR_OF_COUNTERS) \ + ((NR_OF_COUNTERS-1) * sizeof(erts_atomic_t)) + +/* Called by early_init */ +void erts_flxctr_setup(int decentralized_counter_groups); + +/** + * @brief Initializes an ErtsFlxCtr. The macro + * ERTS_FLXCTR_NR_OF_EXTRA_BYTES should be used to determine how much + * extra space that needs to be allocated directly after the + * ErtsFlxCtr when is_decentralized is set to zero. Each ErtsFlxCtr + * instance may contain up to ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE + * counters. These counters are numbered from zero to + * (ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE-1). Most of the functions in + * this module take a parameter named counter_nr that controls which + * of the ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE counters in the given + * ErtsFlxCtr that should be operated on. + * + * @param c The counter to initialize + * @param is_decentralized Non-zero value to make c decentralized + * @param nr_of_counters The number of counters included in c + * (max ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE) + * @param alloc_type + */ +void erts_flxctr_init(ErtsFlxCtr* c, + int is_decentralized, + Uint nr_of_counters, + ErtsAlcType_t alloc_type); + +/** + * @brief Destroys an initialized counter. + * + * @param c The counter that should be destroyed + * @param alloc_type The allocation type (needs to be the same as the + * one passed to erts_flxctr_init when c was + * initialized) + */ +void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t alloc_type); + +/** + * @brief Adds to_add to the counter with counter_nr in c + * + * @param c the ErtsFlxCtr to operate on + * @param counter_nr The number of the counter in c to modify + * @param to_add The amount that should be added to the specified counter + */ +ERTS_GLB_INLINE +void erts_flxctr_add(ErtsFlxCtr* c, + Uint counter_nr, + int to_add); + +/** + * @brief Increases the specified counter by 1 + * + * @param c The ErtsFlxCtr instance to operate on + * @param counter_nr The number of the counter within c to operate on + */ +ERTS_GLB_INLINE +void erts_flxctr_inc(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief Decreases the specified counter by 1 + */ +ERTS_GLB_INLINE +void erts_flxctr_dec(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief This function tries to return the current value of the + * specified counter but may return an incorrect result if the counter + * is decentralized and other threads are accessing the counter + * concurrently. + * + * @param c The ErtsFlxCtr instance to operate on + * @param counter_nr The number of the counter within c to operate on + * + * @return A snapshot of the specifed counter if c is centralized or a + * possibly incorrect estimate of the counter value if c is + * decentralized + */ +Sint erts_flxctr_read_approx(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief This function can only be used together with an ErtsFlxCtr + * that is configured to be centralized. The function increments the + * specified counter by 1 and returns the value of the counter after + * the increment. + */ +ERTS_GLB_INLINE +Sint erts_flxctr_inc_read_centralized(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief This function can only be used together with a ErtsFlxCtr + * that is configured to be centralized. The function decrements the + * specified counter by 1 and returns the value of the counter after + * the operation. + */ +ERTS_GLB_INLINE +Sint erts_flxctr_dec_read_centralized(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief This function can only be used together with an ErtsFlxCtr + * that is configured to be centralized. The function returns the + * current value of the specified counter. + */ +ERTS_GLB_INLINE +Sint erts_flxctr_read_centralized(ErtsFlxCtr* c, + Uint counter_nr); + + +typedef enum { + ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP, + ERTS_FLXCTR_DONE, + ERTS_FLXCTR_GET_RESULT_AFTER_TRAP +} ErtsFlxctrSnapshotResultType; + +typedef struct { + ErtsFlxctrSnapshotResultType type; + Eterm trap_resume_state; + Sint result[ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE]; +} ErtsFlxCtrSnapshotResult; + +/** + * @brief This function initiates an atomic snapshot of an ErtsFlxCtr + * to read out the values of one or more of the counters that are + * stored in the given ErtsFlxCtr. The caller needs to perform + * different actions after the return of this function depending on + * the value of the type field in the returned struct: + * + * - The caller needs to trap and try again after the trap if the + * return value has the type ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP. + * + * - The caller can get the result directly from the result field of + * the returned struct if the return value has the type + * ERTS_FLXCTR_DONE. The value at index i in the result field + * correspond to counter number i. + * + * - Finally, if the return value has the type + * ERTS_FLXCTR_GET_RESULT_AFTER_TRAP, then the caller needs to save + * the value of the field trap_resume_state from the returned struct + * and trap. After the trap, the values of the counters can be + * obtained by using the function + * erts_flxctr_get_snapshot_result_after_trap. Note that the + * function erts_flxctr_is_snapshot_result can be used to check if a + * value is obtained from the trap_resume_state field in the + * returned struct (this can be useful when the calling function + * wakes up again after the trap). + * + * The snapshot operation that is initiated by this function should be + * considered to be ongoing from the issuing of this function until a + * struct with the type field set to ERTS_FLXCTR_DONE has been + * returned from the function or until the caller of this function has + * woken up after trapping. + * + * @param c The ErtsFlxCtr that the snapshot shall be taken from + * @param alloc_type The allocation type (needs to be the same as the + * type passed to erts_flxctr_init when c was + * initialized) + * @param p The Erlang process that is doing the call + * + * @return See the description above + * + */ +ErtsFlxCtrSnapshotResult +erts_flxctr_snapshot(ErtsFlxCtr* c, + ErtsAlcType_t alloc_type, + Process* p); + +/** + * @brief Checks if the parameter term is a snapshot result (i.e., + * something obtained from the trap_resume_state field of an + * ErtsFlxCtrSnapshotResult struct that has been returned from + * erts_flxctr_snapshot). + * + * @param term The term to check + * + * @return A nonzero value iff the term is a snapshot result + */ +int erts_flxctr_is_snapshot_result(Eterm term); + +/** + * @brief Returns the result of a snapshot for a counter given a + * snapshot result returned by a call to erts_flxctr_snapshot (i.e., + * the value stored in the trap_resume_state field of a struct + * returned by erts_flxctr_snapshot). The caller needs to trap between + * the return of erts_flxctr_snapshot and the call to this function. + */ +Sint erts_flxctr_get_snapshot_result_after_trap(Eterm trap_resume_state, + Uint counter_nr); + +/** + * @brief Resets the specified counter to 0. This function is unsafe + * to call while a snapshot operation may be active (initiated with + * the erts_flxctr_snapshot function). + */ +void erts_flxctr_reset(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief Checks if a snapshot operation is active (snapshots are + * initiated with the erts_flxctr_snapshot function). + * + * @return nonzero value iff a snapshot was active at some point + * between the invocation and return of the function + */ +int erts_flxctr_is_snapshot_ongoing(ErtsFlxCtr* c); + +/** + * @brief This function checks if a snapshot operation is ongoing + * (snapshots are initiated with the erts_flxctr_snapshot function) + * and suspend the given process until thread progress has happened if + * it detected an ongoing snapshot operation. The caller needs to trap + * if a non-zero value is returned. + * + * @param c The ErtsFlxCtr to check + * @param p The calling process + * + * @return nonzero value if the given process has got suspended + */ +int erts_flxctr_suspend_until_thr_prg_if_snapshot_ongoing(ErtsFlxCtr* c, Process* p); + +/* End: Public Interface */ + +/* Internal Declarations */ + +#define ERTS_FLXCTR_GET_CTR_ARRAY_PTR(C) \ + ((ErtsFlxCtrDecentralizedCtrArray*) erts_atomic_read_acqb(&(C)->u.counters_ptr)) +#define ERTS_FLXCTR_GET_CTR_PTR(C, SCHEDULER_ID, COUNTER_ID) \ + &(ERTS_FLXCTR_GET_CTR_ARRAY_PTR(C))->array[SCHEDULER_ID].counters[COUNTER_ID] + + +typedef union { + erts_atomic_t counters[ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE]; + char pad[ERTS_CACHE_LINE_SIZE]; +} ErtsFlxCtrDecentralizedCtrArrayElem; + +typedef struct ErtsFlxCtrDecentralizedCtrArray { + void* block_start; + erts_atomic_t snapshot_status; + ErtsFlxCtrDecentralizedCtrArrayElem array[]; +} ErtsFlxCtrDecentralizedCtrArray; + +void erts_flxctr_set_slot(int group); + +ERTS_GLB_INLINE +int erts_flxctr_get_slot_index(void); + +/* End: Internal Declarations */ + + +/* Implementation of inlined functions */ + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE +int erts_flxctr_get_slot_index(void) +{ + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + ASSERT(esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp)); + ASSERT(esdp->flxctr_slot_no > 0); + return esdp->flxctr_slot_no; +} + +ERTS_GLB_INLINE +void erts_flxctr_add(ErtsFlxCtr* c, + Uint counter_nr, + int to_add) +{ + ASSERT(counter_nr < c->nr_of_counters); + if (c->is_decentralized) { + erts_atomic_add_nob(ERTS_FLXCTR_GET_CTR_PTR(c, + erts_flxctr_get_slot_index(), + counter_nr), + to_add); + } else { + erts_atomic_add_nob(&c->u.counters[counter_nr], to_add); + } +} + +ERTS_GLB_INLINE +void erts_flxctr_inc(ErtsFlxCtr* c, + Uint counter_nr) +{ + ASSERT(counter_nr < c->nr_of_counters); + if (c->is_decentralized) { + erts_atomic_inc_nob(ERTS_FLXCTR_GET_CTR_PTR(c, + erts_flxctr_get_slot_index(), + counter_nr)); + } else { + erts_atomic_inc_read_nob(&c->u.counters[counter_nr]); + } +} + +ERTS_GLB_INLINE +void erts_flxctr_dec(ErtsFlxCtr* c, + Uint counter_nr) +{ + ASSERT(counter_nr < c->nr_of_counters); + if (c->is_decentralized) { + erts_atomic_dec_nob(ERTS_FLXCTR_GET_CTR_PTR(c, + erts_flxctr_get_slot_index(), + counter_nr)); + } else { + erts_atomic_dec_nob(&c->u.counters[counter_nr]); + } +} + +ERTS_GLB_INLINE +Sint erts_flxctr_inc_read_centralized(ErtsFlxCtr* c, + Uint counter_nr) +{ + ASSERT(counter_nr < c->nr_of_counters); + ASSERT(!c->is_decentralized); + return erts_atomic_inc_read_nob(&c->u.counters[counter_nr]); +} + +ERTS_GLB_INLINE +Sint erts_flxctr_dec_read_centralized(ErtsFlxCtr* c, + Uint counter_nr) +{ + ASSERT(counter_nr < c->nr_of_counters); + ASSERT(!c->is_decentralized); + return erts_atomic_dec_read_nob(&c->u.counters[counter_nr]); +} + +ERTS_GLB_INLINE +Sint erts_flxctr_read_centralized(ErtsFlxCtr* c, + Uint counter_nr) +{ + ASSERT(counter_nr < c->nr_of_counters); + ASSERT(!c->is_decentralized); + return erts_atomic_read_nob(&((erts_atomic_t*)(c->u.counters))[counter_nr]); +} + +#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +#endif /* ERL_FLXCTR_H__ */ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 12750b9aa6..547e4064a2 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -593,6 +593,7 @@ void erts_usage(void) erts_fprintf(stderr, " no_time_warp|single_time_warp|multi_time_warp\n"); erts_fprintf(stderr, "-d don't write a crash dump for internally detected errors\n"); erts_fprintf(stderr, " (halt(String) will still produce a crash dump)\n"); + erts_fprintf(stderr, "-dcg set the limit for the number of decentralized counter groups\n"); erts_fprintf(stderr, "-fn[u|a|l] Control how filenames are interpreted\n"); erts_fprintf(stderr, "-hms size set minimum heap size in words (default %d)\n", H_DEFAULT_SIZE); @@ -785,6 +786,8 @@ early_init(int *argc, char **argv) /* int dirty_io_scheds; int max_reader_groups; int reader_groups; + int max_decentralized_counter_groups; + int decentralized_counter_groups; char envbuf[21]; /* enough for any 64-bit integer */ size_t envbufsz; @@ -804,7 +807,8 @@ early_init(int *argc, char **argv) /* erts_initialized = 0; - erts_pre_early_init_cpu_topology(&max_reader_groups, + erts_pre_early_init_cpu_topology(&max_decentralized_counter_groups, + &max_reader_groups, &ncpu, &ncpuonln, &ncpuavail); @@ -865,6 +869,24 @@ early_init(int *argc, char **argv) /* } if (argv[i][0] == '-') { switch (argv[i][1]) { + case 'd': { + char *sub_param = argv[i]+2; + if (has_prefix("cg", sub_param)) { + char *arg = get_arg(sub_param+2, argv[i+1], &i); + if (sscanf(arg, "%d", &max_decentralized_counter_groups) != 1) { + erts_fprintf(stderr, + "bad decentralized counter groups limit: %s\n", arg); + erts_usage(); + } + if (max_decentralized_counter_groups < 0) { + erts_fprintf(stderr, + "bad decentralized counter groups limit: %d\n", + max_decentralized_counter_groups); + erts_usage(); + } + } + break; + } case 'r': { char *sub_param = argv[i]+2; if (has_prefix("g", sub_param)) { @@ -1186,8 +1208,10 @@ early_init(int *argc, char **argv) /* erts_early_init_cpu_topology(no_schedulers, &max_main_threads, max_reader_groups, - &reader_groups); - + &reader_groups, + max_decentralized_counter_groups, + &decentralized_counter_groups); + erts_flxctr_setup(decentralized_counter_groups); { erts_thr_late_init_data_t elid = ERTS_THR_LATE_INIT_DATA_DEF_INITER; elid.mem.std.alloc = ethr_std_alloc; diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 49dea8919b..4eb6c3e214 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -805,8 +805,7 @@ static int node_table_cmp(void *venp1, void *venp2) { return ((((ErlNode *) venp1)->sysname == ((ErlNode *) venp2)->sysname) && - ((((ErlNode *) venp1)->creation == ((ErlNode *) venp2)->creation) || - (((ErlNode *) venp1)->creation == 0 || ((ErlNode *) venp2)->creation == 0)) + ((((ErlNode *) venp1)->creation == ((ErlNode *) venp2)->creation)) ? 0 : 1); } @@ -977,7 +976,7 @@ static void print_node(void *venp, void *vpndp) if(pndp->sysname == NIL) { erts_print(pndp->to, pndp->to_arg, "Name: %T ", enp->sysname); } - erts_print(pndp->to, pndp->to_arg, " %u", enp->creation); + erts_print(pndp->to, pndp->to_arg, " %d", enp->creation); #ifdef DEBUG erts_print(pndp->to, pndp->to_arg, " (refc=%ld)", erts_refc_read(&enp->refc, 0)); @@ -1020,7 +1019,7 @@ void erts_print_node_info(fmtfn_t to, /* ----------------------------------------------------------------------- */ void -erts_set_this_node(Eterm sysname, Uint32 creation) +erts_set_this_node(Eterm sysname, Uint creation) { ERTS_LC_ASSERT(erts_thr_progress_is_blocking()); ASSERT(2 <= de_refc_read(erts_this_dist_entry, 2)); diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index c434926142..aa8af12555 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; @@ -258,7 +259,7 @@ void erts_set_dist_entry_pending(DistEntry *); void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint); ErlNode *erts_find_or_insert_node(Eterm, Uint32, Eterm); void erts_schedule_delete_node(ErlNode *); -void erts_set_this_node(Eterm, Uint32); +void erts_set_this_node(Eterm, Uint); Uint erts_node_table_size(void); void erts_init_node_tables(int); void erts_node_table_info(fmtfn_t, void *); 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/erl_process.h b/erts/emulator/beam/erl_process.h index 711b73417d..6118c671ee 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -641,6 +641,7 @@ struct ErtsSchedulerData_ { ErtsSchedType type; Uint no; /* Scheduler number for normal schedulers */ Uint dirty_no; /* Scheduler number for dirty schedulers */ + int flxctr_slot_no; /* slot nr when a flxctr is used */ struct enif_environment_t *current_nif; Process *dirty_shadow_process; Port *current_port; @@ -1847,6 +1848,7 @@ int erts_resume_processes(ErtsProcList *); void erts_deep_process_dump(fmtfn_t, void *); Eterm erts_get_reader_groups_map(Process *c_p); +Eterm erts_get_decentralized_counter_groups_map(Process *c_p); Eterm erts_debug_reader_groups_map(Process *c_p, int groups); Uint erts_debug_nbalance(void); diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 471c1c3938..395ff51ad3 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -51,17 +51,18 @@ #define MAX_STRING_LEN 0xffff -/* - * MAX value for the creation field in pid, port and reference - * for the old PID_EXT, PORT_EXT, REFERENCE_EXT and NEW_REFERENCE_EXT. - * Older nodes (OTP 19-21) will send us these so we must be able to decode them. - * - * From OTP 22 DFLAG_BIG_CREATION is mandatory so this node will always - * encode with new big 32-bit creations using NEW_PID_EXT, NEW_PORT_EXT - * and NEWER_REFERENCE_EXT. +/* MAX value for the creation field in pid, port and reference + for the local node and for the current external format. + + Larger creation values than this are allowed in external pid, port and refs + encoded with NEW_PID_EXT, NEW_PORT_EXT and NEWER_REFERENCE_EXT. + The point here is to prepare for future upgrade to 32-bit creation. + OTP-19 (erts-8.0) can handle big creation values from other (newer) nodes, + but do not use big creation values for the local node yet, + as we still may have to communicate with older nodes. */ -#define ERTS_MAX_TINY_CREATION (3) -#define is_tiny_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_TINY_CREATION) +#define ERTS_MAX_LOCAL_CREATION (3) +#define is_valid_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_LOCAL_CREATION) #undef ERTS_DEBUG_USE_DIST_SEP #ifdef DEBUG @@ -699,6 +700,7 @@ dist_ext_size(ErtsDistExternal *edep) } else { sz -= sizeof(ErtsAtomTranslationTable); } + ASSERT(sz % 4 == 0); return sz; } @@ -706,8 +708,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 +752,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); @@ -2379,8 +2384,7 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags) Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS)) ? INTERNAL_LOCAL_SYSNAME : pid_node_name(pid)); Uint32 creation = pid_creation(pid); - - *ep++ = NEW_PID_EXT; + byte* tagp = ep++; /* insert atom here containing host and sysname */ ep = enc_atom(acmp, sysname, ep, dflags); @@ -2392,8 +2396,15 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags) ep += 4; put_int32(os, ep); ep += 4; - put_int32(creation, ep); - ep += 4; + if (creation <= ERTS_MAX_LOCAL_CREATION) { + *tagp = PID_EXT; + *ep++ = creation; + } else { + ASSERT(is_external_pid(pid)); + *tagp = NEW_PID_EXT; + put_int32(creation, ep); + ep += 4; + } return ep; } @@ -2513,7 +2524,7 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep, if (tag == PID_EXT) { cre = get_int8(ep); ep += 1; - if (!is_tiny_creation(cre)) { + if (!is_valid_creation(cre)) { return NULL; } } else { @@ -2774,18 +2785,25 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj)) ? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj)); Uint32 creation = ref_creation(obj); + byte* tagp = ep++; ASSERT(dflags & DFLAG_EXTENDED_REFERENCES); erts_magic_ref_save_bin(obj); - *ep++ = NEWER_REFERENCE_EXT; i = ref_no_numbers(obj); put_int16(i, ep); ep += 2; ep = enc_atom(acmp, sysname, ep, dflags); - put_int32(creation, ep); - ep += 4; + if (creation <= ERTS_MAX_LOCAL_CREATION) { + *tagp = NEW_REFERENCE_EXT; + *ep++ = creation; + } else { + ASSERT(is_external_ref(obj)); + *tagp = NEWER_REFERENCE_EXT; + put_int32(creation, ep); + ep += 4; + } ref_num = ref_numbers(obj); for (j = 0; j < i; j++) { put_int32(ref_num[j], ep); @@ -2798,14 +2816,21 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj)) ? INTERNAL_LOCAL_SYSNAME : port_node_name(obj)); Uint32 creation = port_creation(obj); + byte* tagp = ep++; - *ep++ = NEW_PORT_EXT; ep = enc_atom(acmp, sysname, ep, dflags); j = port_number(obj); put_int32(j, ep); ep += 4; - put_int32(creation, ep); - ep += 4; + if (creation <= ERTS_MAX_LOCAL_CREATION) { + *tagp = PORT_EXT; + *ep++ = creation; + } else { + ASSERT(is_external_port(obj)); + *tagp = NEW_PORT_EXT; + put_int32(creation, ep); + ep += 4; + } break; } case LIST_DEF: @@ -3500,7 +3525,7 @@ dec_term_atom_common: if (tag == PORT_EXT) { cre = get_int8(ep); ep++; - if (!is_tiny_creation(cre)) { + if (!is_valid_creation(cre)) { goto error; } } @@ -3547,7 +3572,7 @@ dec_term_atom_common: cre = get_int8(ep); ep += 1; - if (!is_tiny_creation(cre)) { + if (!is_valid_creation(cre)) { goto error; } goto ref_ext_common; @@ -3561,7 +3586,7 @@ dec_term_atom_common: cre = get_int8(ep); ep += 1; - if (!is_tiny_creation(cre)) { + if (!is_valid_creation(cre)) { goto error; } r0 = get_int32(ep); @@ -4259,21 +4284,30 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, result += 1 + 4 + 1 + i; /* tag,size,sign,digits */ break; case EXTERNAL_PID_DEF: + if (external_pid_creation(obj) > ERTS_MAX_LOCAL_CREATION) + result += 3; + /*fall through*/ case PID_DEF: result += (1 + encode_size_struct2(acmp, pid_node_name(obj), dflags) + - 4 + 4 + 4); + 4 + 4 + 1); break; case EXTERNAL_REF_DEF: + if (external_ref_creation(obj) > ERTS_MAX_LOCAL_CREATION) + result += 3; + /*fall through*/ case REF_DEF: ASSERT(dflags & DFLAG_EXTENDED_REFERENCES); i = ref_no_numbers(obj); result += (1 + 2 + encode_size_struct2(acmp, ref_node_name(obj), dflags) + - 4 + 4*i); + 1 + 4*i); break; case EXTERNAL_PORT_DEF: + if (external_port_creation(obj) > ERTS_MAX_LOCAL_CREATION) + result += 3; + /*fall through*/ case PORT_DEF: result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags) + - 4 + 4); + 4 + 1); break; case LIST_DEF: { int is_str = is_external_string(obj, &m); 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/epmd/epmd.mk b/erts/epmd/epmd.mk index f6889a7ff1..b1fd04dc04 100644 --- a/erts/epmd/epmd.mk +++ b/erts/epmd/epmd.mk @@ -67,5 +67,5 @@ EPMD_NODE_TYPE=110 # Distribution format 5 contains the new md5 based handshake. EPMD_DIST_LOW=5 -EPMD_DIST_HIGH=6 +EPMD_DIST_HIGH=5 diff --git a/erts/epmd/src/epmd.h b/erts/epmd/src/epmd.h index 7332294d3d..cffcd4ae7a 100644 --- a/erts/epmd/src/epmd.h +++ b/erts/epmd/src/epmd.h @@ -26,7 +26,6 @@ #define EPMD_ALIVE2_REQ 'x' #define EPMD_PORT2_REQ 'z' #define EPMD_ALIVE2_RESP 'y' -#define EPMD_ALIVE2_X_RESP 'v' #define EPMD_PORT2_RESP 'w' #define EPMD_NAMES_REQ 'n' diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index a5156a142e..ed9bbdb8cd 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -277,12 +277,6 @@ static const struct in6_addr in6addr_loopback = #define put_int16(i, s) {((unsigned char*)(s))[0] = ((i) >> 8) & 0xff; \ ((unsigned char*)(s))[1] = (i) & 0xff;} -#define put_int32(i, s) do {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \ - ((char*)(s))[1] = (char)((i) >> 16) & 0xff; \ - ((char*)(s))[2] = (char)((i) >> 8) & 0xff; \ - ((char*)(s))[3] = (char)(i) & 0xff;} \ - while (0) - #if defined(__GNUC__) # define EPMD_INLINE __inline__ #elif defined(__WIN32__) @@ -313,10 +307,10 @@ struct enode { int fd; /* The socket in use */ unsigned short port; /* Port number of Erlang node */ char symname[MAXSYMLEN+1]; /* Name of the Erlang node */ - unsigned int cr_counter; /* Used to generate 'creation' numbers */ + short creation; /* Started as a random number 1..3 */ char nodetype; /* 77 = normal erlang node 72 = hidden (c-node */ char protocol; /* 0 = tcp/ipv4 */ - unsigned short highvsn; /* 5: creation=1..3, 6: creation=1..(2^32-1)*/ + unsigned short highvsn; /* 0 = OTP-R3 erts-4.6.x, 1 = OTP-R4 erts-4.7.x*/ unsigned short lowvsn; int extralen; char extra[MAXSYMLEN+1]; diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 633ec71e5f..3c6f1fbdf4 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -665,21 +665,6 @@ static int do_accept(EpmdVars *g,int listensock) return conn_open(g,msgsock); } -static void bump_creation(Node* node) -{ - if (++node->cr_counter == 0) - node->cr_counter = 1; -} -static unsigned int get_creation(Node* node) -{ - if (node->highvsn >= 6) { - return node->cr_counter; /* 1..(2^32-1)*/ - } - else { - return (node->cr_counter - 1) % 3 + 1; /* 1..3 */ - } -} - /* buf is actually one byte larger than bsize, giving place for null termination */ static void do_request(g, fd, s, buf, bsize) @@ -721,10 +706,8 @@ static void do_request(g, fd, s, buf, bsize) unsigned char protocol; unsigned short highvsn; unsigned short lowvsn; - unsigned int creation; int namelen; int extralen; - int replylen; char *name; char *extra; eport = get_int16(&buf[1]); @@ -754,22 +737,17 @@ static void do_request(g, fd, s, buf, bsize) extra = &buf[11+namelen+2]; extra[extralen]='\000'; - node = node_reg2(g, namelen, name, fd, eport, nodetype, protocol, - highvsn, lowvsn, extralen, extra); - creation = node ? get_creation(node) : 99; - wbuf[1] = node ? 0 : 1; /* ok | error */ - if (highvsn >= 6) { - wbuf[0] = EPMD_ALIVE2_X_RESP; - put_int32(creation, wbuf+2); - replylen = 6; - } - else { - wbuf[0] = EPMD_ALIVE2_RESP; - put_int16(creation, wbuf+2); - replylen = 4; - } + wbuf[0] = EPMD_ALIVE2_RESP; + if ((node = node_reg2(g, namelen, name, fd, eport, nodetype, protocol, + highvsn, lowvsn, extralen, extra)) == NULL) { + wbuf[1] = 1; /* error */ + put_int16(99, wbuf+2); + } else { + wbuf[1] = 0; /* ok */ + put_int16(node->creation, wbuf+2); + } - if (!reply(g, fd, wbuf, replylen)) + if (!reply(g, fd, wbuf, 4)) { node_unreg(g, name); dbg_tty_printf(g,1,"** failed to send ALIVE2_RESP for \"%s\"", @@ -1222,8 +1200,8 @@ static int node_unreg(EpmdVars *g,char *name) for (; node; prev = &node->next, node = node->next) if (is_same_str(node->symname, name)) { - dbg_tty_printf(g,1,"unregistering '%s:%u', port %d", - node->symname, get_creation(node), node->port); + dbg_tty_printf(g,1,"unregistering '%s:%d', port %d", + node->symname, node->creation, node->port); *prev = node->next; /* Link out from "reg" list */ @@ -1257,8 +1235,8 @@ static int node_unreg_sock(EpmdVars *g,int fd) for (; node; prev = &node->next, node = node->next) if (node->fd == fd) { - dbg_tty_printf(g,1,"unregistering '%s:%u', port %d", - node->symname, get_creation(node), node->port); + dbg_tty_printf(g,1,"unregistering '%s:%d', port %d", + node->symname, node->creation, node->port); *prev = node->next; /* Link out from "reg" list */ @@ -1286,8 +1264,19 @@ static int node_unreg_sock(EpmdVars *g,int fd) } /* - * Register a new node + * Finding a node slot and a (name,creation) name is a bit tricky. + * We try in order + * + * 1. If the name was used before and we can reuse that slot but use + * a new "creation" digit in the range 1..3. + * + * 2. We try to find a new unused slot. + * + * 3. We try to use an used slot this isn't used any longer. + * FIXME: The criteria for *what* slot to steal should be improved. + * Perhaps use the oldest or something. */ + static Node *node_reg2(EpmdVars *g, int namelen, char* name, @@ -1357,7 +1346,7 @@ static Node *node_reg2(EpmdVars *g, } /* Try to find the name in the used queue so that we - can change "creation" number */ + can change "creation" number 1..3 */ prev = NULL; @@ -1386,8 +1375,9 @@ static Node *node_reg2(EpmdVars *g, g->nodes.unreg_count--; - /* When reusing we change the "creation" number */ - bump_creation(node); + /* When reusing we change the "creation" number 1..3 */ + + node->creation = node->creation % 3 + 1; break; } @@ -1414,8 +1404,7 @@ static Node *node_reg2(EpmdVars *g, exit(1); } - node->cr_counter = current_time(g); /* "random" */ - bump_creation(node); + node->creation = (current_time(g) % 3) + 1; /* "random" 1-3 */ } } @@ -1434,11 +1423,11 @@ static Node *node_reg2(EpmdVars *g, select_fd_set(g, fd); if (highvsn == 0) { - dbg_tty_printf(g,1,"registering '%s:%u', port %d", - node->symname, get_creation(node), node->port); + dbg_tty_printf(g,1,"registering '%s:%d', port %d", + node->symname, node->creation, node->port); } else { - dbg_tty_printf(g,1,"registering '%s:%u', port %d", - node->symname, get_creation(node), node->port); + dbg_tty_printf(g,1,"registering '%s:%d', port %d", + node->symname, node->creation, node->port); dbg_tty_printf(g,1,"type %d proto %d highvsn %d lowvsn %d", nodetype, protocol, highvsn, lowvsn); } @@ -1572,8 +1561,8 @@ static void print_names(EpmdVars *g) for (node = g->nodes.reg; node; node = node->next) { - fprintf(stderr,"***** active name \"%s#%u\" at port %d, fd = %d\r\n", - node->symname, get_creation(node), node->port, node->fd); + fprintf(stderr,"***** active name \"%s#%d\" at port %d, fd = %d\r\n", + node->symname, node->creation, node->port, node->fd); count ++; } @@ -1583,8 +1572,8 @@ static void print_names(EpmdVars *g) for (node = g->nodes.unreg; node; node = node->next) { - fprintf(stderr,"***** old/unused name \"%s#%u\"\r\n", - node->symname, get_creation(node)); + fprintf(stderr,"***** old/unused name \"%s#%d\"\r\n", + node->symname, node->creation); count ++; } 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/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index 0a5987df88..463d890688 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -324,9 +324,6 @@ run_case(Config, TestArgs, Fun) -> -define(PORT_EXT, 102). -define(PID_EXT, 103). -define(NEW_REFERENCE_EXT, 114). --define(NEW_PID_EXT, $X). --define(NEW_PORT_EXT, $Y). --define(NEWER_REFERENCE_EXT, $Z). uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 -> [(Uint bsr 24) band 16#ff, @@ -354,13 +351,13 @@ mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> mk_pid({atom_to_list(NodeName), Creation}, Number, Serial); mk_pid({NodeName, Creation}, Number, Serial) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?NEW_PID_EXT, + ?PID_EXT, ?ATOM_EXT, uint16_be(length(NodeName)), NodeName, uint32_be(Number), uint32_be(Serial), - uint32_be(Creation)])) of + uint8(Creation)])) of Pid when is_pid(Pid) -> Pid; {'EXIT', {badarg, _}} -> @@ -373,12 +370,12 @@ mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> mk_port({atom_to_list(NodeName), Creation}, Number); mk_port({NodeName, Creation}, Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?NEW_PORT_EXT, + ?PORT_EXT, ?ATOM_EXT, uint16_be(length(NodeName)), NodeName, uint32_be(Number), - uint32_be(Creation)])) of + uint8(Creation)])) of Port when is_port(Port) -> Port; {'EXIT', {badarg, _}} -> @@ -391,16 +388,33 @@ mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName), is_integer(Creation), is_list(Numbers) -> mk_ref({atom_to_list(NodeName), Creation}, Numbers); +mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName), + is_integer(Creation), + is_integer(Number) -> + case catch binary_to_term(list_to_binary([?VERSION_MAGIC, + ?REFERENCE_EXT, + ?ATOM_EXT, + uint16_be(length(NodeName)), + NodeName, + uint32_be(Number), + uint8(Creation)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) + end; mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName), is_integer(Creation), is_list(Numbers) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?NEWER_REFERENCE_EXT, + ?NEW_REFERENCE_EXT, uint16_be(length(Numbers)), ?ATOM_EXT, uint16_be(length(NodeName)), NodeName, - uint32_be(Creation), + uint8(Creation), lists:map(fun (N) -> uint32_be(N) end, @@ -415,10 +429,11 @@ mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName), my_cre() -> erlang:system_info(creation). -oth_cre(N) when N >= 0, N < (1 bsl 32) -> - (N rem ((1 bsl 32) - 1)) + 1; -oth_cre(N) -> - exit({invalid_creation, N}). +oth_cre(0) -> 1; +oth_cre(1) -> 2; +oth_cre(2) -> 3; +oth_cre(3) -> 1; +oth_cre(N) -> exit({invalid_creation, N}). str_1_bsl_10000() -> "19950631168807583848837421626835850838234968318861924548520089498529438830221946631919961684036194597899331129423209124271556491349413781117593785932096323957855730046793794526765246551266059895520550086918193311542508608460618104685509074866089624888090489894838009253941633257850621568309473902556912388065225096643874441046759871626985453222868538161694315775629640762836880760732228535091641476183956381458969463899410840960536267821064621427333394036525565649530603142680234969400335934316651459297773279665775606172582031407994198179607378245683762280037302885487251900834464581454650557929601414833921615734588139257095379769119277800826957735674444123062018757836325502728323789270710373802866393031428133241401624195671690574061419654342324638801248856147305207431992259611796250130992860241708340807605932320161268492288496255841312844061536738951487114256315111089745514203313820202931640957596464756010405845841566072044962867016515061920631004186422275908670900574606417856951911456055068251250406007519842261898059237118054444788072906395242548339221982707404473162376760846613033778706039803413197133493654622700563169937455508241780972810983291314403571877524768509857276937926433221599399876886660808368837838027643282775172273657572744784112294389733810861607423253291974813120197604178281965697475898164531258434135959862784130128185406283476649088690521047580882615823961985770122407044330583075869039319604603404973156583208672105913300903752823415539745394397715257455290510212310947321610753474825740775273986348298498340756937955646638621874569499279016572103701364433135817214311791398222983845847334440270964182851005072927748364550578634501100852987812389473928699540834346158807043959118985815145779177143619698728131459483783202081474982171858011389071228250905826817436220577475921417653715687725614904582904992461028630081535583308130101987675856234343538955409175623400844887526162643568648833519463720377293240094456246923254350400678027273837755376406726898636241037491410966718557050759098100246789880178271925953381282421954028302759408448955014676668389697996886241636313376393903373455801407636741877711055384225739499110186468219696581651485130494222369947714763069155468217682876200362777257723781365331611196811280792669481887201298643660768551639860534602297871557517947385246369446923087894265948217008051120322365496288169035739121368338393591756418733850510970271613915439590991598154654417336311656936031122249937969999226781732358023111862644575299135758175008199839236284615249881088960232244362173771618086357015468484058622329792853875623486556440536962622018963571028812361567512543338303270029097668650568557157505516727518899194129711337690149916181315171544007728650573189557450920330185304847113818315407324053319038462084036421763703911550639789000742853672196280903477974533320468368795868580237952218629120080742819551317948157624448298518461509704888027274721574688131594750409732115080498190455803416826949787141316063210686391511681774304792596709376". diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index 56f6f7bcc4..2695e597cf 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -1297,7 +1297,7 @@ </taglist> <p>For example, if a test is started with:</p> - <p><c>$ ct_run -suite my_SUITE -logopts no_src</c></p> + <p><c>$ ct_run -suite my_SUITE -logopts no_nl</c></p> <p>then printouts during the test made by successive calls to <c>io:format("x")</c>, appears in the test case log as:</p> <p><c>xxx</c></p> diff --git a/lib/common_test/src/test_server_node.erl b/lib/common_test/src/test_server_node.erl index 3ae4a047d8..c11b9071cf 100644 --- a/lib/common_test/src/test_server_node.erl +++ b/lib/common_test/src/test_server_node.erl @@ -598,11 +598,20 @@ pick_erl_program(L) -> {prog, S} -> S; {release, S} -> + clear_erl_aflags(), find_release(S); this -> ct:get_progname() end. +clear_erl_aflags() -> + %% When starting a node with a previous release, options in + %% ERL_AFLAGS could prevent the node from starting. For example, + %% if ERL_AFLAGS is set to "-emu_type lcnt", the node will only + %% start if the previous release happens to also have a lock + %% counter emulator installed (unlikely). + os:unsetenv("ERL_AFLAGS"). + %% This is an attempt to distinguish between spaces in the program %% path and spaces that separate arguments. The program is quoted to %% allow spaces in the path. 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/compiler/src/Makefile b/lib/compiler/src/Makefile index 9f8d63baa1..87b0d345f2 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -194,13 +194,16 @@ $(EBIN)/beam_disasm.beam: $(EGEN)/beam_opcodes.hrl beam_disasm.hrl $(EBIN)/beam_listing.beam: core_parse.hrl v3_kernel.hrl beam_ssa.hrl $(EBIN)/beam_kernel_to_ssa.beam: v3_kernel.hrl beam_ssa.hrl $(EBIN)/beam_ssa.beam: beam_ssa.hrl +$(EBIN)/beam_ssa_bsm.beam: beam_ssa.hrl $(EBIN)/beam_ssa_codegen.beam: beam_ssa.hrl $(EBIN)/beam_ssa_dead.beam: beam_ssa.hrl +$(EBIN)/beam_ssa_funs.beam: beam_ssa.hrl $(EBIN)/beam_ssa_lint.beam: beam_ssa.hrl $(EBIN)/beam_ssa_opt.beam: beam_ssa.hrl $(EBIN)/beam_ssa_pp.beam: beam_ssa.hrl $(EBIN)/beam_ssa_pre_codegen.beam: beam_ssa.hrl $(EBIN)/beam_ssa_recv.beam: beam_ssa.hrl +$(EBIN)/beam_ssa_share.beam: beam_ssa.hrl $(EBIN)/beam_ssa_type.beam: beam_ssa.hrl $(EBIN)/cerl.beam: core_parse.hrl $(EBIN)/compile.beam: core_parse.hrl ../../stdlib/include/erl_compile.hrl diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 1d45ed9df2..20707c0531 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -80,8 +80,12 @@ void init_algorithms_types(ErlNifEnv* env) algo_pubkey_cnt = 0; algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "rsa"); +#ifdef HAVE_DSA algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dss"); +#endif +#ifdef HAVE_DH algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dh"); +#endif #if defined(HAVE_EC) #if !defined(OPENSSL_NO_EC2M) algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ec_gf2m"); diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c index 107723d2cb..3408ba1b88 100644 --- a/lib/crypto/c_src/api_ng.c +++ b/lib/crypto/c_src/api_ng.c @@ -522,6 +522,11 @@ ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg const struct cipher_type_t *cipherp; ERL_NIF_TERM ret; + ctx_res.ctx = NULL; +#if !defined(HAVE_EVP_AES_CTR) + ctx_res.env = NULL; +#endif + if (!get_init_args(env, &ctx_res, argv[0], argv[1], argv[2], argv[4], &cipherp, &ret)) goto ret; @@ -530,9 +535,16 @@ ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg ret: if (ctx_res.ctx) EVP_CIPHER_CTX_free(ctx_res.ctx); + +#if !defined(HAVE_EVP_AES_CTR) + if (ctx_res.env) + enif_free_env(ctx_res.env); +#endif + return ret; } + ERL_NIF_TERM ng_crypto_one_time_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Cipher, Key, IVec, Data, Encrypt) % if no IV for the Cipher, set IVec = <<>> */ diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c index 13de3562e8..8f0c93c5db 100644 --- a/lib/crypto/c_src/cipher.c +++ b/lib/crypto/c_src/cipher.c @@ -20,10 +20,10 @@ #include "cipher.h" -#ifdef OPENSSL_NO_DES -#define COND_NO_DES_PTR(Ptr) (NULL) -#else +#ifdef HAVE_DES #define COND_NO_DES_PTR(Ptr) (Ptr) +#else +#define COND_NO_DES_PTR(Ptr) (NULL) #endif static struct cipher_type_t cipher_types[] = @@ -50,10 +50,17 @@ static struct cipher_type_t cipher_types[] = {{"des_ede3_cfb"}, {NULL}, 0, 0}, #endif +#ifdef HAVE_BF {{"blowfish_cbc"}, {&EVP_bf_cbc}, 0, NO_FIPS_CIPHER}, {{"blowfish_cfb64"}, {&EVP_bf_cfb64}, 0, NO_FIPS_CIPHER}, {{"blowfish_ofb64"}, {&EVP_bf_ofb}, 0, NO_FIPS_CIPHER}, {{"blowfish_ecb"}, {&EVP_bf_ecb}, 0, NO_FIPS_CIPHER | ECB_BUG_0_9_8L}, +#else + {{"blowfish_cbc"}, {NULL}, 0, 0}, + {{"blowfish_cfb64"}, {NULL}, 0, 0}, + {{"blowfish_ofb64"}, {NULL}, 0, 0}, + {{"blowfish_ecb"}, {NULL}, 0, 0}, +#endif {{"aes_cbc"}, {&EVP_aes_128_cbc}, 16, 0}, {{"aes_cbc"}, {&EVP_aes_192_cbc}, 24, 0}, diff --git a/lib/crypto/c_src/dh.c b/lib/crypto/c_src/dh.c index 38eb534d99..13a2336f25 100644 --- a/lib/crypto/c_src/dh.c +++ b/lib/crypto/c_src/dh.c @@ -23,6 +23,7 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */ +#ifdef HAVE_DH DH *dh_params = NULL; unsigned int mpint; /* 0 or 4 */ ERL_NIF_TERM head, tail; @@ -187,10 +188,14 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar #endif return ret; +#else + return enif_raise_exception(env, atom_notsup); +#endif } ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */ +#ifdef HAVE_DH BIGNUM *other_pub_key = NULL; BIGNUM *dh_p = NULL; BIGNUM *dh_g = NULL; @@ -291,4 +296,7 @@ ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg DH_free(dh_priv); return ret; +#else + return enif_raise_exception(env, atom_notsup); +#endif } diff --git a/lib/crypto/c_src/dss.c b/lib/crypto/c_src/dss.c index 9bf8eb3ce0..63268f0f2b 100644 --- a/lib/crypto/c_src/dss.c +++ b/lib/crypto/c_src/dss.c @@ -21,6 +21,8 @@ #include "dss.h" #include "bn.h" +#ifdef HAVE_DSA + int get_dss_private_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa) { /* key=[P,Q,G,KEY] */ @@ -142,3 +144,5 @@ int get_dss_public_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa) BN_free(dsa_y); return 0; } + +#endif diff --git a/lib/crypto/c_src/dss.h b/lib/crypto/c_src/dss.h index 3275657e98..07e28ca7c5 100644 --- a/lib/crypto/c_src/dss.h +++ b/lib/crypto/c_src/dss.h @@ -23,7 +23,9 @@ #include "common.h" +#ifdef HAVE_DSA int get_dss_private_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa); int get_dss_public_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa); +#endif #endif /* E_DSS_H__ */ diff --git a/lib/crypto/c_src/openssl_config.h b/lib/crypto/c_src/openssl_config.h index f926f8af13..339eb5b8f4 100644 --- a/lib/crypto/c_src/openssl_config.h +++ b/lib/crypto/c_src/openssl_config.h @@ -25,9 +25,8 @@ #include <openssl/opensslconf.h> #include <openssl/crypto.h> -#ifndef OPENSSL_NO_DES #include <openssl/des.h> -#endif /* #ifndef OPENSSL_NO_DES */ + /* #include <openssl/idea.h> This is not supported on the openssl OTP requires */ #include <openssl/dsa.h> #include <openssl/rsa.h> @@ -166,6 +165,22 @@ # define HAVE_BLAKE2 #endif +#ifndef OPENSSL_NO_BF +# define HAVE_BF +#endif + +#ifndef OPENSSL_NO_DES +# define HAVE_DES +#endif + +#ifndef OPENSSL_NO_DH +# define HAVE_DH +#endif + +#ifndef OPENSSL_NO_DSA +# define HAVE_DSA +#endif + #ifndef OPENSSL_NO_MD4 # define HAVE_MD4 #endif diff --git a/lib/crypto/c_src/otp_test_engine.c b/lib/crypto/c_src/otp_test_engine.c index fd26b7cb5d..c3bd9dfb55 100644 --- a/lib/crypto/c_src/otp_test_engine.c +++ b/lib/crypto/c_src/otp_test_engine.c @@ -160,7 +160,7 @@ static int test_engine_md5_update(EVP_MD_CTX *ctx,const void *data, size_t count static int test_engine_md5_final(EVP_MD_CTX *ctx,unsigned char *md) { #ifdef OLD - fprintf(stderr, "MD5 final size of EVP_MD: %lu\r\n", sizeof(EVP_MD)); + fprintf(stderr, "MD5 final size of EVP_MD: %lu\r\n", (unsigned long)sizeof(EVP_MD)); if (!MD5_Final(md, data(ctx))) goto err; @@ -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/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c index 638bb588fa..a1e2677b34 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -254,7 +254,9 @@ static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_ { EVP_PKEY *result = NULL; RSA *rsa = NULL; +#ifdef HAVE_DSA DSA *dsa = NULL; +#endif #if defined(HAVE_EC) EC_KEY *ec = NULL; #endif @@ -327,6 +329,7 @@ static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_ return PKEY_NOTSUP; #endif } else if (algorithm == atom_dss) { +#ifdef HAVE_DSA if ((dsa = DSA_new()) == NULL) goto err; if (!get_dss_private_key(env, key, dsa)) @@ -340,9 +343,9 @@ static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_ dsa = NULL; } else { +#endif return PKEY_BADARG; } - goto done; err: @@ -357,8 +360,10 @@ static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_ enif_free(id); if (rsa) RSA_free(rsa); +#ifdef HAVE_DSA if (dsa) DSA_free(dsa); +#endif #ifdef HAVE_EC if (ec) EC_KEY_free(ec); @@ -377,7 +382,9 @@ static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_T { EVP_PKEY *result = NULL; RSA *rsa = NULL; +#ifdef HAVE_DSA DSA *dsa = NULL; +#endif #if defined(HAVE_EC) EC_KEY *ec = NULL; #endif @@ -449,6 +456,7 @@ static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_T return PKEY_NOTSUP; #endif } else if (algorithm == atom_dss) { +#ifdef HAVE_DSA if ((dsa = DSA_new()) == NULL) goto err; @@ -461,7 +469,9 @@ static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_T goto err; /* On success, result owns dsa */ dsa = NULL; - +#else + return PKEY_NOTSUP; +#endif } else { return PKEY_BADARG; } @@ -480,8 +490,10 @@ static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_T enif_free(id); if (rsa) RSA_free(rsa); +#ifdef HAVE_DSA if (dsa) DSA_free(dsa); +#endif #ifdef HAVE_EC if (ec) EC_KEY_free(ec); @@ -518,7 +530,9 @@ ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) unsigned char *tbs; /* data to be signed */ size_t tbslen; RSA *rsa = NULL; +#ifdef HAVE_DSA DSA *dsa = NULL; +#endif #if defined(HAVE_EC) EC_KEY *ec = NULL; #endif @@ -706,8 +720,10 @@ enif_get_atom(env,argv[1],buf,1024,ERL_NIF_LATIN1); printf("hash=%s ",buf); enif_release_binary(&sig_bin); if (rsa) RSA_free(rsa); +#ifdef HAVE_DSA if (dsa) DSA_free(dsa); +#endif #ifdef HAVE_EC if (ec) EC_KEY_free(ec); @@ -744,7 +760,9 @@ ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[] size_t tbslen; ERL_NIF_TERM ret; RSA *rsa = NULL; +#ifdef HAVE_DSA DSA *dsa = NULL; +#endif #ifdef HAVE_EC EC_KEY *ec = NULL; #endif @@ -890,8 +908,10 @@ ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[] EVP_PKEY_free(pkey); if (rsa) RSA_free(rsa); +#ifdef HAVE_DSA if (dsa) DSA_free(dsa); +#endif #ifdef HAVE_EC if (ec) EC_KEY_free(ec); @@ -1358,7 +1378,9 @@ ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ERL_NIF_TERM ret; EVP_PKEY *pkey = NULL; RSA *rsa = NULL; +#ifdef HAVE_DSA DSA *dsa = NULL; +#endif ERL_NIF_TERM result[8]; ASSERT(argc == 2); @@ -1383,6 +1405,7 @@ ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ret = enif_make_list_from_array(env, result, 2); +#ifdef HAVE_DSA } else if (argv[0] == atom_dss) { const BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL; @@ -1402,7 +1425,7 @@ ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM goto err; ret = enif_make_list_from_array(env, result, 4); - +#endif } else if (argv[0] == atom_ecdsa) { #if defined(HAVE_EC) /* not yet implemented @@ -1452,8 +1475,10 @@ ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM done: if (rsa) RSA_free(rsa); +#ifdef HAVE_DSA if (dsa) DSA_free(dsa); +#endif if (pkey) EVP_PKEY_free(pkey); diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 14efc5c6f6..641738247e 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -671,11 +671,12 @@ <fsummary>Initializes a series of encryptions or decryptions</fsummary> <desc> <p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>. - Initializes a series of encryptions or decryptions. + Initializes a series of encryptions or decryptions and creates an internal state + with a reference that is returned. The actual encryption or decryption is done by <seealso marker="crypto#crypto_update/2">crypto_update/2</seealso>. </p> - <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c>. + <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c>. For decryption, set it to <c>false</c>. </p> <p>See <seealso marker="crypto:new_api#the-new-api">examples in the User's Guide.</seealso> </p> @@ -683,15 +684,17 @@ </func> <func> - <name name="crypto_init_dyn_iv" arity="3" since="OTP 22.0"/> - <fsummary>Initializes a series of encryptions or decryptions where the IV is provided later</fsummary> + <name name="crypto_update" arity="2" since="OTP 22.0"/> + <fsummary>Do an actual crypto operation on a part of the full text</fsummary> <desc> <p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>. - Initializes a series of encryptions or decryptions where the IV is provided later. - The actual encryption or decryption is done by - <seealso marker="crypto#crypto_update_dyn_iv/3">crypto_update_dyn_iv/3</seealso>. - </p> - <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c>. + It does an actual crypto operation on a part of the full text. If the part is less + than a number of full blocks, only the full blocks (possibly none) are encrypted + or decrypted and the remaining bytes are saved to the next <c>crypto_update</c> operation. + The <c>State</c> should be created with + <seealso marker="crypto#crypto_init/3">crypto_init/3</seealso> + or + <seealso marker="crypto#crypto_init/4">crypto_init/4</seealso>. </p> <p>See <seealso marker="crypto:new_api#the-new-api">examples in the User's Guide.</seealso> </p> @@ -699,15 +702,15 @@ </func> <func> - <name name="crypto_update" arity="2" since="OTP 22.0"/> - <fsummary>Do an actual crypto operation on a part of the full text</fsummary> + <name name="crypto_dyn_iv_init" arity="3" since="OTP 22.0"/> + <fsummary>Initializes a series of encryptions or decryptions where the IV is provided later</fsummary> <desc> <p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>. - Do an actual crypto operation on a part of the full text. - The <c>State</c> should be created with - <seealso marker="crypto#crypto_init/3">crypto_init/3</seealso> - or - <seealso marker="crypto#crypto_init/4">crypto_init/4</seealso>. + Initializes a series of encryptions or decryptions where the IV is provided later. + The actual encryption or decryption is done by + <seealso marker="crypto#crypto_dyn_iv_update/3">crypto_dyn_iv_update/3</seealso>. + </p> + <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c>. For decryption, set it to <c>false</c>. </p> <p>See <seealso marker="crypto:new_api#the-new-api">examples in the User's Guide.</seealso> </p> @@ -715,13 +718,13 @@ </func> <func> - <name name="crypto_update_dyn_iv" arity="3" since="OTP 22.0"/> + <name name="crypto_dyn_iv_update" arity="3" since="OTP 22.0"/> <fsummary>Do an actual crypto operation on a part of the full text and the IV is supplied for each part</fsummary> <desc> <p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>. Do an actual crypto operation on a part of the full text and the IV is supplied for each part. The <c>State</c> should be created with - <seealso marker="crypto#crypto_init_dyn_iv/3">crypto_init_dyn_iv/3</seealso>. + <seealso marker="crypto#crypto_dyn_iv_init/3">crypto_dyn_iv_init/3</seealso>. </p> <p>See <seealso marker="crypto:new_api#the-new-api">examples in the User's Guide.</seealso> </p> @@ -743,7 +746,7 @@ <p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>. Do a complete encrypt or decrypt of the full text. </p> - <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c>. + <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c>. For decryption, set it to <c>false</c>. </p> <p>See <seealso marker="crypto:new_api#the-new-api">examples in the User's Guide.</seealso> </p> @@ -751,14 +754,19 @@ </func> <func> - <name name="crypto_aead" arity="6" since="OTP 22.0"/> - <name name="crypto_aead" arity="7" since="OTP 22.0"/> + <name name="crypto_one_time_aead" arity="6" since="OTP 22.0"/> + <name name="crypto_one_time_aead" arity="7" since="OTP 22.0"/> <fsummary>Do a complete encrypt or decrypt with an AEAD cipher of the full text</fsummary> <desc> <p>Part of the <seealso marker="crypto:new_api#the-new-api">new API</seealso>. Do a complete encrypt or decrypt with an AEAD cipher of the full text. </p> - <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c>. + <p>For encryption, set the <c>EncryptFlag</c> to <c>true</c> and set the <c>TagOrTagLength</c> + to the wanted size of the tag, that is, the tag length. If the default length is wanted, the + <c>crypto_aead/6</c> form may be used. + </p> + <p>For decryption, set the <c>EncryptFlag</c> to <c>false</c> and put the tag to be checked + in the argument <c>TagOrTagLength</c>. </p> <p>See <seealso marker="crypto:new_api#the-new-api">examples in the User's Guide.</seealso> </p> diff --git a/lib/crypto/doc/src/new_api.xml b/lib/crypto/doc/src/new_api.xml index 66eeefb692..79096b55e8 100644 --- a/lib/crypto/doc/src/new_api.xml +++ b/lib/crypto/doc/src/new_api.xml @@ -40,7 +40,7 @@ to maintain. </p> <p>It turned out that using the old api in the new way (more about that later), and still keep it - backwards compatible was not possible. Specially as more precision in the error messages was wanted + backwards compatible, was not possible. Specially as more precision in the error messages was wanted it could not be combined with the old standard. </p> <p>Therefore the old api (see next section) is kept for now but internally implemented with new primitives. @@ -66,26 +66,31 @@ <section> <title>The new API</title> - <p>The new functions for encrypting or decrypting one single text in one binary are: + <p>The new functions for encrypting or decrypting one single binary are: </p> <list> <item><seealso marker="crypto#crypto_one_time/4">crypto_one_time/4</seealso></item> <item><seealso marker="crypto#crypto_one_time/5">crypto_one_time/5</seealso></item> - <item><seealso marker="crypto#crypto_aead/6">crypto_aead/6</seealso></item> - <item><seealso marker="crypto#crypto_aead/7">crypto_aead/7</seealso></item> + <item><seealso marker="crypto#crypto_one_time_aead/6">crypto_one_time_aead/6</seealso></item> + <item><seealso marker="crypto#crypto_one_time_aead/7">crypto_one_time_aead/7</seealso></item> </list> - <p>The <c>crypto_aead</c> functions are for the ciphers of mode <c>ccm</c> or + <p>In those functions the internal crypto state is first created and initialized + with the cipher type, the key and possibly other data. Then the data is encrypted or decrypted, + the crypto state is de-allocated and the result of the crypto operation is returned. + </p> + <p>The <c>crypto_one_time_aead</c> functions are for the ciphers of mode <c>ccm</c> or <c>gcm</c>, and for the cipher <c>chacha20-poly1305</c>. </p> - <p>For repeated encryption or decryption of a text divided in parts, where the parts are handled - one by one but in sequence, the functions are: + <p>For repeated encryption or decryption of a text divided in parts, where the internal + crypto state is initialized once, and then many binaries are encrypted or decrypted with + the same state, the functions are: </p> <list> <item><seealso marker="crypto#crypto_init/4">crypto_init/4</seealso></item> <item><seealso marker="crypto#crypto_init/3">crypto_init/3</seealso></item> <item><seealso marker="crypto#crypto_update/2">crypto_update/2</seealso></item> </list> - <p>The <c>crypto_init</c> initialies a cipher operation and one or more calls of + <p>The <c>crypto_init</c> initialies an internal cipher state, and one or more calls of <c>crypto_update</c> does the acual encryption or decryption. Note that AEAD ciphers can't be handled this way due to their nature. </p> @@ -94,8 +99,8 @@ for each part, the functions are: </p> <list> - <item><seealso marker="crypto#crypto_init_dyn_iv/3">crypto_init_dyn_iv/3</seealso></item> - <item><seealso marker="crypto#crypto_update_dyn_iv/3">crypto_update_dyn_iv/3</seealso></item> + <item><seealso marker="crypto#crypto_dyn_iv_init/3">crypto_dyn_iv_init/3</seealso></item> + <item><seealso marker="crypto#crypto_dyn_iv_update/3">crypto_dyn_iv_update/3</seealso></item> </list> <p>An example of where those functions are needed, is when handling the TLS protocol.</p> @@ -105,8 +110,8 @@ <code type="erl"> 1> crypto:start(). ok - 2> Key = <<1:128>>, - 2> IV = <<0:128>>, + 2> Key = <<1:128>>. + 2> IV = <<0:128>>. 2> StateEnc = crypto:crypto_init(aes_128_ctr, Key, IV, true). % encrypt -> true #Ref<0.3768901617.1128660993.124047> 3> crypto:crypto_update(StateEnc, <<"First bytes">>). @@ -125,8 +130,8 @@ <<"s">> 9> </code> - <p>Note that the data that the <c>StateEnc</c> and <c>StateDec</c> references are destructivly - updated by the calls to <seealso marker="crypto#crypto_update/2">crypto_update/2</seealso>. + <p>Note that the internal data that the <c>StateEnc</c> and <c>StateDec</c> references are + destructivly updated by the calls to <seealso marker="crypto#crypto_update/2">crypto_update/2</seealso>. This is to gain time in the calls of the nifs interfacing the cryptolib. In a loop where the state is saved in the loop's state, it also saves one update of the loop state per crypto operation. </p> @@ -135,7 +140,7 @@ </p> <code type="erl"> encode(Crypto, Key, IV) -> - crypto_loop(crypto:crypto_init(Crypto, Key, IV, true)). + crypto_loop(crypto:crypto_init(Crypto, Key, IV, true)). crypto_loop(State) -> receive @@ -144,20 +149,17 @@ loop(State) end. </code> - <p>Note that the <c>State</c> is not updated. Such updates could be costly if the loop state - is a tuple or record with many elements. - </p> - </section> + </section> <section> <title>Example of crypto_one_time/5</title> - <p>The same eample as in the + <p>The same example as in the <seealso marker="#examples-of-crypto_init-4-and-crypto_update-2">previous section</seealso>, but now with one call to <c>crypto_one_time/5</c>: </p> <code> - 2> Key = <<1:128>>, - 2> IV = <<0:128>>, + 2> Key = <<1:128>>. + 2> IV = <<0:128>>. 2> Txt = [<<"First bytes">>,<<"Second bytes">>], 2> crypto:crypto_one_time(aes_128_ctr, Key, IV, Txt, true). <<67,44,216,166,25,130,203,5,66,6,162,16,79,94,115,234, diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 04b2f62266..3b431cceba 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -59,9 +59,9 @@ -export([crypto_init/4, crypto_init/3, crypto_update/2, crypto_one_time/4, crypto_one_time/5, - crypto_aead/6, crypto_aead/7, - crypto_init_dyn_iv/3, - crypto_update_dyn_iv/3 + crypto_one_time_aead/6, crypto_one_time_aead/7, + crypto_dyn_iv_init/3, + crypto_dyn_iv_update/3 ]). @@ -733,9 +733,9 @@ block_encrypt(Type, Key0, Ivec, Data) -> ?COMPAT( case Data of {AAD, PlainText} -> - crypto_aead(alias(Type,Key), Key, Ivec, PlainText, AAD, true); + crypto_one_time_aead(alias(Type,Key), Key, Ivec, PlainText, AAD, true); {AAD, PlainText, TagLength} -> - crypto_aead(alias(Type,Key), Key, Ivec, PlainText, AAD, TagLength, true); + crypto_one_time_aead(alias(Type,Key), Key, Ivec, PlainText, AAD, TagLength, true); PlainText -> crypto_one_time(alias(Type,Key), Key, Ivec, PlainText, true) end). @@ -764,7 +764,7 @@ block_decrypt(Type, Key0, Ivec, Data) -> ?COMPAT( case Data of {AAD, CryptoText, Tag} -> - crypto_aead(alias(Type,Key), Key, Ivec, CryptoText, AAD, Tag, false); + crypto_one_time_aead(alias(Type,Key), Key, Ivec, CryptoText, AAD, Tag, false); CryptoText -> crypto_one_time(alias(Type,Key), Key, Ivec, CryptoText, false) end). @@ -901,12 +901,12 @@ crypto_init(Cipher, Key, IV, EncryptFlag) -> %%%---------------------------------------------------------------- --spec crypto_init_dyn_iv(Cipher, Key, EncryptFlag) -> State | descriptive_error() +-spec crypto_dyn_iv_init(Cipher, Key, EncryptFlag) -> State | descriptive_error() when Cipher :: cipher_iv(), Key :: iodata(), EncryptFlag :: boolean(), State :: crypto_state() . -crypto_init_dyn_iv(Cipher, Key, EncryptFlag) -> +crypto_dyn_iv_init(Cipher, Key, EncryptFlag) -> %% The IV is supposed to be supplied by calling crypto_update/3 ng_crypto_init_nif(Cipher, iolist_to_binary(Key), undefined, EncryptFlag). @@ -931,12 +931,12 @@ crypto_update(State, Data0) -> %%%---------------------------------------------------------------- --spec crypto_update_dyn_iv(State, Data, IV) -> Result | descriptive_error() +-spec crypto_dyn_iv_update(State, Data, IV) -> Result | descriptive_error() when State :: crypto_state(), Data :: iodata(), IV :: iodata(), Result :: binary() . -crypto_update_dyn_iv(State, Data0, IV) -> +crypto_dyn_iv_update(State, Data0, IV) -> %% When State is from State = crypto_init(Cipher, Key, undefined, EncryptFlag) case iolist_to_binary(Data0) of <<>> -> @@ -982,7 +982,7 @@ crypto_one_time(Cipher, Key, IV, Data0, EncryptFlag) -> end. --spec crypto_aead(Cipher, Key, IV, InText, AAD, EncFlag::true) -> +-spec crypto_one_time_aead(Cipher, Key, IV, InText, AAD, EncFlag::true) -> Result | descriptive_error() when Cipher :: cipher_aead(), Key :: iodata(), @@ -994,11 +994,11 @@ crypto_one_time(Cipher, Key, IV, Data0, EncryptFlag) -> OutCryptoText :: binary(), OutTag :: binary(). -crypto_aead(Cipher, Key, IV, PlainText, AAD, true) -> - crypto_aead(Cipher, Key, IV, PlainText, AAD, aead_tag_len(Cipher), true). +crypto_one_time_aead(Cipher, Key, IV, PlainText, AAD, true) -> + crypto_one_time_aead(Cipher, Key, IV, PlainText, AAD, aead_tag_len(Cipher), true). --spec crypto_aead(Cipher, Key, IV, InText, AAD, TagOrTagLength, EncFlag) -> +-spec crypto_one_time_aead(Cipher, Key, IV, InText, AAD, TagOrTagLength, EncFlag) -> Result | descriptive_error() when Cipher :: cipher_aead(), Key :: iodata(), @@ -1016,7 +1016,7 @@ crypto_aead(Cipher, Key, IV, PlainText, AAD, true) -> OutTag :: binary(), OutPlainText :: binary(). -crypto_aead(Cipher, Key, IV, TextIn, AAD, TagOrTagLength, EncFlg) -> +crypto_one_time_aead(Cipher, Key, IV, TextIn, AAD, TagOrTagLength, EncFlg) -> aead_cipher(Cipher, Key, IV, TextIn, AAD, TagOrTagLength, EncFlg). @@ -1058,8 +1058,21 @@ ng_crypto_one_time_nif(_Cipher, _Key, _IVec, _Data, _EncryptFlg) -> ?nif_stub. %%%---------------------------------------------------------------- %%% Cipher aliases %%% -prepend_cipher_aliases(L) -> - [des3_cbc, des_ede3, des_ede3_cbf, des3_cbf, des3_cfb, aes_cbc128, aes_cbc256 | L]. +prepend_cipher_aliases(L0) -> + L = + case lists:member(des_ede3_cbc, L0) of + true -> + [des3_cbc, des_ede3, des_ede3_cbf, des3_cbf, des3_cfb | L0]; + false -> + L0 + end, + case lists:member(aes_128_cbc, L0) of + true -> + [aes_cbc128, aes_cbc256 | L]; + false -> + L + end. + %%%---- des_ede3_cbc alias(des3_cbc) -> des_ede3_cbc; diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 6a2727a622..880fd7ab0b 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -621,15 +621,15 @@ do_api_ng_tls({Type, Key, IV, PlainTexts}=_X) -> do_api_ng_tls({Type, Key, IV, PlainText0, ExpectedEncText}=_X) -> ct:log("~p",[_X]), PlainText = iolist_to_binary(lazy_eval(PlainText0)), - Renc = crypto:crypto_init_dyn_iv(Type, Key, true), - Rdec = crypto:crypto_init_dyn_iv(Type, Key, false), - EncTxt = crypto:crypto_update_dyn_iv(Renc, PlainText, IV), + Renc = crypto:crypto_dyn_iv_init(Type, Key, true), + Rdec = crypto:crypto_dyn_iv_init(Type, Key, false), + EncTxt = crypto:crypto_dyn_iv_update(Renc, PlainText, IV), case ExpectedEncText of undefined -> ok; EncTxt -> %% Now check that the state is NOT updated: - case crypto:crypto_update_dyn_iv(Renc, PlainText, IV) of + case crypto:crypto_dyn_iv_update(Renc, PlainText, IV) of EncTxt -> ok; EncTxt2 -> @@ -640,10 +640,10 @@ do_api_ng_tls({Type, Key, IV, PlainText0, ExpectedEncText}=_X) -> ct:log("1st encode~nIn: ~p~nExpected: ~p~nEnc: ~p~n", [{Type,Key,IV,PlainText}, ExpectedEncText, OtherEnc]), ct:fail("api_ng_tls (encode)",[]) end, - case crypto:crypto_update_dyn_iv(Rdec, EncTxt, IV) of + case crypto:crypto_dyn_iv_update(Rdec, EncTxt, IV) of PlainText -> %% Now check that the state is NOT updated: - case crypto:crypto_update_dyn_iv(Rdec, EncTxt, IV) of + case crypto:crypto_dyn_iv_update(Rdec, EncTxt, IV) of PlainText -> ok; PlainText2 -> @@ -1183,7 +1183,7 @@ aead_cipher({Type, Key, PlainText, IV, AAD, CipherText, CipherTag, TagLen, Info} catch error:E -> ct:log("~p",[{Type, Key, PlainText, IV, AAD, CipherText, CipherTag, TagLen, Info}]), - try crypto:crypto_aead(Type, Key, IV, PlainText, AAD, TagLen, true) + try crypto:crypto_one_time_aead(Type, Key, IV, PlainText, AAD, TagLen, true) of RR -> ct:log("Works: ~p",[RR]) diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl index 3416fbd78d..41cd132734 100644 --- a/lib/crypto/test/engine_SUITE.erl +++ b/lib/crypto/test/engine_SUITE.erl @@ -148,8 +148,21 @@ end_per_group(_, Config) -> end. %%-------------------------------------------------------------------- -init_per_testcase(_Case, Config) -> - Config. +init_per_testcase(Case, Config) -> + case string:tokens(atom_to_list(Case),"_") of + ["sign","verify",Type|_] -> + skip_if_unsup(list_to_atom(Type), Config); + + ["priv","encrypt","pub","decrypt",Type|_] -> + skip_if_unsup(list_to_atom(Type), Config); + + ["get","pub","from","priv","key",Type|_] -> + skip_if_unsup(list_to_atom(Type), Config); + + _ -> + Config + end. + end_per_testcase(_Case, _Config) -> ok. @@ -851,6 +864,19 @@ get_pub_from_priv_key_ecdsa(Config) -> %%%================================================================ %%% Help for engine_stored_pub_priv_keys* test cases %%% +skip_if_unsup(Type, Config) -> + case pkey_supported(Type) of + false -> + {skip, "Unsupported in this cryptolib"}; + true -> + Config + end. + + +pkey_supported(Type) -> + lists:member(Type, proplists:get_value(public_keys, crypto:supports(), [])). + + load_storage_engine(Config) -> load_storage_engine(Config, []). diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl index 3fe026b096..245c099fef 100644 --- a/lib/dialyzer/src/dialyzer_utils.erl +++ b/lib/dialyzer/src/dialyzer_utils.erl @@ -46,6 +46,7 @@ ]). -include("dialyzer.hrl"). +-include("../../compiler/src/core_parse.hrl"). %%-define(DEBUG, true). @@ -751,9 +752,13 @@ pp_hook(Node, Ctxt, Cont) -> map -> pp_map(Node, Ctxt, Cont); literal -> - case is_map(cerl:concrete(Node)) of - true -> pp_map(Node, Ctxt, Cont); - false -> Cont(Node, Ctxt) + case cerl:concrete(Node) of + Map when is_map(Map) -> + pp_map(Node, Ctxt, Cont); + Bitstr when is_bitstring(Bitstr) -> + pp_binary(Node, Ctxt, Cont); + _ -> + Cont(Node, Ctxt) end; _ -> Cont(Node, Ctxt) @@ -761,7 +766,7 @@ pp_hook(Node, Ctxt, Cont) -> pp_binary(Node, Ctxt, Cont) -> prettypr:beside(prettypr:text("<<"), - prettypr:beside(pp_segments(cerl:binary_segments(Node), + prettypr:beside(pp_segments(cerl_binary_segments(Node), Ctxt, Cont), prettypr:text(">>"))). @@ -780,10 +785,29 @@ pp_segment(Node, Ctxt, Cont) -> Unit = cerl:bitstr_unit(Node), Type = cerl:bitstr_type(Node), Flags = cerl:bitstr_flags(Node), - prettypr:beside(Cont(Val, Ctxt), - prettypr:beside(pp_size(Size, Ctxt, Cont), - prettypr:beside(pp_opts(Type, Flags), - pp_unit(Unit, Ctxt, Cont)))). + RestPP = + case {concrete(Unit), concrete(Type), concrete(Flags)} of + {1, integer, [unsigned, big]} -> % Simplify common cases. + case concrete(Size) of + 8 -> prettypr:text(""); + _ -> pp_size(Size, Ctxt, Cont) + end; + {8, binary, [unsigned, big]} -> + SizePP = pp_size(Size, Ctxt, Cont), + prettypr:beside(SizePP, + prettypr:beside(prettypr:text("/"), pp_atom(Type))); + _What -> + SizePP = pp_size(Size, Ctxt, Cont), + UnitPP = pp_unit(Unit, Ctxt, Cont), + OptsPP = pp_opts(Type, Flags), + prettypr:beside(SizePP, prettypr:beside(OptsPP, UnitPP)) + end, + prettypr:beside(Cont(Val, Ctxt), RestPP). + +concrete(Cerl) -> + try cerl:concrete(Cerl) + catch _:_ -> anything_unexpected + end. pp_size(Size, Ctxt, Cont) -> case cerl:is_c_atom(Size) of @@ -859,6 +883,31 @@ seq([H | T], Separator, Ctxt, Fun) -> seq([], _, _, _) -> [prettypr:empty()]. +cerl_binary_segments(#c_literal{val = B}) when is_bitstring(B) -> + segs_from_bitstring(B); +cerl_binary_segments(CBinary) -> + cerl:binary_segments(CBinary). + +%% Copied from core_pp. The function cerl:binary_segments/2 should/could +%% be extended to handle literals, but then the cerl module cannot be +%% HiPE-compiled as of Erlang/OTP 22.0 (due to <<I:N>>). +segs_from_bitstring(<<H,T/bitstring>>) -> + [#c_bitstr{val=#c_literal{val=H}, + size=#c_literal{val=8}, + unit=#c_literal{val=1}, + type=#c_literal{val=integer}, + flags=#c_literal{val=[unsigned,big]}}|segs_from_bitstring(T)]; +segs_from_bitstring(<<>>) -> + []; +segs_from_bitstring(Bitstring) -> + N = bit_size(Bitstring), + <<I:N>> = Bitstring, + [#c_bitstr{val=#c_literal{val=I}, + size=#c_literal{val=N}, + unit=#c_literal{val=1}, + type=#c_literal{val=integer}, + flags=#c_literal{val=[unsigned,big]}}]. + %%------------------------------------------------------------------------------ -spec refold_pattern(cerl:cerl()) -> cerl:cerl(). diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/simple b/lib/dialyzer/test/opaque_SUITE_data/results/simple index 5cd8916aee..0e1bb934e9 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/simple +++ b/lib/dialyzer/test/opaque_SUITE_data/results/simple @@ -63,9 +63,9 @@ simple1_api.erl:381: Invalid type specification for function simple1_api:bool_ad simple1_api.erl:407: The size simple1_adt:i1() breaks the opacity of A simple1_api.erl:418: The attempt to match a term of type non_neg_integer() against the variable A breaks the opacity of simple1_adt:i1() simple1_api.erl:425: The attempt to match a term of type non_neg_integer() against the variable B breaks the opacity of simple1_adt:i1() -simple1_api.erl:432: The pattern <<_:B/integer-unit:1>> can never match the type any() +simple1_api.erl:432: The pattern <<_:B>> can never match the type any() simple1_api.erl:448: The attempt to match a term of type non_neg_integer() against the variable Sz breaks the opacity of simple1_adt:i1() -simple1_api.erl:460: The attempt to match a term of type simple1_adt:bit1() against the pattern <<_/binary-unit:8>> breaks the opacity of the term +simple1_api.erl:460: The attempt to match a term of type simple1_adt:bit1() against the pattern <<_/binary>> breaks the opacity of the term simple1_api.erl:478: The call 'foo':A(A::simple1_adt:a()) breaks the opacity of the term A :: simple1_adt:a() simple1_api.erl:486: The call A:'foo'(A::simple1_adt:a()) breaks the opacity of the term A :: simple1_adt:a() simple1_api.erl:499: The call 'foo':A(A::simple1_api:i()) requires that A is of type atom() not simple1_api:i() diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/asn1 b/lib/dialyzer/test/r9c_SUITE_data/results/asn1 index 1cf03346ee..6e51b972af 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/results/asn1 +++ b/lib/dialyzer/test/r9c_SUITE_data/results/asn1 @@ -87,7 +87,7 @@ asn1rt_per_bin.erl:2127: Cons will produce an improper list since its 2nd argume asn1rt_per_bin.erl:2129: Cons will produce an improper list since its 2nd argument is integer() asn1rt_per_bin.erl:446: The variable _ can never match since previous clauses completely covered the type integer() asn1rt_per_bin.erl:467: The variable _ can never match since previous clauses completely covered the type integer() -asn1rt_per_bin.erl:474: The pattern <{_N, <<_:8/integer-unit:1,Bs/binary-unit:8>>}, C> can never match since previous clauses completely covered the type <{0,_},integer()> +asn1rt_per_bin.erl:474: The pattern <{_N, <<_,Bs/binary>>}, C> can never match since previous clauses completely covered the type <{0,_},integer()> asn1rt_per_bin.erl:487: The variable _ can never match since previous clauses completely covered the type integer() asn1rt_per_bin.erl:498: The variable _ can never match since previous clauses completely covered the type integer() asn1rt_per_bin_rt2ct.erl:152: The call asn1rt_per_bin_rt2ct:getbit({0,maybe_improper_list()}) will never return since it differs in the 1st argument from the success typing arguments: (<<_:8,_:_*8>> | {non_neg_integer(),<<_:1,_:_*1>>}) @@ -95,7 +95,7 @@ asn1rt_per_bin_rt2ct.erl:1533: The pattern {'BMPString', {'octets', Ol}} can nev asn1rt_per_bin_rt2ct.erl:1875: The pattern {Name, Val} can never match since previous clauses completely covered the type any() asn1rt_per_bin_rt2ct.erl:443: The variable _ can never match since previous clauses completely covered the type integer() asn1rt_per_bin_rt2ct.erl:464: The variable _ can never match since previous clauses completely covered the type integer() -asn1rt_per_bin_rt2ct.erl:471: The pattern <{_N, <<_B:8/integer-unit:1,Bs/binary-unit:8>>}, C> can never match since previous clauses completely covered the type <{0,_},integer()> +asn1rt_per_bin_rt2ct.erl:471: The pattern <{_N, <<_B,Bs/binary>>}, C> can never match since previous clauses completely covered the type <{0,_},integer()> asn1rt_per_bin_rt2ct.erl:484: The variable _ can never match since previous clauses completely covered the type integer() asn1rt_per_bin_rt2ct.erl:495: The variable _ can never match since previous clauses completely covered the type integer() asn1rt_per_v1.erl:1209: The pattern <_, 'true', _> can never match the type <_,'false',_> 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/dialyzer/test/small_SUITE_data/results/bs_fail_constr b/lib/dialyzer/test/small_SUITE_data/results/bs_fail_constr index dbc8241971..797f83956d 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/bs_fail_constr +++ b/lib/dialyzer/test/small_SUITE_data/results/bs_fail_constr @@ -1,9 +1,9 @@ bs_fail_constr.erl:11: Function w3/1 has no local return -bs_fail_constr.erl:12: Binary construction will fail since the size field S in segment 42:S/integer-unit:1 has type neg_integer() +bs_fail_constr.erl:12: Binary construction will fail since the size field S in segment 42:S has type neg_integer() bs_fail_constr.erl:14: Function w4/1 has no local return bs_fail_constr.erl:15: Binary construction will fail since the value field V in segment V/utf32 has type float() bs_fail_constr.erl:5: Function w1/1 has no local return -bs_fail_constr.erl:6: Binary construction will fail since the value field V in segment V:8/integer-unit:1 has type float() +bs_fail_constr.erl:6: Binary construction will fail since the value field V in segment V has type float() bs_fail_constr.erl:8: Function w2/1 has no local return -bs_fail_constr.erl:9: Binary construction will fail since the value field V in segment V/binary-unit:8 has type atom() +bs_fail_constr.erl:9: Binary construction will fail since the value field V in segment V/binary has type atom() diff --git a/lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring b/lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring index e148e5cf22..dc3620fcf0 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring +++ b/lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring @@ -1,3 +1,3 @@ pretty_bitstring.erl:7: Function t/0 has no local return -pretty_bitstring.erl:8: The call binary:copy(#{#<1>(8, 1, 'integer', ['unsigned', 'big']), #<2>(8, 1, 'integer', ['unsigned', 'big']), #<3>(3, 1, 'integer', ['unsigned', 'big'])}#,2) breaks the contract (Subject,N) -> binary() when Subject :: binary(), N :: non_neg_integer() +pretty_bitstring.erl:8: The call binary:copy(<<1,2,3:3>>,2) breaks the contract (Subject,N) -> binary() when Subject :: binary(), N :: non_neg_integer() diff --git a/lib/dialyzer/test/small_SUITE_data/results/tuple_set_crash b/lib/dialyzer/test/small_SUITE_data/results/tuple_set_crash index 8c9df56a4b..7fd1f304cb 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/tuple_set_crash +++ b/lib/dialyzer/test/small_SUITE_data/results/tuple_set_crash @@ -3,12 +3,12 @@ tuple_set_crash.erl:103: Invalid type specification for function tuple_set_crash tuple_set_crash.erl:123: Invalid type specification for function tuple_set_crash:parse_video_target_info/1. The success typing is (<<_:48>>) -> [{'status',byte()} | {'target_id',non_neg_integer()},...] tuple_set_crash.erl:127: Invalid type specification for function tuple_set_crash:parse_audio_target_info/1. The success typing is (<<_:48>>) -> [{'master_volume',char()} | {'status',byte()} | {'target_id',non_neg_integer()},...] tuple_set_crash.erl:138: Invalid type specification for function tuple_set_crash:parse_av_device_info/1. The success typing is (<<_:48>>) -> [{'address',byte()} | {'device_id',non_neg_integer()} | {'model',binary()} | {'status',byte()},...] -tuple_set_crash.erl:143: The pattern <<TargetId:32/integer-little-unit:1,Rest1/binary-unit:8>> can never match the type <<_:8>> +tuple_set_crash.erl:143: The pattern <<TargetId:32/integer-little-unit:1,Rest1/binary>> can never match the type <<_:8>> tuple_set_crash.erl:155: Invalid type specification for function tuple_set_crash:parse_video_output_info/1. The success typing is (<<_:48>>) -> [{'audio_volume',char()} | {'display_type',binary()} | {'output_id',non_neg_integer()},...] -tuple_set_crash.erl:160: The pattern <<DeviceId:32/integer-little-unit:1,Rest1/binary-unit:8>> can never match the type <<_:8>> +tuple_set_crash.erl:160: The pattern <<DeviceId:32/integer-little-unit:1,Rest1/binary>> can never match the type <<_:8>> tuple_set_crash.erl:171: Invalid type specification for function tuple_set_crash:parse_audio_output_info/1. The success typing is (<<_:48>>) -> [{'output_id',non_neg_integer()},...] -tuple_set_crash.erl:176: The pattern <<DeviceId:32/integer-little-unit:1,Rest1/binary-unit:8>> can never match the type <<_:8>> -tuple_set_crash.erl:179: The pattern <<AudioVolume:16/integer-little-unit:1,Rest2/binary-unit:8>> can never match the type <<_:8>> -tuple_set_crash.erl:182: The pattern <<Delay:16/integer-little-unit:1,_Padding/binary-unit:8>> can never match the type <<_:8>> +tuple_set_crash.erl:176: The pattern <<DeviceId:32/integer-little-unit:1,Rest1/binary>> can never match the type <<_:8>> +tuple_set_crash.erl:179: The pattern <<AudioVolume:16/integer-little-unit:1,Rest2/binary>> can never match the type <<_:8>> +tuple_set_crash.erl:182: The pattern <<Delay:16/integer-little-unit:1,_Padding/binary>> can never match the type <<_:8>> tuple_set_crash.erl:62: The pattern {'play_list', _Playlist} can never match the type 'ok' | {'device_properties',[{atom(),_}]} | {'error',[{atom(),_}]} tuple_set_crash.erl:64: The pattern {'error', 17} can never match the type 'ok' | {'device_properties',[{atom(),_}]} | {'error',[{atom(),_}]} diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in index b2600f0fab..6e0d3476c7 100644 --- a/lib/erl_interface/src/Makefile.in +++ b/lib/erl_interface/src/Makefile.in @@ -784,29 +784,31 @@ $(MDD_OBJDIR)/ei_fake_prog_mdd_cxx$(EXE): prog/ei_fake_prog.c $(MDD_EILIB) # Create dependency file using gcc -MM ########################################################################### -depend: +depend: $(TARGET)/depend.mk + +$(TARGET)/depend.mk: $(TARGET)/config.h $(gen_verbose) - $(V_colon)@echo "Generating dependency file depend.mk..." - @echo "# Generated dependency rules" > depend.mk; \ - $(V_CC) $(CFLAGS) -MM $(SOURCES) | \ + $(V_colon)echo "Generating dependency file depend.mk..." + @echo "# Generated dependency rules" > $@ + $(V_CC) $(CFLAGS) -MM $(SOURCES) | \ sed 's&$(TARGET)&\$$\(TARGET\)&g' | \ - sed 's/^.*:/\$$\(ST_OBJDIR\)\/&/' >> depend.mk; \ - echo >> depend.mk; \ - $(CC) $(CFLAGS) -MM $(SOURCES) | \ + sed 's/^.*:/\$$\(ST_OBJDIR\)\/&/' >> $@ + @echo >> $@ + $(V_CC) $(CFLAGS) -MM $(SOURCES) | \ sed 's&$(TARGET)&\$$\(TARGET\)&g' | \ - sed 's/^.*:/\$$\(MT_OBJDIR\)\/&/' >> depend.mk; \ - echo >> depend.mk; \ - $(CC) $(CFLAGS) -MM $(SOURCES) | \ + sed 's/^.*:/\$$\(MT_OBJDIR\)\/&/' >> $@ + @echo >> $@ + $(V_CC) $(CFLAGS) -MM $(SOURCES) | \ sed 's&$(TARGET)&\$$\(TARGET\)&g' | \ - sed 's/^.*:/\$$\(MD_OBJDIR\)\/&/' >> depend.mk; \ - echo >> depend.mk; \ - $(CC) $(CFLAGS) -MM $(SOURCES) | \ + sed 's/^.*:/\$$\(MD_OBJDIR\)\/&/' >> $@ + @echo >> $@ + $(V_CC) $(CFLAGS) -MM $(SOURCES) | \ sed 's&$(TARGET)&\$$\(TARGET\)&g' | \ - sed 's/^.*:/\$$\(MDD_OBJDIR\)\/&/' >> depend.mk; \ - echo >> depend.mk + sed 's/^.*:/\$$\(MDD_OBJDIR\)\/&/' >> $@ + @echo >> $@ # For some reason this has to be after 'opt' target -include depend.mk +-include $(TARGET)/depend.mk # ---------------------------------------------------- # Release Target diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c index be7a2a6b0e..7a304e6d4f 100644 --- a/lib/erl_interface/src/connect/ei_connect.c +++ b/lib/erl_interface/src/connect/ei_connect.c @@ -659,7 +659,7 @@ int ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname, return ERL_ERROR; } - ec->creation = creation; + ec->creation = creation & 0x3; /* 2 bits */ if (cookie) { if (strlen(cookie) >= sizeof(ec->ei_connect_cookie)) { @@ -698,7 +698,7 @@ int ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname, strcpy(ec->self.node,thisnodename); ec->self.num = 0; ec->self.serial = 0; - ec->self.creation = creation; + ec->self.creation = creation & 0x3; /* 2 bits */ ec->cbs = cbs; ec->setup_context = setup_context; diff --git a/lib/erl_interface/src/depend.mk b/lib/erl_interface/src/depend.mk deleted file mode 100644 index af753046e5..0000000000 --- a/lib/erl_interface/src/depend.mk +++ /dev/null @@ -1,1133 +0,0 @@ -# Generated dependency rules -$(ST_OBJDIR)/ei_connect.o: connect/ei_connect.c $(TARGET)/config.h \ - misc/eidef.h ../include/ei.h misc/eiext.h misc/ei_portio.h \ - misc/ei_internal.h connect/ei_connect_int.h misc/ei_locking.h \ - connect/eisend.h connect/eirecv.h misc/eimd5.h misc/putget.h \ - connect/ei_resolve.h epmd/ei_epmd.h -$(ST_OBJDIR)/ei_resolve.o: connect/ei_resolve.c $(TARGET)/config.h \ - misc/eidef.h ../include/ei.h connect/ei_resolve.h misc/ei_locking.h -$(ST_OBJDIR)/eirecv.o: connect/eirecv.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eirecv.h misc/ei_portio.h \ - misc/ei_internal.h misc/putget.h misc/ei_trace.h misc/show_msg.h -$(ST_OBJDIR)/send.o: connect/send.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eisend.h misc/putget.h \ - connect/ei_connect_int.h misc/ei_internal.h misc/ei_trace.h \ - misc/show_msg.h -$(ST_OBJDIR)/send_exit.o: connect/send_exit.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/ei_connect_int.h misc/ei_trace.h \ - misc/ei_internal.h misc/putget.h misc/show_msg.h -$(ST_OBJDIR)/send_reg.o: connect/send_reg.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eisend.h misc/putget.h \ - connect/ei_connect_int.h misc/ei_internal.h misc/ei_trace.h \ - misc/show_msg.h -$(ST_OBJDIR)/decode_atom.o: decode/decode_atom.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_big.o: decode/decode_big.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_bignum.o: decode/decode_bignum.c $(TARGET)/config.h -$(ST_OBJDIR)/decode_binary.o: decode/decode_binary.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_boolean.o: decode/decode_boolean.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_char.o: decode/decode_char.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_double.o: decode/decode_double.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_fun.o: decode/decode_fun.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_malloc.h decode/decode_skip.h misc/putget.h -$(ST_OBJDIR)/decode_intlist.o: decode/decode_intlist.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_list_header.o: decode/decode_list_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_long.o: decode/decode_long.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_pid.o: decode/decode_pid.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_port.o: decode/decode_port.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_ref.o: decode/decode_ref.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_skip.o: decode/decode_skip.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - decode/decode_skip.h -$(ST_OBJDIR)/decode_string.o: decode/decode_string.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_trace.o: decode/decode_trace.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/putget.h -$(ST_OBJDIR)/decode_tuple_header.o: decode/decode_tuple_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_ulong.o: decode/decode_ulong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_version.o: decode/decode_version.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_longlong.o: decode/decode_longlong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/decode_ulonglong.o: decode/decode_ulonglong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_atom.o: encode/encode_atom.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_bignum.o: encode/encode_bignum.c $(TARGET)/config.h -$(ST_OBJDIR)/encode_binary.o: encode/encode_binary.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_boolean.o: encode/encode_boolean.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_char.o: encode/encode_char.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_double.o: encode/encode_double.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_fun.o: encode/encode_fun.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_list_header.o: encode/encode_list_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_long.o: encode/encode_long.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_pid.o: encode/encode_pid.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_port.o: encode/encode_port.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_ref.o: encode/encode_ref.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_string.o: encode/encode_string.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_trace.o: encode/encode_trace.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/putget.h -$(ST_OBJDIR)/encode_tuple_header.o: encode/encode_tuple_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_ulong.o: encode/encode_ulong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_version.o: encode/encode_version.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(ST_OBJDIR)/encode_longlong.o: encode/encode_longlong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h -$(ST_OBJDIR)/encode_ulonglong.o: encode/encode_ulonglong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h -$(ST_OBJDIR)/epmd_port.o: epmd/epmd_port.c misc/ei_internal.h epmd/ei_epmd.h \ - misc/putget.h -$(ST_OBJDIR)/epmd_publish.o: epmd/epmd_publish.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_internal.h \ - misc/putget.h ../include/erl_interface.h epmd/ei_epmd.h -$(ST_OBJDIR)/epmd_unpublish.o: epmd/epmd_unpublish.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_internal.h \ - misc/putget.h ../include/erl_interface.h epmd/ei_epmd.h -$(ST_OBJDIR)/ei_decode_term.o: misc/ei_decode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_decode_term.h misc/putget.h -$(ST_OBJDIR)/ei_format.o: misc/ei_format.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/ei_malloc.h misc/ei_format.h -$(ST_OBJDIR)/ei_locking.o: misc/ei_locking.c $(TARGET)/config.h \ - misc/ei_malloc.h misc/ei_locking.h -$(ST_OBJDIR)/ei_malloc.o: misc/ei_malloc.c misc/ei_malloc.h -$(ST_OBJDIR)/ei_portio.o: misc/ei_portio.c misc/ei_portio.h misc/ei_internal.h -$(ST_OBJDIR)/ei_printterm.o: misc/ei_printterm.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_printterm.h misc/ei_malloc.h -$(ST_OBJDIR)/ei_pthreads.o: misc/ei_pthreads.c $(TARGET)/config.h \ - ../include/ei.h misc/ei_locking.h -$(ST_OBJDIR)/ei_trace.o: misc/ei_trace.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/ei_trace.h -$(ST_OBJDIR)/ei_x_encode.o: misc/ei_x_encode.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_x_encode.h \ - misc/ei_malloc.h -$(ST_OBJDIR)/eimd5.o: misc/eimd5.c misc/eimd5.h -$(ST_OBJDIR)/get_type.o: misc/get_type.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h misc/putget.h -$(ST_OBJDIR)/show_msg.o: misc/show_msg.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h misc/putget.h misc/ei_printterm.h \ - misc/ei_internal.h misc/show_msg.h -$(ST_OBJDIR)/ei_compat.o: misc/ei_compat.c ../include/ei.h misc/ei_internal.h -$(ST_OBJDIR)/hash_dohash.o: registry/hash_dohash.c registry/hash.h ../include/ei.h -$(ST_OBJDIR)/hash_foreach.o: registry/hash_foreach.c registry/hash.h ../include/ei.h -$(ST_OBJDIR)/hash_freetab.o: registry/hash_freetab.c registry/hash.h ../include/ei.h -$(ST_OBJDIR)/hash_insert.o: registry/hash_insert.c registry/hash.h ../include/ei.h -$(ST_OBJDIR)/hash_isprime.o: registry/hash_isprime.c registry/hash.h ../include/ei.h -$(ST_OBJDIR)/hash_lookup.o: registry/hash_lookup.c registry/hash.h ../include/ei.h -$(ST_OBJDIR)/hash_newtab.o: registry/hash_newtab.c registry/hash.h ../include/ei.h -$(ST_OBJDIR)/hash_remove.o: registry/hash_remove.c registry/hash.h ../include/ei.h -$(ST_OBJDIR)/hash_resize.o: registry/hash_resize.c registry/hash.h ../include/ei.h -$(ST_OBJDIR)/hash_rlookup.o: registry/hash_rlookup.c registry/hash.h ../include/ei.h -$(ST_OBJDIR)/reg_close.o: registry/reg_close.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_delete.o: registry/reg_delete.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_dirty.o: registry/reg_dirty.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_dump.o: registry/reg_dump.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - registry/reg.h registry/hash.h connect/eisend.h connect/eirecv.h \ - connect/ei_connect_int.h -$(ST_OBJDIR)/reg_free.o: registry/reg_free.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_get.o: registry/reg_get.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_getf.o: registry/reg_getf.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_geti.o: registry/reg_geti.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_getp.o: registry/reg_getp.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_gets.o: registry/reg_gets.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_make.o: registry/reg_make.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_open.o: registry/reg_open.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_purge.o: registry/reg_purge.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_resize.o: registry/reg_resize.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_restore.o: registry/reg_restore.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - registry/reg.h registry/hash.h connect/eisend.h connect/eirecv.h \ - connect/ei_connect_int.h -$(ST_OBJDIR)/reg_set.o: registry/reg_set.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_setf.o: registry/reg_setf.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_seti.o: registry/reg_seti.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_setp.o: registry/reg_setp.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_sets.o: registry/reg_sets.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_stat.o: registry/reg_stat.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/reg_tabstat.o: registry/reg_tabstat.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(ST_OBJDIR)/decode_term.o: legacy/decode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h ../include/erl_interface.h -$(ST_OBJDIR)/encode_term.o: legacy/encode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h ../include/erl_interface.h \ - legacy/erl_marshal.h legacy/erl_eterm.h legacy/portability.h -$(ST_OBJDIR)/erl_connect.o: legacy/erl_connect.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_config.h \ - legacy/erl_connect.h legacy/erl_eterm.h legacy/portability.h \ - legacy/erl_malloc.h misc/putget.h connect/ei_connect_int.h \ - misc/ei_locking.h epmd/ei_epmd.h misc/ei_internal.h -$(ST_OBJDIR)/erl_error.o: legacy/erl_error.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_error.h -$(ST_OBJDIR)/erl_eterm.o: legacy/erl_eterm.c misc/ei_locking.h \ - $(TARGET)/config.h connect/ei_resolve.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_eterm.h \ - legacy/portability.h legacy/erl_malloc.h legacy/erl_marshal.h \ - legacy/erl_error.h legacy/erl_internal.h misc/ei_internal.h -$(ST_OBJDIR)/erl_fix_alloc.o: legacy/erl_fix_alloc.c $(TARGET)/config.h \ - misc/ei_locking.h ../include/erl_interface.h ../include/ei.h \ - legacy/erl_error.h legacy/erl_malloc.h legacy/erl_fix_alloc.h \ - legacy/erl_eterm.h legacy/portability.h -$(ST_OBJDIR)/erl_format.o: legacy/erl_format.c ../include/erl_interface.h \ - ../include/ei.h legacy/erl_eterm.h legacy/portability.h \ - legacy/erl_malloc.h legacy/erl_error.h legacy/erl_internal.h -$(ST_OBJDIR)/erl_malloc.o: legacy/erl_malloc.c ../include/erl_interface.h \ - ../include/ei.h legacy/erl_fix_alloc.h legacy/erl_malloc.h \ - legacy/erl_internal.h legacy/erl_eterm.h legacy/portability.h \ - misc/ei_malloc.h -$(ST_OBJDIR)/erl_marshal.o: legacy/erl_marshal.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_marshal.h \ - legacy/erl_eterm.h legacy/portability.h legacy/erl_malloc.h \ - legacy/erl_error.h legacy/erl_internal.h misc/eiext.h misc/putget.h -$(ST_OBJDIR)/erl_timeout.o: legacy/erl_timeout.c $(TARGET)/config.h \ - legacy/erl_timeout.h -$(ST_OBJDIR)/global_names.o: legacy/global_names.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h -$(ST_OBJDIR)/global_register.o: legacy/global_register.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h ../include/erl_interface.h -$(ST_OBJDIR)/global_unregister.o: legacy/global_unregister.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h -$(ST_OBJDIR)/global_whereis.o: legacy/global_whereis.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h - -$(MT_OBJDIR)/ei_connect.o: connect/ei_connect.c $(TARGET)/config.h \ - misc/eidef.h ../include/ei.h misc/eiext.h misc/ei_portio.h \ - misc/ei_internal.h connect/ei_connect_int.h misc/ei_locking.h \ - connect/eisend.h connect/eirecv.h misc/eimd5.h misc/putget.h \ - connect/ei_resolve.h epmd/ei_epmd.h -$(MT_OBJDIR)/ei_resolve.o: connect/ei_resolve.c $(TARGET)/config.h \ - misc/eidef.h ../include/ei.h connect/ei_resolve.h misc/ei_locking.h -$(MT_OBJDIR)/eirecv.o: connect/eirecv.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eirecv.h misc/ei_portio.h \ - misc/ei_internal.h misc/putget.h misc/ei_trace.h misc/show_msg.h -$(MT_OBJDIR)/send.o: connect/send.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eisend.h misc/putget.h \ - connect/ei_connect_int.h misc/ei_internal.h misc/ei_trace.h \ - misc/show_msg.h -$(MT_OBJDIR)/send_exit.o: connect/send_exit.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/ei_connect_int.h misc/ei_trace.h \ - misc/ei_internal.h misc/putget.h misc/show_msg.h -$(MT_OBJDIR)/send_reg.o: connect/send_reg.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eisend.h misc/putget.h \ - connect/ei_connect_int.h misc/ei_internal.h misc/ei_trace.h \ - misc/show_msg.h -$(MT_OBJDIR)/decode_atom.o: decode/decode_atom.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_big.o: decode/decode_big.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_bignum.o: decode/decode_bignum.c $(TARGET)/config.h -$(MT_OBJDIR)/decode_binary.o: decode/decode_binary.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_boolean.o: decode/decode_boolean.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_char.o: decode/decode_char.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_double.o: decode/decode_double.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_fun.o: decode/decode_fun.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_malloc.h decode/decode_skip.h misc/putget.h -$(MT_OBJDIR)/decode_intlist.o: decode/decode_intlist.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_list_header.o: decode/decode_list_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_long.o: decode/decode_long.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_pid.o: decode/decode_pid.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_port.o: decode/decode_port.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_ref.o: decode/decode_ref.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_skip.o: decode/decode_skip.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - decode/decode_skip.h -$(MT_OBJDIR)/decode_string.o: decode/decode_string.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_trace.o: decode/decode_trace.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/putget.h -$(MT_OBJDIR)/decode_tuple_header.o: decode/decode_tuple_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_ulong.o: decode/decode_ulong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_version.o: decode/decode_version.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_longlong.o: decode/decode_longlong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/decode_ulonglong.o: decode/decode_ulonglong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_atom.o: encode/encode_atom.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_bignum.o: encode/encode_bignum.c $(TARGET)/config.h -$(MT_OBJDIR)/encode_binary.o: encode/encode_binary.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_boolean.o: encode/encode_boolean.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_char.o: encode/encode_char.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_double.o: encode/encode_double.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_fun.o: encode/encode_fun.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_list_header.o: encode/encode_list_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_long.o: encode/encode_long.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_pid.o: encode/encode_pid.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_port.o: encode/encode_port.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_ref.o: encode/encode_ref.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_string.o: encode/encode_string.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_trace.o: encode/encode_trace.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/putget.h -$(MT_OBJDIR)/encode_tuple_header.o: encode/encode_tuple_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_ulong.o: encode/encode_ulong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_version.o: encode/encode_version.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MT_OBJDIR)/encode_longlong.o: encode/encode_longlong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h -$(MT_OBJDIR)/encode_ulonglong.o: encode/encode_ulonglong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h -$(MT_OBJDIR)/epmd_port.o: epmd/epmd_port.c misc/ei_internal.h epmd/ei_epmd.h \ - misc/putget.h -$(MT_OBJDIR)/epmd_publish.o: epmd/epmd_publish.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_internal.h \ - misc/putget.h ../include/erl_interface.h epmd/ei_epmd.h -$(MT_OBJDIR)/epmd_unpublish.o: epmd/epmd_unpublish.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_internal.h \ - misc/putget.h ../include/erl_interface.h epmd/ei_epmd.h -$(MT_OBJDIR)/ei_decode_term.o: misc/ei_decode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_decode_term.h misc/putget.h -$(MT_OBJDIR)/ei_format.o: misc/ei_format.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/ei_malloc.h misc/ei_format.h -$(MT_OBJDIR)/ei_locking.o: misc/ei_locking.c $(TARGET)/config.h \ - misc/ei_malloc.h misc/ei_locking.h -$(MT_OBJDIR)/ei_malloc.o: misc/ei_malloc.c misc/ei_malloc.h -$(MT_OBJDIR)/ei_portio.o: misc/ei_portio.c misc/ei_portio.h misc/ei_internal.h -$(MT_OBJDIR)/ei_printterm.o: misc/ei_printterm.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_printterm.h misc/ei_malloc.h -$(MT_OBJDIR)/ei_pthreads.o: misc/ei_pthreads.c $(TARGET)/config.h \ - ../include/ei.h misc/ei_locking.h -$(MT_OBJDIR)/ei_trace.o: misc/ei_trace.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/ei_trace.h -$(MT_OBJDIR)/ei_x_encode.o: misc/ei_x_encode.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_x_encode.h \ - misc/ei_malloc.h -$(MT_OBJDIR)/eimd5.o: misc/eimd5.c misc/eimd5.h -$(MT_OBJDIR)/get_type.o: misc/get_type.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h misc/putget.h -$(MT_OBJDIR)/show_msg.o: misc/show_msg.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h misc/putget.h misc/ei_printterm.h \ - misc/ei_internal.h misc/show_msg.h -$(MT_OBJDIR)/ei_compat.o: misc/ei_compat.c ../include/ei.h misc/ei_internal.h -$(MT_OBJDIR)/hash_dohash.o: registry/hash_dohash.c registry/hash.h ../include/ei.h -$(MT_OBJDIR)/hash_foreach.o: registry/hash_foreach.c registry/hash.h ../include/ei.h -$(MT_OBJDIR)/hash_freetab.o: registry/hash_freetab.c registry/hash.h ../include/ei.h -$(MT_OBJDIR)/hash_insert.o: registry/hash_insert.c registry/hash.h ../include/ei.h -$(MT_OBJDIR)/hash_isprime.o: registry/hash_isprime.c registry/hash.h ../include/ei.h -$(MT_OBJDIR)/hash_lookup.o: registry/hash_lookup.c registry/hash.h ../include/ei.h -$(MT_OBJDIR)/hash_newtab.o: registry/hash_newtab.c registry/hash.h ../include/ei.h -$(MT_OBJDIR)/hash_remove.o: registry/hash_remove.c registry/hash.h ../include/ei.h -$(MT_OBJDIR)/hash_resize.o: registry/hash_resize.c registry/hash.h ../include/ei.h -$(MT_OBJDIR)/hash_rlookup.o: registry/hash_rlookup.c registry/hash.h ../include/ei.h -$(MT_OBJDIR)/reg_close.o: registry/reg_close.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_delete.o: registry/reg_delete.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_dirty.o: registry/reg_dirty.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_dump.o: registry/reg_dump.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - registry/reg.h registry/hash.h connect/eisend.h connect/eirecv.h \ - connect/ei_connect_int.h -$(MT_OBJDIR)/reg_free.o: registry/reg_free.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_get.o: registry/reg_get.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_getf.o: registry/reg_getf.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_geti.o: registry/reg_geti.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_getp.o: registry/reg_getp.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_gets.o: registry/reg_gets.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_make.o: registry/reg_make.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_open.o: registry/reg_open.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_purge.o: registry/reg_purge.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_resize.o: registry/reg_resize.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_restore.o: registry/reg_restore.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - registry/reg.h registry/hash.h connect/eisend.h connect/eirecv.h \ - connect/ei_connect_int.h -$(MT_OBJDIR)/reg_set.o: registry/reg_set.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_setf.o: registry/reg_setf.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_seti.o: registry/reg_seti.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_setp.o: registry/reg_setp.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_sets.o: registry/reg_sets.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_stat.o: registry/reg_stat.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/reg_tabstat.o: registry/reg_tabstat.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MT_OBJDIR)/decode_term.o: legacy/decode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h ../include/erl_interface.h -$(MT_OBJDIR)/encode_term.o: legacy/encode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h ../include/erl_interface.h \ - legacy/erl_marshal.h legacy/erl_eterm.h legacy/portability.h -$(MT_OBJDIR)/erl_connect.o: legacy/erl_connect.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_config.h \ - legacy/erl_connect.h legacy/erl_eterm.h legacy/portability.h \ - legacy/erl_malloc.h misc/putget.h connect/ei_connect_int.h \ - misc/ei_locking.h epmd/ei_epmd.h misc/ei_internal.h -$(MT_OBJDIR)/erl_error.o: legacy/erl_error.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_error.h -$(MT_OBJDIR)/erl_eterm.o: legacy/erl_eterm.c misc/ei_locking.h \ - $(TARGET)/config.h connect/ei_resolve.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_eterm.h \ - legacy/portability.h legacy/erl_malloc.h legacy/erl_marshal.h \ - legacy/erl_error.h legacy/erl_internal.h misc/ei_internal.h -$(MT_OBJDIR)/erl_fix_alloc.o: legacy/erl_fix_alloc.c $(TARGET)/config.h \ - misc/ei_locking.h ../include/erl_interface.h ../include/ei.h \ - legacy/erl_error.h legacy/erl_malloc.h legacy/erl_fix_alloc.h \ - legacy/erl_eterm.h legacy/portability.h -$(MT_OBJDIR)/erl_format.o: legacy/erl_format.c ../include/erl_interface.h \ - ../include/ei.h legacy/erl_eterm.h legacy/portability.h \ - legacy/erl_malloc.h legacy/erl_error.h legacy/erl_internal.h -$(MT_OBJDIR)/erl_malloc.o: legacy/erl_malloc.c ../include/erl_interface.h \ - ../include/ei.h legacy/erl_fix_alloc.h legacy/erl_malloc.h \ - legacy/erl_internal.h legacy/erl_eterm.h legacy/portability.h \ - misc/ei_malloc.h -$(MT_OBJDIR)/erl_marshal.o: legacy/erl_marshal.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_marshal.h \ - legacy/erl_eterm.h legacy/portability.h legacy/erl_malloc.h \ - legacy/erl_error.h legacy/erl_internal.h misc/eiext.h misc/putget.h -$(MT_OBJDIR)/erl_timeout.o: legacy/erl_timeout.c $(TARGET)/config.h \ - legacy/erl_timeout.h -$(MT_OBJDIR)/global_names.o: legacy/global_names.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h -$(MT_OBJDIR)/global_register.o: legacy/global_register.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h ../include/erl_interface.h -$(MT_OBJDIR)/global_unregister.o: legacy/global_unregister.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h -$(MT_OBJDIR)/global_whereis.o: legacy/global_whereis.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h - -$(MD_OBJDIR)/ei_connect.o: connect/ei_connect.c $(TARGET)/config.h \ - misc/eidef.h ../include/ei.h misc/eiext.h misc/ei_portio.h \ - misc/ei_internal.h connect/ei_connect_int.h misc/ei_locking.h \ - connect/eisend.h connect/eirecv.h misc/eimd5.h misc/putget.h \ - connect/ei_resolve.h epmd/ei_epmd.h -$(MD_OBJDIR)/ei_resolve.o: connect/ei_resolve.c $(TARGET)/config.h \ - misc/eidef.h ../include/ei.h connect/ei_resolve.h misc/ei_locking.h -$(MD_OBJDIR)/eirecv.o: connect/eirecv.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eirecv.h misc/ei_portio.h \ - misc/ei_internal.h misc/putget.h misc/ei_trace.h misc/show_msg.h -$(MD_OBJDIR)/send.o: connect/send.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eisend.h misc/putget.h \ - connect/ei_connect_int.h misc/ei_internal.h misc/ei_trace.h \ - misc/show_msg.h -$(MD_OBJDIR)/send_exit.o: connect/send_exit.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/ei_connect_int.h misc/ei_trace.h \ - misc/ei_internal.h misc/putget.h misc/show_msg.h -$(MD_OBJDIR)/send_reg.o: connect/send_reg.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eisend.h misc/putget.h \ - connect/ei_connect_int.h misc/ei_internal.h misc/ei_trace.h \ - misc/show_msg.h -$(MD_OBJDIR)/decode_atom.o: decode/decode_atom.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_big.o: decode/decode_big.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_bignum.o: decode/decode_bignum.c $(TARGET)/config.h -$(MD_OBJDIR)/decode_binary.o: decode/decode_binary.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_boolean.o: decode/decode_boolean.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_char.o: decode/decode_char.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_double.o: decode/decode_double.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_fun.o: decode/decode_fun.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_malloc.h decode/decode_skip.h misc/putget.h -$(MD_OBJDIR)/decode_intlist.o: decode/decode_intlist.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_list_header.o: decode/decode_list_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_long.o: decode/decode_long.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_pid.o: decode/decode_pid.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_port.o: decode/decode_port.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_ref.o: decode/decode_ref.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_skip.o: decode/decode_skip.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - decode/decode_skip.h -$(MD_OBJDIR)/decode_string.o: decode/decode_string.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_trace.o: decode/decode_trace.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/putget.h -$(MD_OBJDIR)/decode_tuple_header.o: decode/decode_tuple_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_ulong.o: decode/decode_ulong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_version.o: decode/decode_version.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_longlong.o: decode/decode_longlong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/decode_ulonglong.o: decode/decode_ulonglong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_atom.o: encode/encode_atom.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_bignum.o: encode/encode_bignum.c $(TARGET)/config.h -$(MD_OBJDIR)/encode_binary.o: encode/encode_binary.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_boolean.o: encode/encode_boolean.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_char.o: encode/encode_char.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_double.o: encode/encode_double.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_fun.o: encode/encode_fun.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_list_header.o: encode/encode_list_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_long.o: encode/encode_long.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_pid.o: encode/encode_pid.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_port.o: encode/encode_port.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_ref.o: encode/encode_ref.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_string.o: encode/encode_string.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_trace.o: encode/encode_trace.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/putget.h -$(MD_OBJDIR)/encode_tuple_header.o: encode/encode_tuple_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_ulong.o: encode/encode_ulong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_version.o: encode/encode_version.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MD_OBJDIR)/encode_longlong.o: encode/encode_longlong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h -$(MD_OBJDIR)/encode_ulonglong.o: encode/encode_ulonglong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h -$(MD_OBJDIR)/epmd_port.o: epmd/epmd_port.c misc/ei_internal.h epmd/ei_epmd.h \ - misc/putget.h -$(MD_OBJDIR)/epmd_publish.o: epmd/epmd_publish.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_internal.h \ - misc/putget.h ../include/erl_interface.h epmd/ei_epmd.h -$(MD_OBJDIR)/epmd_unpublish.o: epmd/epmd_unpublish.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_internal.h \ - misc/putget.h ../include/erl_interface.h epmd/ei_epmd.h -$(MD_OBJDIR)/ei_decode_term.o: misc/ei_decode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_decode_term.h misc/putget.h -$(MD_OBJDIR)/ei_format.o: misc/ei_format.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/ei_malloc.h misc/ei_format.h -$(MD_OBJDIR)/ei_locking.o: misc/ei_locking.c $(TARGET)/config.h \ - misc/ei_malloc.h misc/ei_locking.h -$(MD_OBJDIR)/ei_malloc.o: misc/ei_malloc.c misc/ei_malloc.h -$(MD_OBJDIR)/ei_portio.o: misc/ei_portio.c misc/ei_portio.h misc/ei_internal.h -$(MD_OBJDIR)/ei_printterm.o: misc/ei_printterm.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_printterm.h misc/ei_malloc.h -$(MD_OBJDIR)/ei_pthreads.o: misc/ei_pthreads.c $(TARGET)/config.h \ - ../include/ei.h misc/ei_locking.h -$(MD_OBJDIR)/ei_trace.o: misc/ei_trace.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/ei_trace.h -$(MD_OBJDIR)/ei_x_encode.o: misc/ei_x_encode.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_x_encode.h \ - misc/ei_malloc.h -$(MD_OBJDIR)/eimd5.o: misc/eimd5.c misc/eimd5.h -$(MD_OBJDIR)/get_type.o: misc/get_type.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h misc/putget.h -$(MD_OBJDIR)/show_msg.o: misc/show_msg.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h misc/putget.h misc/ei_printterm.h \ - misc/ei_internal.h misc/show_msg.h -$(MD_OBJDIR)/ei_compat.o: misc/ei_compat.c ../include/ei.h misc/ei_internal.h -$(MD_OBJDIR)/hash_dohash.o: registry/hash_dohash.c registry/hash.h ../include/ei.h -$(MD_OBJDIR)/hash_foreach.o: registry/hash_foreach.c registry/hash.h ../include/ei.h -$(MD_OBJDIR)/hash_freetab.o: registry/hash_freetab.c registry/hash.h ../include/ei.h -$(MD_OBJDIR)/hash_insert.o: registry/hash_insert.c registry/hash.h ../include/ei.h -$(MD_OBJDIR)/hash_isprime.o: registry/hash_isprime.c registry/hash.h ../include/ei.h -$(MD_OBJDIR)/hash_lookup.o: registry/hash_lookup.c registry/hash.h ../include/ei.h -$(MD_OBJDIR)/hash_newtab.o: registry/hash_newtab.c registry/hash.h ../include/ei.h -$(MD_OBJDIR)/hash_remove.o: registry/hash_remove.c registry/hash.h ../include/ei.h -$(MD_OBJDIR)/hash_resize.o: registry/hash_resize.c registry/hash.h ../include/ei.h -$(MD_OBJDIR)/hash_rlookup.o: registry/hash_rlookup.c registry/hash.h ../include/ei.h -$(MD_OBJDIR)/reg_close.o: registry/reg_close.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_delete.o: registry/reg_delete.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_dirty.o: registry/reg_dirty.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_dump.o: registry/reg_dump.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - registry/reg.h registry/hash.h connect/eisend.h connect/eirecv.h \ - connect/ei_connect_int.h -$(MD_OBJDIR)/reg_free.o: registry/reg_free.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_get.o: registry/reg_get.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_getf.o: registry/reg_getf.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_geti.o: registry/reg_geti.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_getp.o: registry/reg_getp.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_gets.o: registry/reg_gets.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_make.o: registry/reg_make.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_open.o: registry/reg_open.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_purge.o: registry/reg_purge.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_resize.o: registry/reg_resize.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_restore.o: registry/reg_restore.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - registry/reg.h registry/hash.h connect/eisend.h connect/eirecv.h \ - connect/ei_connect_int.h -$(MD_OBJDIR)/reg_set.o: registry/reg_set.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_setf.o: registry/reg_setf.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_seti.o: registry/reg_seti.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_setp.o: registry/reg_setp.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_sets.o: registry/reg_sets.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_stat.o: registry/reg_stat.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/reg_tabstat.o: registry/reg_tabstat.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MD_OBJDIR)/decode_term.o: legacy/decode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h ../include/erl_interface.h -$(MD_OBJDIR)/encode_term.o: legacy/encode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h ../include/erl_interface.h \ - legacy/erl_marshal.h legacy/erl_eterm.h legacy/portability.h -$(MD_OBJDIR)/erl_connect.o: legacy/erl_connect.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_config.h \ - legacy/erl_connect.h legacy/erl_eterm.h legacy/portability.h \ - legacy/erl_malloc.h misc/putget.h connect/ei_connect_int.h \ - misc/ei_locking.h epmd/ei_epmd.h misc/ei_internal.h -$(MD_OBJDIR)/erl_error.o: legacy/erl_error.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_error.h -$(MD_OBJDIR)/erl_eterm.o: legacy/erl_eterm.c misc/ei_locking.h \ - $(TARGET)/config.h connect/ei_resolve.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_eterm.h \ - legacy/portability.h legacy/erl_malloc.h legacy/erl_marshal.h \ - legacy/erl_error.h legacy/erl_internal.h misc/ei_internal.h -$(MD_OBJDIR)/erl_fix_alloc.o: legacy/erl_fix_alloc.c $(TARGET)/config.h \ - misc/ei_locking.h ../include/erl_interface.h ../include/ei.h \ - legacy/erl_error.h legacy/erl_malloc.h legacy/erl_fix_alloc.h \ - legacy/erl_eterm.h legacy/portability.h -$(MD_OBJDIR)/erl_format.o: legacy/erl_format.c ../include/erl_interface.h \ - ../include/ei.h legacy/erl_eterm.h legacy/portability.h \ - legacy/erl_malloc.h legacy/erl_error.h legacy/erl_internal.h -$(MD_OBJDIR)/erl_malloc.o: legacy/erl_malloc.c ../include/erl_interface.h \ - ../include/ei.h legacy/erl_fix_alloc.h legacy/erl_malloc.h \ - legacy/erl_internal.h legacy/erl_eterm.h legacy/portability.h \ - misc/ei_malloc.h -$(MD_OBJDIR)/erl_marshal.o: legacy/erl_marshal.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_marshal.h \ - legacy/erl_eterm.h legacy/portability.h legacy/erl_malloc.h \ - legacy/erl_error.h legacy/erl_internal.h misc/eiext.h misc/putget.h -$(MD_OBJDIR)/erl_timeout.o: legacy/erl_timeout.c $(TARGET)/config.h \ - legacy/erl_timeout.h -$(MD_OBJDIR)/global_names.o: legacy/global_names.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h -$(MD_OBJDIR)/global_register.o: legacy/global_register.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h ../include/erl_interface.h -$(MD_OBJDIR)/global_unregister.o: legacy/global_unregister.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h -$(MD_OBJDIR)/global_whereis.o: legacy/global_whereis.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h - -$(MDD_OBJDIR)/ei_connect.o: connect/ei_connect.c $(TARGET)/config.h \ - misc/eidef.h ../include/ei.h misc/eiext.h misc/ei_portio.h \ - misc/ei_internal.h connect/ei_connect_int.h misc/ei_locking.h \ - connect/eisend.h connect/eirecv.h misc/eimd5.h misc/putget.h \ - connect/ei_resolve.h epmd/ei_epmd.h -$(MDD_OBJDIR)/ei_resolve.o: connect/ei_resolve.c $(TARGET)/config.h \ - misc/eidef.h ../include/ei.h connect/ei_resolve.h misc/ei_locking.h -$(MDD_OBJDIR)/eirecv.o: connect/eirecv.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eirecv.h misc/ei_portio.h \ - misc/ei_internal.h misc/putget.h misc/ei_trace.h misc/show_msg.h -$(MDD_OBJDIR)/send.o: connect/send.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eisend.h misc/putget.h \ - connect/ei_connect_int.h misc/ei_internal.h misc/ei_trace.h \ - misc/show_msg.h -$(MDD_OBJDIR)/send_exit.o: connect/send_exit.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/ei_connect_int.h misc/ei_trace.h \ - misc/ei_internal.h misc/putget.h misc/show_msg.h -$(MDD_OBJDIR)/send_reg.o: connect/send_reg.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h connect/eisend.h misc/putget.h \ - connect/ei_connect_int.h misc/ei_internal.h misc/ei_trace.h \ - misc/show_msg.h -$(MDD_OBJDIR)/decode_atom.o: decode/decode_atom.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_big.o: decode/decode_big.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_bignum.o: decode/decode_bignum.c $(TARGET)/config.h -$(MDD_OBJDIR)/decode_binary.o: decode/decode_binary.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_boolean.o: decode/decode_boolean.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_char.o: decode/decode_char.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_double.o: decode/decode_double.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_fun.o: decode/decode_fun.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_malloc.h decode/decode_skip.h misc/putget.h -$(MDD_OBJDIR)/decode_intlist.o: decode/decode_intlist.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_list_header.o: decode/decode_list_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_long.o: decode/decode_long.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_pid.o: decode/decode_pid.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_port.o: decode/decode_port.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_ref.o: decode/decode_ref.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_skip.o: decode/decode_skip.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - decode/decode_skip.h -$(MDD_OBJDIR)/decode_string.o: decode/decode_string.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_trace.o: decode/decode_trace.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/putget.h -$(MDD_OBJDIR)/decode_tuple_header.o: decode/decode_tuple_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_ulong.o: decode/decode_ulong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_version.o: decode/decode_version.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_longlong.o: decode/decode_longlong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/decode_ulonglong.o: decode/decode_ulonglong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_atom.o: encode/encode_atom.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_bignum.o: encode/encode_bignum.c $(TARGET)/config.h -$(MDD_OBJDIR)/encode_binary.o: encode/encode_binary.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_boolean.o: encode/encode_boolean.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_char.o: encode/encode_char.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_double.o: encode/encode_double.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_fun.o: encode/encode_fun.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_list_header.o: encode/encode_list_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_long.o: encode/encode_long.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_pid.o: encode/encode_pid.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_port.o: encode/encode_port.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_ref.o: encode/encode_ref.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_string.o: encode/encode_string.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_trace.o: encode/encode_trace.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/putget.h -$(MDD_OBJDIR)/encode_tuple_header.o: encode/encode_tuple_header.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_ulong.o: encode/encode_ulong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_version.o: encode/encode_version.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h -$(MDD_OBJDIR)/encode_longlong.o: encode/encode_longlong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h -$(MDD_OBJDIR)/encode_ulonglong.o: encode/encode_ulonglong.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h -$(MDD_OBJDIR)/epmd_port.o: epmd/epmd_port.c misc/ei_internal.h epmd/ei_epmd.h \ - misc/putget.h -$(MDD_OBJDIR)/epmd_publish.o: epmd/epmd_publish.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_internal.h \ - misc/putget.h ../include/erl_interface.h epmd/ei_epmd.h -$(MDD_OBJDIR)/epmd_unpublish.o: epmd/epmd_unpublish.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_internal.h \ - misc/putget.h ../include/erl_interface.h epmd/ei_epmd.h -$(MDD_OBJDIR)/ei_decode_term.o: misc/ei_decode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_decode_term.h misc/putget.h -$(MDD_OBJDIR)/ei_format.o: misc/ei_format.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/ei_malloc.h misc/ei_format.h -$(MDD_OBJDIR)/ei_locking.o: misc/ei_locking.c $(TARGET)/config.h \ - misc/ei_malloc.h misc/ei_locking.h -$(MDD_OBJDIR)/ei_malloc.o: misc/ei_malloc.c misc/ei_malloc.h -$(MDD_OBJDIR)/ei_portio.o: misc/ei_portio.c misc/ei_portio.h misc/ei_internal.h -$(MDD_OBJDIR)/ei_printterm.o: misc/ei_printterm.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/ei_printterm.h misc/ei_malloc.h -$(MDD_OBJDIR)/ei_pthreads.o: misc/ei_pthreads.c $(TARGET)/config.h \ - ../include/ei.h misc/ei_locking.h -$(MDD_OBJDIR)/ei_trace.o: misc/ei_trace.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/ei_trace.h -$(MDD_OBJDIR)/ei_x_encode.o: misc/ei_x_encode.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/ei_x_encode.h \ - misc/ei_malloc.h -$(MDD_OBJDIR)/eimd5.o: misc/eimd5.c misc/eimd5.h -$(MDD_OBJDIR)/get_type.o: misc/get_type.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h misc/putget.h -$(MDD_OBJDIR)/show_msg.o: misc/show_msg.c misc/eidef.h $(TARGET)/config.h \ - ../include/ei.h misc/eiext.h misc/putget.h misc/ei_printterm.h \ - misc/ei_internal.h misc/show_msg.h -$(MDD_OBJDIR)/ei_compat.o: misc/ei_compat.c ../include/ei.h misc/ei_internal.h -$(MDD_OBJDIR)/hash_dohash.o: registry/hash_dohash.c registry/hash.h ../include/ei.h -$(MDD_OBJDIR)/hash_foreach.o: registry/hash_foreach.c registry/hash.h ../include/ei.h -$(MDD_OBJDIR)/hash_freetab.o: registry/hash_freetab.c registry/hash.h ../include/ei.h -$(MDD_OBJDIR)/hash_insert.o: registry/hash_insert.c registry/hash.h ../include/ei.h -$(MDD_OBJDIR)/hash_isprime.o: registry/hash_isprime.c registry/hash.h ../include/ei.h -$(MDD_OBJDIR)/hash_lookup.o: registry/hash_lookup.c registry/hash.h ../include/ei.h -$(MDD_OBJDIR)/hash_newtab.o: registry/hash_newtab.c registry/hash.h ../include/ei.h -$(MDD_OBJDIR)/hash_remove.o: registry/hash_remove.c registry/hash.h ../include/ei.h -$(MDD_OBJDIR)/hash_resize.o: registry/hash_resize.c registry/hash.h ../include/ei.h -$(MDD_OBJDIR)/hash_rlookup.o: registry/hash_rlookup.c registry/hash.h ../include/ei.h -$(MDD_OBJDIR)/reg_close.o: registry/reg_close.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_delete.o: registry/reg_delete.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_dirty.o: registry/reg_dirty.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_dump.o: registry/reg_dump.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - registry/reg.h registry/hash.h connect/eisend.h connect/eirecv.h \ - connect/ei_connect_int.h -$(MDD_OBJDIR)/reg_free.o: registry/reg_free.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_get.o: registry/reg_get.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_getf.o: registry/reg_getf.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_geti.o: registry/reg_geti.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_getp.o: registry/reg_getp.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_gets.o: registry/reg_gets.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_make.o: registry/reg_make.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_open.o: registry/reg_open.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_purge.o: registry/reg_purge.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_resize.o: registry/reg_resize.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_restore.o: registry/reg_restore.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - registry/reg.h registry/hash.h connect/eisend.h connect/eirecv.h \ - connect/ei_connect_int.h -$(MDD_OBJDIR)/reg_set.o: registry/reg_set.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_setf.o: registry/reg_setf.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_seti.o: registry/reg_seti.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_setp.o: registry/reg_setp.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_sets.o: registry/reg_sets.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_stat.o: registry/reg_stat.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/reg_tabstat.o: registry/reg_tabstat.c registry/reg.h ../include/ei.h \ - registry/hash.h -$(MDD_OBJDIR)/decode_term.o: legacy/decode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h ../include/erl_interface.h -$(MDD_OBJDIR)/encode_term.o: legacy/encode_term.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - misc/putget.h misc/ei_x_encode.h ../include/erl_interface.h \ - legacy/erl_marshal.h legacy/erl_eterm.h legacy/portability.h -$(MDD_OBJDIR)/erl_connect.o: legacy/erl_connect.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_config.h \ - legacy/erl_connect.h legacy/erl_eterm.h legacy/portability.h \ - legacy/erl_malloc.h misc/putget.h connect/ei_connect_int.h \ - misc/ei_locking.h epmd/ei_epmd.h misc/ei_internal.h -$(MDD_OBJDIR)/erl_error.o: legacy/erl_error.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_error.h -$(MDD_OBJDIR)/erl_eterm.o: legacy/erl_eterm.c misc/ei_locking.h \ - $(TARGET)/config.h connect/ei_resolve.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_eterm.h \ - legacy/portability.h legacy/erl_malloc.h legacy/erl_marshal.h \ - legacy/erl_error.h legacy/erl_internal.h misc/ei_internal.h -$(MDD_OBJDIR)/erl_fix_alloc.o: legacy/erl_fix_alloc.c $(TARGET)/config.h \ - misc/ei_locking.h ../include/erl_interface.h ../include/ei.h \ - legacy/erl_error.h legacy/erl_malloc.h legacy/erl_fix_alloc.h \ - legacy/erl_eterm.h legacy/portability.h -$(MDD_OBJDIR)/erl_format.o: legacy/erl_format.c ../include/erl_interface.h \ - ../include/ei.h legacy/erl_eterm.h legacy/portability.h \ - legacy/erl_malloc.h legacy/erl_error.h legacy/erl_internal.h -$(MDD_OBJDIR)/erl_malloc.o: legacy/erl_malloc.c ../include/erl_interface.h \ - ../include/ei.h legacy/erl_fix_alloc.h legacy/erl_malloc.h \ - legacy/erl_internal.h legacy/erl_eterm.h legacy/portability.h \ - misc/ei_malloc.h -$(MDD_OBJDIR)/erl_marshal.o: legacy/erl_marshal.c $(TARGET)/config.h \ - ../include/erl_interface.h ../include/ei.h legacy/erl_marshal.h \ - legacy/erl_eterm.h legacy/portability.h legacy/erl_malloc.h \ - legacy/erl_error.h legacy/erl_internal.h misc/eiext.h misc/putget.h -$(MDD_OBJDIR)/erl_timeout.o: legacy/erl_timeout.c $(TARGET)/config.h \ - legacy/erl_timeout.h -$(MDD_OBJDIR)/global_names.o: legacy/global_names.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h -$(MDD_OBJDIR)/global_register.o: legacy/global_register.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h ../include/erl_interface.h -$(MDD_OBJDIR)/global_unregister.o: legacy/global_unregister.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h -$(MDD_OBJDIR)/global_whereis.o: legacy/global_whereis.c misc/eidef.h \ - $(TARGET)/config.h ../include/ei.h misc/eiext.h \ - connect/eisend.h connect/eirecv.h connect/ei_connect_int.h \ - ../include/erl_interface.h legacy/erl_connect.h - diff --git a/lib/erl_interface/src/encode/encode_pid.c b/lib/erl_interface/src/encode/encode_pid.c index 0dfdb16372..d14746b40f 100644 --- a/lib/erl_interface/src/encode/encode_pid.c +++ b/lib/erl_interface/src/encode/encode_pid.c @@ -25,6 +25,7 @@ int ei_encode_pid(char *buf, int *index, const erlang_pid *p) { char* s = buf + *index; + const char tag = (p->creation > 3) ? ERL_NEW_PID_EXT : ERL_PID_EXT; ++(*index); /* skip ERL_PID_EXT */ if (ei_encode_atom_len_as(buf, index, p->node, strlen(p->node), @@ -32,17 +33,21 @@ int ei_encode_pid(char *buf, int *index, const erlang_pid *p) return -1; if (buf) { - put8(s, ERL_NEW_PID_EXT); + put8(s, tag); s = buf + *index; /* now the integers */ put32be(s,p->num & 0x7fff); /* 15 bits */ put32be(s,p->serial & 0x1fff); /* 13 bits */ - put32be(s, p->creation); /* 32 bits */ + if (tag == ERL_PID_EXT) { + put8(s,(p->creation & 0x03)); /* 2 bits */ + } else { + put32be(s, p->creation); /* 32 bits */ + } } - *index += 4 + 4 + 4; + *index += 4 + 4 + (tag == ERL_PID_EXT ? 1 : 4); return 0; } diff --git a/lib/erl_interface/src/encode/encode_port.c b/lib/erl_interface/src/encode/encode_port.c index 0fb4018db1..eb464380c0 100644 --- a/lib/erl_interface/src/encode/encode_port.c +++ b/lib/erl_interface/src/encode/encode_port.c @@ -25,6 +25,7 @@ int ei_encode_port(char *buf, int *index, const erlang_port *p) { char *s = buf + *index; + const char tag = p->creation > 3 ? ERL_NEW_PORT_EXT : ERL_PORT_EXT; ++(*index); /* skip ERL_PORT_EXT */ if (ei_encode_atom_len_as(buf, index, p->node, strlen(p->node), ERLANG_UTF8, @@ -32,15 +33,19 @@ int ei_encode_port(char *buf, int *index, const erlang_port *p) return -1; } if (buf) { - put8(s, ERL_NEW_PORT_EXT); + put8(s, tag); s = buf + *index; /* now the integers */ put32be(s,p->id & 0x0fffffff /* 28 bits */); - put32be(s, p->creation); + if (tag == ERL_PORT_EXT) { + put8(s,(p->creation & 0x03)); + } else { + put32be(s, p->creation); + } } - *index += 4 + 4; + *index += 4 + (tag == ERL_PORT_EXT ? 1 : 4); return 0; } diff --git a/lib/erl_interface/src/encode/encode_ref.c b/lib/erl_interface/src/encode/encode_ref.c index 8c2e0a25f7..5ccfc32c6d 100644 --- a/lib/erl_interface/src/encode/encode_ref.c +++ b/lib/erl_interface/src/encode/encode_ref.c @@ -24,6 +24,7 @@ int ei_encode_ref(char *buf, int *index, const erlang_ref *p) { + const char tag = (p->creation > 3) ? ERL_NEWER_REFERENCE_EXT : ERL_NEW_REFERENCE_EXT; char *s = buf + *index; int i; @@ -36,7 +37,7 @@ int ei_encode_ref(char *buf, int *index, const erlang_ref *p) /* Always encode as an extended reference; all participating parties are now expected to be able to decode extended references. */ if (buf) { - put8(s, ERL_NEWER_REFERENCE_EXT); + put8(s, tag); /* first, number of integers */ put16be(s, p->len); @@ -45,12 +46,15 @@ int ei_encode_ref(char *buf, int *index, const erlang_ref *p) s = buf + *index; /* now the integers */ - put32be(s, p->creation); + if (tag == ERL_NEW_REFERENCE_EXT) + put8(s,(p->creation & 0x03)); + else + put32be(s, p->creation); for (i = 0; i < p->len; i++) put32be(s,p->n[i]); } - *index += p->len*4 + 4; + *index += p->len*4 + (tag == ERL_NEW_REFERENCE_EXT ? 1 : 4); return 0; } diff --git a/lib/erl_interface/src/epmd/ei_epmd.h b/lib/erl_interface/src/epmd/ei_epmd.h index f72c354e32..ac153b6e66 100644 --- a/lib/erl_interface/src/epmd/ei_epmd.h +++ b/lib/erl_interface/src/epmd/ei_epmd.h @@ -25,8 +25,8 @@ #endif #ifndef EI_DIST_HIGH -#define EI_DIST_HIGH 6 /* OTP 22 and later */ -#define EI_DIST_LOW 5 /* OTP R4 - 21 */ +#define EI_DIST_HIGH 5 /* R4 and later */ +#define EI_DIST_LOW 1 /* R3 and earlier */ #endif #ifndef EPMD_PORT @@ -45,7 +45,6 @@ #ifndef EI_EPMD_ALIVE2_REQ #define EI_EPMD_ALIVE2_REQ 120 #define EI_EPMD_ALIVE2_RESP 121 -#define EI_EPMD_ALIVE2_X_RESP 118 #define EI_EPMD_PORT2_REQ 122 #define EI_EPMD_PORT2_RESP 119 #define EI_EPMD_STOP_REQ 's' diff --git a/lib/erl_interface/src/epmd/epmd_publish.c b/lib/erl_interface/src/epmd/epmd_publish.c index ef8a5d6b70..20b8e867e8 100644 --- a/lib/erl_interface/src/epmd/epmd_publish.c +++ b/lib/erl_interface/src/epmd/epmd_publish.c @@ -68,8 +68,7 @@ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms) int nlen = strlen(alive); int len = elen + nlen + 13; /* hard coded: be careful! */ int n; - int err, response, res; - unsigned creation; + int err, res, creation; ssize_t dlen; unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; @@ -125,10 +124,8 @@ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms) /* Don't close fd here! It keeps us registered with epmd */ s = buf; - response = get8(s); - if (response != EI_EPMD_ALIVE2_RESP && - response != EI_EPMD_ALIVE2_X_RESP) { - EI_TRACE_ERR1("ei_epmd_r4_publish","<- unknown (%d)",response); + if (((res=get8(s)) != EI_EPMD_ALIVE2_RESP)) { /* response */ + EI_TRACE_ERR1("ei_epmd_r4_publish","<- unknown (%d)",res); EI_TRACE_ERR0("ei_epmd_r4_publish","-> CLOSE"); ei_close__(fd); erl_errno = EIO; @@ -144,21 +141,18 @@ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms) return -1; } - if (response == EI_EPMD_ALIVE2_RESP) - creation = get16be(s); - else /* EI_EPMD_ALIVE2_X_RESP */ - creation = get32be(s); + creation = get16be(s); EI_TRACE_CONN2("ei_epmd_r4_publish", - " result=%d (ok) creation=%u",res,creation); + " result=%d (ok) creation=%d",res,creation); - /* - * Would be nice to somehow use the nice "unique" creation value - * received here from epmd instead of using the crappy one - * passed (already) to ei_connect_init. - */ + /* probably should save fd so we can close it later... */ + /* epmd_saveconn(OPEN,fd,alive); */ - /* return the descriptor */ + /* return the creation number, for no good reason */ + /* return creation;*/ + + /* no - return the descriptor */ return fd; } diff --git a/lib/erl_interface/src/prog/erl_call.c b/lib/erl_interface/src/prog/erl_call.c index dce2ecdec2..ab91157035 100644 --- a/lib/erl_interface/src/prog/erl_call.c +++ b/lib/erl_interface/src/prog/erl_call.c @@ -292,7 +292,8 @@ int erl_call(int argc, char **argv) flags.cookie = NULL; } - creation = time(NULL) + 1; /* "random" */ + /* FIXME decide how many bits etc or leave to connect_xinit? */ + creation = (time(NULL) % 3) + 1; /* "random" */ if (flags.hidden == NULL) { /* As default we are c17@gethostname */ diff --git a/lib/erl_interface/test/erl_eterm_SUITE.erl b/lib/erl_interface/test/erl_eterm_SUITE.erl index 4605293c74..77910a9fc7 100644 --- a/lib/erl_interface/test/erl_eterm_SUITE.erl +++ b/lib/erl_interface/test/erl_eterm_SUITE.erl @@ -73,10 +73,6 @@ -import(runner, [get_term/1]). --define(REFERENCE_EXT, $e). --define(NEW_REFERENCE_EXT, $r). --define(NEWER_REFERENCE_EXT, $Z). - %% This test suite controls the running of the C language functions %% in eterm_test.c and print_term.c. @@ -1030,11 +1026,9 @@ cnode_1(Config) when is_list(Config) -> check_ref(Ref) -> case bin_ext_type(Ref) of - ?REFERENCE_EXT -> + 101 -> ct:fail(oldref); - ?NEW_REFERENCE_EXT -> - ok; - ?NEWER_REFERENCE_EXT -> + 114 -> ok; Type -> ct:fail({type, Type}) diff --git a/lib/hipe/doc/src/hipe_app.xml b/lib/hipe/doc/src/hipe_app.xml index 480290cd9e..61d92fdffe 100644 --- a/lib/hipe/doc/src/hipe_app.xml +++ b/lib/hipe/doc/src/hipe_app.xml @@ -64,9 +64,7 @@ <taglist> <tag>Binary matching</tag> <item><p>The HiPE compiler will crash on modules containing binary - matching unless they have been compiled with the <c>+no_bsm3</c> flag. - Note that this will disable all related optimizations done by the BEAM - compiler.</p> + matching.</p> </item> <tag>Stack traces</tag> 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/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java index 3abdf9535f..9cbd735751 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java @@ -27,6 +27,7 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> // don't change this! private static final long serialVersionUID = 1664394142301803659L; + private final int tag; private final String node; private final int id; private final int serial; @@ -44,6 +45,7 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> public OtpErlangPid(final OtpLocalNode self) { final OtpErlangPid p = self.createPid(); + tag = p.tag; id = p.id; serial = p.serial; creation = p.creation; @@ -65,6 +67,7 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> throws OtpErlangDecodeException { final OtpErlangPid p = buf.read_pid(); + tag = p.tag; node = p.node(); id = p.id(); serial = p.serial(); @@ -115,6 +118,7 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> */ protected OtpErlangPid(final int tag, final String node, final int id, final int serial, final int creation) { + this.tag = tag; this.node = node; if (tag == OtpExternal.pidTag) { this.id = id & 0x7fff; // 15 bits @@ -129,7 +133,7 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> } protected int tag() { - return OtpExternal.newPidTag; + return tag; } /** diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java index c8648d7aa3..79b5d2736c 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java @@ -26,6 +26,7 @@ public class OtpErlangPort extends OtpErlangObject { // don't change this! private static final long serialVersionUID = 4037115468007644704L; + private final int tag; private final String node; private final int id; private final int creation; @@ -42,6 +43,7 @@ public class OtpErlangPort extends OtpErlangObject { private OtpErlangPort(final OtpSelf self) { final OtpErlangPort p = self.createPort(); + tag = p.tag; id = p.id; creation = p.creation; node = p.node; @@ -62,6 +64,7 @@ public class OtpErlangPort extends OtpErlangObject { throws OtpErlangDecodeException { final OtpErlangPort p = buf.read_port(); + tag = p.tag; node = p.node(); id = p.id(); creation = p.creation(); @@ -102,6 +105,7 @@ public class OtpErlangPort extends OtpErlangObject { */ public OtpErlangPort(final int tag, final String node, final int id, final int creation) { + this.tag = tag; this.node = node; if (tag == OtpExternal.portTag) { this.id = id & 0xfffffff; // 28 bits @@ -114,7 +118,7 @@ public class OtpErlangPort extends OtpErlangObject { } protected int tag() { - return OtpExternal.newPortTag; + return tag; } /** diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java index 2bf8d9a56b..2165397013 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java @@ -28,6 +28,7 @@ public class OtpErlangRef extends OtpErlangObject { // don't change this! private static final long serialVersionUID = -7022666480768586521L; + private final int tag; private final String node; private final int creation; @@ -48,6 +49,7 @@ public class OtpErlangRef extends OtpErlangObject { public OtpErlangRef(final OtpLocalNode self) { final OtpErlangRef r = self.createRef(); + tag = r.tag; ids = r.ids; creation = r.creation; node = r.node; @@ -68,6 +70,7 @@ public class OtpErlangRef extends OtpErlangObject { throws OtpErlangDecodeException { final OtpErlangRef r = buf.read_ref(); + tag = r.tag; node = r.node(); creation = r.creation(); @@ -87,6 +90,7 @@ public class OtpErlangRef extends OtpErlangObject { * another arbitrary number. */ public OtpErlangRef(final String node, final int id, final int creation) { + this.tag = OtpExternal.newRefTag; this.node = node; ids = new int[1]; ids[0] = id & 0x3ffff; // 18 bits @@ -134,6 +138,7 @@ public class OtpErlangRef extends OtpErlangObject { */ public OtpErlangRef(final int tag, final String node, final int[] ids, final int creation) { + this.tag = tag; this.node = node; // use at most 3 words @@ -157,7 +162,7 @@ public class OtpErlangRef extends OtpErlangObject { } protected int tag() { - return OtpExternal.newerRefTag; + return tag; } /** diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java index a3b089c1da..187705a0b5 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java @@ -713,7 +713,7 @@ public class OtpOutputStream extends ByteArrayOutputStream { */ public void write_pid(final String node, final int id, final int serial, final int creation) { - write1(OtpExternal.newPidTag); + write1(OtpExternal.pidTag); write_atom(node); write4BE(id & 0x7fff); // 15 bits write4BE(serial & 0x1fff); // 13 bits @@ -727,11 +727,20 @@ public class OtpOutputStream extends ByteArrayOutputStream { * the pid */ public void write_pid(OtpErlangPid pid) { - write1(OtpExternal.newPidTag); + write1(pid.tag()); write_atom(pid.node()); write4BE(pid.id()); write4BE(pid.serial()); - write4BE(pid.creation()); + switch (pid.tag()) { + case OtpExternal.pidTag: + write1(pid.creation()); + break; + case OtpExternal.newPidTag: + write4BE(pid.creation()); + break; + default: + throw new AssertionError("Invalid pid tag " + pid.tag()); + } } @@ -749,7 +758,7 @@ public class OtpOutputStream extends ByteArrayOutputStream { * be used. */ public void write_port(final String node, final int id, final int creation) { - write1(OtpExternal.newPortTag); + write1(OtpExternal.portTag); write_atom(node); write4BE(id & 0xfffffff); // 28 bits write1(creation & 0x3); // 2 bits @@ -762,10 +771,19 @@ public class OtpOutputStream extends ByteArrayOutputStream { * the port. */ public void write_port(OtpErlangPort port) { - write1(OtpExternal.newPortTag); + write1(port.tag()); write_atom(port.node()); write4BE(port.id()); - write4BE(port.creation()); + switch (port.tag()) { + case OtpExternal.portTag: + write1(port.creation()); + break; + case OtpExternal.newPortTag: + write4BE(port.creation()); + break; + default: + throw new AssertionError("Invalid port tag " + port.tag()); + } } /** @@ -811,7 +829,7 @@ public class OtpOutputStream extends ByteArrayOutputStream { arity = 3; // max 3 words in ref } - write1(OtpExternal.newerRefTag); + write1(OtpExternal.newRefTag); // how many id values write2BE(arity); @@ -839,12 +857,24 @@ public class OtpOutputStream extends ByteArrayOutputStream { int[] ids = ref.ids(); int arity = ids.length; - write1(OtpExternal.newerRefTag); + write1(ref.tag()); write2BE(arity); write_atom(ref.node()); - write4BE(ref.creation()); - for (int i = 0; i < arity; i++) { + switch (ref.tag()) { + case OtpExternal.newRefTag: + write1(ref.creation()); + write4BE(ids[0] & 0x3ffff); // first word gets truncated to 18 bits + break; + case OtpExternal.newerRefTag: + write4BE(ref.creation()); + write4BE(ids[0]); // full first word + break; + default: + throw new AssertionError("Invalid ref tag " + ref.tag()); + } + + for (int i = 1; i < arity; i++) { write4BE(ids[i]); } } diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index f31a1722ce..7a14e2635c 100644 --- a/lib/kernel/src/erl_epmd.erl +++ b/lib/kernel/src/erl_epmd.erl @@ -33,10 +33,10 @@ -define(erlang_daemon_port, 4369). -endif. -ifndef(epmd_dist_high). --define(epmd_dist_high, 6). +-define(epmd_dist_high, 4370). -endif. -ifndef(epmd_dist_low). --define(epmd_dist_low, 5). +-define(epmd_dist_low, 4370). -endif. %% External exports @@ -342,13 +342,6 @@ wait_for_reg_reply(Socket, SoFar) -> receive {tcp, Socket, Data0} -> case SoFar ++ Data0 of - [$v, Result, A, B, C, D] -> - case Result of - 0 -> - {alive, Socket, ?u32(A, B, C, D)}; - _ -> - {error, duplicate_name} - end; [$y, Result, A, B] -> case Result of 0 -> 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/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl index f3db6705a2..8256444bdc 100644 --- a/lib/kernel/test/erl_distribution_wb_SUITE.erl +++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl @@ -47,9 +47,6 @@ R end). --define(EPMD_DIST_HIGH, 6). --define(EPMD_DIST_LOW, 5). - -define(DFLAG_PUBLISHED,1). -define(DFLAG_ATOM_CACHE,2). -define(DFLAG_EXTENDED_REFERENCES,4). @@ -60,18 +57,15 @@ -define(DFLAG_NEW_FUN_TAGS,16#80). -define(DFLAG_EXTENDED_PIDS_PORTS,16#100). -define(DFLAG_UTF8_ATOMS, 16#10000). --define(DFLAG_BIG_CREATION, 16#40000). %% From R9 and forward extended references is compulsory %% From R10 and forward extended pids and ports are compulsory %% From R20 and forward UTF8 atoms are compulsory %% From R21 and forward NEW_FUN_TAGS is compulsory (no more tuple fallback {fun, ...}) -%% From R22 and forward BIG_CREATION is compulsory -define(COMPULSORY_DFLAGS, (?DFLAG_EXTENDED_REFERENCES bor ?DFLAG_EXTENDED_PIDS_PORTS bor ?DFLAG_UTF8_ATOMS bor - ?DFLAG_NEW_FUN_TAGS bor - ?DFLAG_BIG_CREATION)). + ?DFLAG_NEW_FUN_TAGS)). -define(PASS_THROUGH, $p). @@ -214,9 +208,9 @@ pending_up_md5(Node,OurName,Cookie) -> {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, [{active,false}, {packet,2}]), - send_name(SocketA,OurName, ?EPMD_DIST_HIGH), + send_name(SocketA,OurName,5), ok = recv_status(SocketA), - {hidden,Node,?EPMD_DIST_HIGH,HisChallengeA} = recv_challenge(SocketA), % See 1) + {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1) OurChallengeA = gen_challenge(), OurDigestA = gen_digest(HisChallengeA, Cookie), send_challenge_reply(SocketA, OurChallengeA, OurDigestA), @@ -230,11 +224,11 @@ pending_up_md5(Node,OurName,Cookie) -> {ok, SocketB} = gen_tcp:connect(atom_to_list(NB),PortNo, [{active,false}, {packet,2}]), - send_name(SocketB,OurName, ?EPMD_DIST_HIGH), + send_name(SocketB,OurName,5), alive = recv_status(SocketB), send_status(SocketB, true), gen_tcp:close(SocketA), - {hidden,Node,?EPMD_DIST_HIGH,HisChallengeB} = recv_challenge(SocketB), % See 1) + {hidden,Node,5,HisChallengeB} = recv_challenge(SocketB), % See 1) OurChallengeB = gen_challenge(), OurDigestB = gen_digest(HisChallengeB, Cookie), send_challenge_reply(SocketB, OurChallengeB, OurDigestB), @@ -260,7 +254,7 @@ simultaneous_md5(Node, OurName, Cookie) when OurName < Node -> Else -> exit(Else) end, - EpmdSocket = register_node(OurName, LSocket, ?EPMD_DIST_LOW, ?EPMD_DIST_HIGH), + EpmdSocket = register(OurName, LSocket, 1, 5), {NA, NB} = split(Node), rpc:cast(Node, net_adm, ping, [OurName]), receive after 1000 -> ok end, @@ -268,7 +262,7 @@ simultaneous_md5(Node, OurName, Cookie) when OurName < Node -> {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, [{active,false}, {packet,2}]), - send_name(SocketA,OurName, ?EPMD_DIST_HIGH), + send_name(SocketA,OurName,5), %% We are still not marked up on the other side, as our first message %% is not sent. SocketB = case gen_tcp:accept(LSocket) of @@ -281,11 +275,11 @@ simultaneous_md5(Node, OurName, Cookie) when OurName < Node -> %% Now we are expected to close A gen_tcp:close(SocketA), %% But still Socket B will continue - {normal,Node,?EPMD_DIST_HIGH} = recv_name(SocketB), % See 1) + {normal,Node,5} = recv_name(SocketB), % See 1) send_status(SocketB, ok_simultaneous), MyChallengeB = gen_challenge(), - send_challenge(SocketB, OurName, MyChallengeB, ?EPMD_DIST_HIGH), - {ok,HisChallengeB} = recv_challenge_reply(SocketB, MyChallengeB, Cookie), + send_challenge(SocketB, OurName, MyChallengeB,5), + HisChallengeB = recv_challenge_reply(SocketB, MyChallengeB, Cookie), DigestB = gen_digest(HisChallengeB,Cookie), send_challenge_ack(SocketB, DigestB), inet:setopts(SocketB, [{active, false}, @@ -307,8 +301,7 @@ simultaneous_md5(Node, OurName, Cookie) when OurName > Node -> Else -> exit(Else) end, - EpmdSocket = register_node(OurName, LSocket, - ?EPMD_DIST_LOW, ?EPMD_DIST_HIGH), + EpmdSocket = register(OurName, LSocket, 1, 5), {NA, NB} = split(Node), rpc:cast(Node, net_adm, ping, [OurName]), receive after 1000 -> ok end, @@ -322,16 +315,16 @@ simultaneous_md5(Node, OurName, Cookie) when OurName > Node -> Else2 -> exit(Else2) end, - send_name(SocketA,OurName, ?EPMD_DIST_HIGH), + send_name(SocketA,OurName,5), ok_simultaneous = recv_status(SocketA), %% Socket B should die during this case catch begin - {normal,Node,?EPMD_DIST_HIGH} = recv_name(SocketB), % See 1) + {normal,Node,5} = recv_name(SocketB), % See 1) send_status(SocketB, ok_simultaneous), MyChallengeB = gen_challenge(), send_challenge(SocketB, OurName, MyChallengeB, 5), - {ok,HisChallengeB} = recv_challenge_reply( + HisChallengeB = recv_challenge_reply( SocketB, MyChallengeB, Cookie), @@ -353,7 +346,7 @@ simultaneous_md5(Node, OurName, Cookie) when OurName > Node -> end, gen_tcp:close(SocketB), %% But still Socket A will continue - {hidden,Node,?EPMD_DIST_HIGH,HisChallengeA} = recv_challenge(SocketA), % See 1) + {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1) OurChallengeA = gen_challenge(), OurDigestA = gen_digest(HisChallengeA, Cookie), send_challenge_reply(SocketA, OurChallengeA, OurDigestA), @@ -379,7 +372,7 @@ missing_compulsory_dflags(Config) when is_list(Config) -> [{active,false}, {packet,2}]), BadNode = list_to_atom(atom_to_list(Name2)++"@"++atom_to_list(NB)), - send_name(SocketA,BadNode, ?EPMD_DIST_HIGH, 0), + send_name(SocketA,BadNode,5,0), not_allowed = recv_status(SocketA), gen_tcp:close(SocketA), stop_node(Node), @@ -523,16 +516,16 @@ send_challenge_reply(Socket, Challenge, Digest) -> recv_challenge_reply(Socket, ChallengeA, Cookie) -> case gen_tcp:recv(Socket, 0) of - {ok,[$r,CB3,CB2,CB1,CB0 | SumB]=Data} when length(SumB) == 16 -> + {ok,[$r,CB3,CB2,CB1,CB0 | SumB]} when length(SumB) == 16 -> SumA = gen_digest(ChallengeA, Cookie), ChallengeB = ?u32(CB3,CB2,CB1,CB0), if SumB == SumA -> - {ok,ChallengeB}; + ChallengeB; true -> - {error,Data} + ?shutdown(bad_challenge_reply) end; - Err -> - {error,Err} + _ -> + ?shutdown(no_node) end. send_challenge_ack(Socket, Digest) -> @@ -627,13 +620,6 @@ wait_for_reg_reply(Socket, SoFar) -> receive {tcp, Socket, Data0} -> case SoFar ++ Data0 of - [$v, Result, A, B, C, D] -> - case Result of - 0 -> - {alive, Socket, ?u32(A, B, C, D)}; - _ -> - {error, duplicate_name} - end; [$y, Result, A, B] -> case Result of 0 -> @@ -654,7 +640,7 @@ wait_for_reg_reply(Socket, SoFar) -> end. -register_node(NodeName, ListenSocket, VLow, VHigh) -> +register(NodeName, ListenSocket, VLow, VHigh) -> {ok,{_,TcpPort}} = inet:sockname(ListenSocket), case do_register_node(NodeName, TcpPort, VLow, VHigh) of {alive, Socket, _Creation} -> diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 0c5516f82b..16ab0e97fc 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -71,6 +71,14 @@ init_per_group(_Group, Config) -> end_per_group(_Group, _Config) -> ok. +init_per_testcase(reopen_changed_log=TC, Config) -> + case os:type() of + {win32,_} -> + {skip,"This test can only work with inodes, i.e. not on Windows"}; + _ -> + ct:print("********** ~w **********", [TC]), + Config + end; init_per_testcase(TestHooksCase, Config) when TestHooksCase == write_failure; TestHooksCase == sync_failure -> diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl index 3f5d9fee3e..ad8fb11beb 100644 --- a/lib/parsetools/test/leex_SUITE.erl +++ b/lib/parsetools/test/leex_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2017. All Rights Reserved. +%% Copyright Ericsson AB 2010-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. @@ -124,10 +124,6 @@ file(Config) when is_list(Config) -> "Erlang code.\n">>, ?line ok = file:write_file(Filename, Mini), ?line {error,[{_,[{none,leex,{file_error,_}}]}],[]} = - leex:file(Filename, [{scannerfile,"//"} | Ret]), - ?line {error,[{_,[{none,leex,{file_error,_}}]}],[]} = - leex:file(Filename, [{includefile,"//"} | Ret]), - ?line {error,[{_,[{none,leex,{file_error,_}}]}],[]} = leex:file(Filename, [{includefile,"/ /"} | Ret]), LeexPre = filename:join(Dir, "leexinc.hrl"), @@ -191,7 +187,6 @@ compile(Config) when is_list(Config) -> "{L}+ : {token,{word,TokenLine,TokenChars}}.\n" "Erlang code.\n">>, ?line ok = file:write_file(Filename, Mini), - ?line error = leex:compile(Filename, "//", #options{}), ?line ok = leex:compile(Filename, Scannerfile, #options{}), file:delete(Scannerfile), file:delete(Filename), diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 47c5dbb95a..431c77141c 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -406,8 +406,7 @@ decrypt_private(CipherText, Options) when is_binary(CipherText), is_list(Options) -> - Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:private_decrypt(rsa, CipherText, format_rsa_private_key(Key), Padding). + crypto:private_decrypt(rsa, CipherText, format_rsa_private_key(Key), default_options(Options)). %%-------------------------------------------------------------------- %% Description: Public key decryption using the public key. @@ -428,8 +427,7 @@ decrypt_public(CipherText, Key) -> PlainText :: binary() . decrypt_public(CipherText, #'RSAPublicKey'{modulus = N, publicExponent = E}, Options) when is_binary(CipherText), is_list(Options) -> - Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:public_decrypt(rsa, CipherText,[E, N], Padding). + crypto:public_decrypt(rsa, CipherText,[E, N], default_options(Options)). %%-------------------------------------------------------------------- %% Description: Public key encryption using the public key. @@ -451,8 +449,7 @@ encrypt_public(PlainText, Key) -> CipherText :: binary() . encrypt_public(PlainText, #'RSAPublicKey'{modulus=N,publicExponent=E}, Options) when is_binary(PlainText), is_list(Options) -> - Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:public_encrypt(rsa, PlainText, [E,N], Padding). + crypto:public_encrypt(rsa, PlainText, [E,N], default_options(Options)). %%-------------------------------------------------------------------- %% @@ -480,8 +477,7 @@ encrypt_private(PlainText, when is_binary(PlainText), is_integer(N), is_integer(E), is_integer(D), is_list(Options) -> - Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:private_encrypt(rsa, PlainText, format_rsa_private_key(Key), Padding). + crypto:private_encrypt(rsa, PlainText, format_rsa_private_key(Key), default_options(Options)). %%-------------------------------------------------------------------- %% Description: List available group sizes among the pre-computed dh groups @@ -1234,6 +1230,33 @@ pkix_test_root_cert(Name, Opts) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +default_options([]) -> + [{rsa_padding, rsa_pkcs1_padding}]; +default_options(Opts) -> + case proplists:get_value(rsa_pad, Opts) of + undefined -> + case proplists:get_value(rsa_padding, Opts) of + undefined -> + case lists:dropwhile(fun erlang:is_tuple/1, Opts) of + [Pad|_] -> + set_padding(Pad, Opts); + [] -> + set_padding(rsa_pkcs1_padding, Opts) + end; + Pad -> + set_padding(Pad, Opts) + end; + Pad -> + set_padding(Pad, Opts) + end. + +set_padding(Pad, Opts) -> + [{rsa_padding,Pad} | [{T,V} || {T,V} <- Opts, + T =/= rsa_padding, + T =/= rsa_pad] + ]. + + format_sign_key(Key = #'RSAPrivateKey'{}) -> {rsa, format_rsa_private_key(Key)}; format_sign_key(#'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> diff --git a/lib/snmp/include/snmp_types.hrl b/lib/snmp/include/snmp_types.hrl index ffe30996dc..eff17a13a3 100644 --- a/lib/snmp/include/snmp_types.hrl +++ b/lib/snmp/include/snmp_types.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -349,6 +349,9 @@ -define(view_included, 1). -define(view_excluded, 2). +-define(view_wildcard, 0). +-define(view_exact, 1). + %%----------------------------------------------------------------- %% From SNMPv2-SMI diff --git a/lib/snmp/src/agent/depend.mk b/lib/snmp/src/agent/depend.mk index 8eba50fa3b..49c7669e41 100644 --- a/lib/snmp/src/agent/depend.mk +++ b/lib/snmp/src/agent/depend.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2016. All Rights Reserved. +# Copyright Ericsson AB 2004-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. @@ -24,6 +24,9 @@ $(EBIN)/snmpa_authentication_service.$(EMULATOR): \ $(EBIN)/snmpa_error_report.$(EMULATOR): \ snmpa_error_report.erl +$(EBIN)/snmpa_get_mechanism.$(EMULATOR): \ + snmpa_get_mechanism.erl + $(EBIN)/snmpa_network_interface.$(EMULATOR): \ snmpa_network_interface.erl @@ -78,6 +81,20 @@ $(EBIN)/snmpa_error_logger.$(EMULATOR): \ snmpa_error_report.erl \ snmpa_error_logger.erl +$(EBIN)/snmpa_set.$(EMULATOR): \ + snmpa_set_mechanism.erl \ + snmpa_set.erl \ + ../misc/snmp_verbosity.hrl + +$(EBIN)/snmpa_get.$(EMULATOR): \ + snmpa_get_mechanism.erl \ + snmpa_get.erl \ + ../misc/snmp_verbosity.hrl + +$(EBIN)/snmpa_get_lib.$(EMULATOR): \ + snmpa_get_lib.erl \ + ../misc/snmp_verbosity.hrl + $(EBIN)/snmpa_local_db.$(EMULATOR): \ snmpa_local_db.erl \ ../misc/snmp_debug.hrl \ diff --git a/lib/snmp/src/agent/modules.mk b/lib/snmp/src/agent/modules.mk index 0f8615588a..49cc158c2e 100644 --- a/lib/snmp/src/agent/modules.mk +++ b/lib/snmp/src/agent/modules.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2016. All Rights Reserved. +# Copyright Ericsson AB 2004-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. @@ -22,6 +22,7 @@ BEHAVIOUR_MODULES = \ snmpa_authentication_service \ snmpa_discovery_handler \ snmpa_error_report \ + snmpa_get_mechanism \ snmpa_mib_storage \ snmpa_mib_data \ snmpa_network_interface \ @@ -30,12 +31,24 @@ BEHAVIOUR_MODULES = \ snmpa_notification_filter \ snmpa_set_mechanism +MIB_MODULES = \ + snmp_community_mib \ + snmp_framework_mib \ + snmp_notification_mib \ + snmp_standard_mib \ + snmp_target_mib \ + snmp_user_based_sm_mib \ + snmp_view_based_acm_mib + # snmpa is "plain" interface module but also defines some agent specific types # and therefor must be compiled before the modules that use them, including # the behaviour modules... +# Some of the MIB modules also define types used elsewhere and therefor +# has to be built before the other mods. # snmpa_mib_data_ttln MODULES = \ snmpa \ + $(MIB_MODULES) \ $(BEHAVIOUR_MODULES) \ snmpa_acm \ snmpa_agent \ @@ -46,6 +59,8 @@ MODULES = \ snmpa_error \ snmpa_error_io \ snmpa_error_logger \ + snmpa_get \ + snmpa_get_lib \ snmpa_local_db \ snmpa_mib_storage_ets \ snmpa_mib_storage_dets \ @@ -66,17 +81,10 @@ MODULES = \ snmpa_trap \ snmpa_usm \ snmpa_vacm \ - snmp_community_mib \ - snmp_framework_mib \ snmp_generic \ snmp_generic_mnesia \ snmp_index \ - snmp_notification_mib \ - snmp_shadow_table \ - snmp_standard_mib \ - snmp_target_mib \ - snmp_user_based_sm_mib \ - snmp_view_based_acm_mib + snmp_shadow_table INTERNAL_HRL_FILES = \ diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl index 02415e8036..c6eeb7cea2 100644 --- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl +++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-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. @@ -37,6 +37,12 @@ %% -export([emask2imask/1]). +-export_type([ + mibview/0, + internal_view_mask/0, + internal_view_mask_element/0, + internal_view_type/0 + ]). -include("snmp_types.hrl"). -include("SNMPv2-TC.hrl"). @@ -53,7 +59,13 @@ -type internal_view_mask() :: null | [internal_view_mask_element()]. --type internal_view_mask_element() :: 0 | 1. +-type internal_view_mask_element() :: ?view_wildcard | + ?view_exact. +-type internal_view_type() :: ?view_included | ?view_excluded. + +-type mibview() :: [{SubTree :: snmp:oid(), + Mask :: internal_view_mask(), + Type :: internal_view_type()}]. -type external_view_mask() :: octet_string(). % At most length of 16 octet -type octet_string() :: [octet()]. diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl index 458b88359b..a521b3773b 100644 --- a/lib/snmp/src/agent/snmpa_agent.erl +++ b/lib/snmp/src/agent/snmpa_agent.erl @@ -28,7 +28,8 @@ %% External exports -export([start_link/4, start_link/5, stop/1]). --export([subagent_set/2, +-export([subagent_get/3, subagent_get_next/3, + subagent_set/2, load_mibs/3, unload_mibs/3, which_mibs/1, whereis_mib/2, info/1, register_subagent/3, unregister_subagent/2, @@ -362,12 +363,19 @@ do_init(Prio, Parent, Ref, Options) -> "~n Options: ~p",[Prio, Parent, Ref, Options]), Mibs = get_mibs(Options), + SetModule = get_set_mechanism(Options), put(set_module, SetModule), + ?vtrace("set-module: ~w", [SetModule]), + + GetModule = get_get_mechanism(Options), + put(get_module, GetModule), + ?vtrace("get-module: ~w", [GetModule]), %% OTP-3324. For AXD301. AuthModule = get_authentication_service(Options), put(auth_module, AuthModule), + ?vtrace("auth-module: ~w", [AuthModule]), MultiT = get_multi_threaded(Options), Vsns = get_versions(Options), @@ -1133,7 +1141,7 @@ handle_call({subagent_get_next, MibView, Varbinds, PduData}, _From, S) -> "~n PduData: ~p", [MibView,Varbinds,PduData]), put_pdu_data(PduData), - {reply, do_get_next(MibView, Varbinds, infinity), S}; + {reply, do_get_next(MibView, Varbinds), S}; handle_call({subagent_set, Arguments, PduData}, _From, S) -> ?vlog("[handle_call] subagent set:" "~n Arguments: ~p" @@ -1174,7 +1182,7 @@ handle_call({get_next, Vars, Context}, _From, S) -> ?vdebug("Varbinds: ~p",[Varbinds]), MibView = snmpa_acm:get_root_mib_view(), Reply = - case do_get_next(MibView, Varbinds, infinity) of + case do_get_next(MibView, Varbinds) of {noError, 0, NewVarbinds} -> Vbs = lists:keysort(#varbind.org_index, NewVarbinds), [{Oid,Val} || #varbind{oid = Oid, value = Val} <- Vbs]; @@ -2559,7 +2567,7 @@ process_pdu(#pdu{type = 'get-next-request', request_id = ReqId, varbinds = Vbs}, "~n ReqId: ~p" "~n Vbs: ~p" "~n MibView: ~p",[ReqId, Vbs, MibView]), - Res = get_err(do_get_next(MibView, Vbs, infinity)), + Res = get_err(do_get_next(MibView, Vbs)), ?vtrace("get-next result: " "~n ~p",[Res]), {ErrStatus, ErrIndex, ResVarbinds} = @@ -2650,8 +2658,7 @@ validate_next_v1_2([Vb | _Vbs], _MibView, _Res) {noSuchName, Vb#varbind.org_index}; validate_next_v1_2([Vb | Vbs], MibView, Res) when Vb#varbind.variabletype =:= 'Counter64' -> - case validate_next_v1( - do_get_next(MibView, [mk_next_oid(Vb)], infinity), MibView) of + case validate_next_v1( do_get_next(MibView, [mk_next_oid(Vb)]), MibView) of {noError, 0, [NVb]} -> validate_next_v1_2(Vbs, MibView, [NVb | Res]); {Error, Index, _OrgVb} -> @@ -2693,6 +2700,20 @@ mk_next_oid(Vb) -> %%%----------------------------------------------------------------- %%----------------------------------------------------------------- +%% Func: do_get/2 +%% Purpose: Handles all VBs in a request that is inside the +%% mibview (local). +%% Returns: {noError, 0, ListOfNewVarbinds} | +%% {ErrorStatus, ErrorIndex, []} +%%----------------------------------------------------------------- + +do_get(UnsortedVarbinds, IsNotification) -> + Extra = get(net_if_data), + GetModule = get(get_module), + GetModule:do_get(UnsortedVarbinds, IsNotification, Extra). + + +%%----------------------------------------------------------------- %% Func: do_get/3 %% Purpose: do_get handles "getRequests". %% Pre: incoming varbinds have type == 'NULL', value == unSpecified @@ -2700,390 +2721,24 @@ mk_next_oid(Vb) -> %% {ErrorStatus, ErrorIndex, []} %%----------------------------------------------------------------- -%% If this function is called from a worker-process, we *may* -%% need to tunnel into the master-agent and let it do the -%% work +%% If this function is called from a worker-process (or other process), +%% we *may* need to tunnel into the master-agent and let it do the work. do_get(MibView, UnsortedVarbinds, IsNotification) -> - do_get(MibView, UnsortedVarbinds, IsNotification, false). + Extra = get(net_if_data), + GetModule = get(get_module), + GetModule:do_get(MibView, UnsortedVarbinds, IsNotification, Extra). do_get(MibView, UnsortedVarbinds, IsNotification, ForceMaster) -> - ?vtrace("do_get -> entry with" - "~n MibView: ~p" - "~n UnsortedVarbinds: ~p" - "~n IsNotification: ~p", - [MibView, UnsortedVarbinds, IsNotification]), case (whereis(snmp_master_agent) =:= self()) of false when (ForceMaster =:= true) -> - %% I am a lowly worker process, handoff to the master agent PduData = get_pdu_data(), call(snmp_master_agent, {do_get, MibView, UnsortedVarbinds, IsNotification, PduData}); - - _ -> - %% This is me, the master, so go ahead - {OutSideView, InSideView} = - split_vbs_view(UnsortedVarbinds, MibView), - {Error, Index, NewVbs} = - do_get(InSideView, IsNotification), - {Error, Index, NewVbs ++ OutSideView} - - end. - - -split_vbs_view(Vbs, MibView) -> - ?vtrace("split the varbinds view", []), - split_vbs_view(Vbs, MibView, [], []). - -split_vbs_view([Vb | Vbs], MibView, Out, In) -> - case snmpa_acm:validate_mib_view(Vb#varbind.oid, MibView) of - true -> split_vbs_view(Vbs, MibView, Out, [Vb | In]); - false -> split_vbs_view(Vbs, MibView, - [Vb#varbind{value = noSuchObject} | Out], In) - end; -split_vbs_view([], _MibView, Out, In) -> - {Out, In}. - -do_get(UnsortedVarbinds, IsNotification) -> - {MyVarbinds, SubagentVarbinds} = sort_varbindlist(UnsortedVarbinds), - case do_get_local(MyVarbinds, [], IsNotification) of - {noError, 0, NewMyVarbinds} -> - case do_get_subagents(SubagentVarbinds, IsNotification) of - {noError, 0, NewSubagentVarbinds} -> - {noError, 0, NewMyVarbinds ++ NewSubagentVarbinds}; - {ErrorStatus, ErrorIndex, _} -> - {ErrorStatus, ErrorIndex, []} - end; - {ErrorStatus, ErrorIndex, _} -> - {ErrorStatus, ErrorIndex, []} + _ -> + do_get(MibView, UnsortedVarbinds, IsNotification) end. -%%----------------------------------------------------------------- -%% Func: do_get_local/3 -%% Purpose: Loop the variablebindings list. We know that each varbind -%% in that list belongs to us. -%% Returns: {noError, 0, ListOfNewVarbinds} | -%% {ErrorStatus, ErrorIndex, []} -%%----------------------------------------------------------------- -do_get_local([Vb | Vbs], Res, IsNotification) -> - case try_get(Vb, IsNotification) of - NewVb when is_record(NewVb, varbind) -> - do_get_local(Vbs, [NewVb | Res], IsNotification); - ListOfNewVb when is_list(ListOfNewVb) -> - do_get_local(Vbs, lists:append(ListOfNewVb, Res), IsNotification); - {error, Error, OrgIndex} -> - {Error, OrgIndex, []} - end; -do_get_local([], Res, _IsNotification) -> - {noError, 0, Res}. - -%%----------------------------------------------------------------- -%% Func: do_get_subagents/2 -%% Purpose: Loop the list of varbinds for different subagents. -%% For each of them, call sub_agent_get to retreive -%% the values for them. -%% Returns: {noError, 0, ListOfNewVarbinds} | -%% {ErrorStatus, ErrorIndex, []} -%%----------------------------------------------------------------- -do_get_subagents(SubagentVarbinds, IsNotification) -> - do_get_subagents(SubagentVarbinds, [], IsNotification). -do_get_subagents([{SubAgentPid, SAVbs} | Tail], Res, IsNotification) -> - {_SAOids, Vbs} = sa_split(SAVbs), - case catch subagent_get(SubAgentPid, Vbs, IsNotification) of - {noError, 0, NewVbs} -> - do_get_subagents(Tail, lists:append(NewVbs, Res), IsNotification); - {ErrorStatus, ErrorIndex, _} -> - {ErrorStatus, ErrorIndex, []}; - {'EXIT', Reason} -> - user_err("Lost contact with subagent (get) ~w. Using genErr", - [Reason]), - {genErr, 0, []} - end; -do_get_subagents([], Res, _IsNotification) -> - {noError, 0, Res}. - - -%%----------------------------------------------------------------- -%% Func: try_get/2 -%% Returns: {error, ErrorStatus, OrgIndex} | -%% #varbind | -%% List of #varbind -%%----------------------------------------------------------------- -try_get(IVb, IsNotification) when is_record(IVb, ivarbind) -> - ?vtrace("try_get(ivarbind) -> entry with" - "~n IVb: ~p", [IVb]), - get_var_value_from_ivb(IVb, IsNotification); -try_get({TableOid, TableVbs}, IsNotification) -> - ?vtrace("try_get(table) -> entry with" - "~n TableOid: ~p" - "~n TableVbs: ~p", [TableOid, TableVbs]), - [#ivarbind{mibentry = MibEntry}|_] = TableVbs, - {NoAccessVbs, AccessVbs} = - check_all_table_vbs(TableVbs, IsNotification, [], []), - case get_tab_value_from_mib(MibEntry, TableOid, AccessVbs) of - {error, ErrorStatus, OrgIndex} -> - {error, ErrorStatus, OrgIndex}; - NVbs -> - NVbs ++ NoAccessVbs - end. - -%%----------------------------------------------------------------- -%% Make sure all requested columns are accessible. -%%----------------------------------------------------------------- -check_all_table_vbs([IVb| IVbs], IsNotification, NoA, A) -> - #ivarbind{mibentry = Me, varbind = Vb} = IVb, - case Me#me.access of - 'not-accessible' -> - NNoA = [Vb#varbind{value = noSuchInstance} | NoA], - check_all_table_vbs(IVbs, IsNotification, NNoA, A); - 'accessible-for-notify' when IsNotification =:= false -> - NNoA = [Vb#varbind{value = noSuchInstance} | NoA], - check_all_table_vbs(IVbs, IsNotification, NNoA, A); - 'write-only' -> - NNoA = [Vb#varbind{value = noSuchInstance} | NoA], - check_all_table_vbs(IVbs, IsNotification, NNoA, A); - _ -> - check_all_table_vbs(IVbs, IsNotification, NoA, [IVb | A]) - end; -check_all_table_vbs([], _IsNotification, NoA, A) -> {NoA, A}. - -%%----------------------------------------------------------------- -%% Returns: {error, ErrorStatus, OrgIndex} | -%% #varbind -%%----------------------------------------------------------------- -get_var_value_from_ivb(IVb, IsNotification) - when IVb#ivarbind.status =:= noError -> - ?vtrace("get_var_value_from_ivb(noError) -> entry", []), - #ivarbind{mibentry = Me, varbind = Vb} = IVb, - #varbind{org_index = OrgIndex, oid = Oid} = Vb, - case Me#me.access of - 'not-accessible' -> - Vb#varbind{value = noSuchInstance}; - 'accessible-for-notify' when IsNotification =:= false -> - Vb#varbind{value = noSuchInstance}; - 'write-only' -> - Vb#varbind{value = noSuchInstance}; - _ -> - case get_var_value_from_mib(Me, Oid) of - {value, Type, Value} -> - Vb#varbind{variabletype = Type, value = Value}; - {error, ErrorStatus} -> - {error, ErrorStatus, OrgIndex} - end - end; -get_var_value_from_ivb(#ivarbind{status = Status, varbind = Vb}, _) -> - ?vtrace("get_var_value_from_ivb(~p) -> entry", [Status]), - Vb#varbind{value = Status}. - -%%----------------------------------------------------------------- -%% Func: get_var_value_from_mib/1 -%% Purpose: -%% Returns: {error, ErrorStatus} | -%% {value, Type, Value} -%%----------------------------------------------------------------- -%% Pre: Oid is a correct instance Oid (lookup checked that). -%% Returns: A correct return value (see make_value_a_correct_value) -get_var_value_from_mib(#me{entrytype = variable, - asn1_type = ASN1Type, - mfa = {Mod, Func, Args}}, - _Oid) -> - ?vtrace("get_var_value_from_mib(variable) -> entry when" - "~n Mod: ~p" - "~n Func: ~p" - "~n Args: ~p", [Mod, Func, Args]), - Result = (catch dbg_apply(Mod, Func, [get | Args])), - % mib shall return {value, <a-nice-value-within-range>} | - % {noValue, noSuchName} (v1) | - % {noValue, noSuchObject | noSuchInstance} (v2, v1) - % everything else (including 'genErr') will generate 'genErr'. - make_value_a_correct_value(Result, ASN1Type, {Mod, Func, Args}); - -get_var_value_from_mib(#me{entrytype = table_column, - oid = MeOid, - asn1_type = ASN1Type, - mfa = {Mod, Func, Args}}, - Oid) -> - ?vtrace("get_var_value_from_mib(table_column) -> entry when" - "~n MeOid: ~p" - "~n Mod: ~p" - "~n Func: ~p" - "~n Args: ~p" - "~n Oid: ~p", [MeOid, Mod, Func, Args, Oid]), - Col = lists:last(MeOid), - Indexes = snmp_misc:diff(Oid, MeOid), - [Result] = (catch dbg_apply(Mod, Func, [get, Indexes, [Col] | Args])), - make_value_a_correct_value(Result, ASN1Type, - {Mod, Func, Args, Indexes, Col}). - - -%% For table operations we need to pass RestOid down to the table-function. -%% Its up to the table-function to check for noSuchInstance (ex: a -%% non-existing row). -%% Returns: {error, ErrorStatus, OrgIndex} | -%% {value, Type, Value} -get_tab_value_from_mib(#me{mfa = {Mod, Func, Args}}, TableOid, TableVbs) -> - ?vtrace("get_tab_value_from_mib -> entry when" - "~n Mod: ~p" - "~n Func: ~p" - "~n Args: ~p", [Mod, Func, Args]), - TableOpsWithShortOids = deletePrefixes(TableOid, TableVbs), - SortedVBsRows = snmpa_svbl:sort_varbinds_rows(TableOpsWithShortOids), - case get_value_all_rows(SortedVBsRows, Mod, Func, Args, []) of - {Error, Index} -> - #ivarbind{varbind = Vb} = lists:nth(Index, TableVbs), - {error, Error, Vb#varbind.org_index}; - ListOfValues -> - merge_varbinds_and_value(TableVbs, ListOfValues) - end. - -%%----------------------------------------------------------------- -%% Values is a scrambled list of {CorrectValue, Index}, where Index -%% is index into the #ivarbind list. So for each Value, we must -%% find the corresponding #ivarbind, and merge them into a new -%% #varbind. -%% The Values list comes from validate_tab_res. -%%----------------------------------------------------------------- -merge_varbinds_and_value(IVbs, [{{value, Type, Value}, Index} | Values]) -> - #ivarbind{varbind = Vb} = lists:nth(Index, IVbs), - [Vb#varbind{variabletype = Type, value = Value} | - merge_varbinds_and_value(IVbs, Values)]; -merge_varbinds_and_value(_, []) -> []. - -get_value_all_rows([{[], OrgCols} | Rows], Mod, Func, Args, Res) -> - ?vtrace("get_value_all_rows -> entry when" - "~n OrgCols: ~p", [OrgCols]), - Cols = [{{value, noValue, noSuchInstance}, Index} || - {_Col, _ASN1Type, Index} <- OrgCols], - NewRes = lists:append(Cols, Res), - get_value_all_rows(Rows, Mod, Func, Args, NewRes); -get_value_all_rows([{RowIndex, OrgCols} | Rows], Mod, Func, Args, Res) -> - ?vtrace("get_value_all_rows -> entry when" - "~n RowIndex: ~p" - "~n OrgCols: ~p", [RowIndex, OrgCols]), - {DOrgCols, Dup} = remove_duplicates(OrgCols), - Cols = delete_index(DOrgCols), - Result = (catch dbg_apply(Mod, Func, [get, RowIndex, Cols | Args])), - case validate_tab_res(Result, DOrgCols, {Mod, Func, Args}) of - Values when is_list(Values) -> - NVals = restore_duplicates(Dup, Values), - NewRes = lists:append(NVals, Res), - get_value_all_rows(Rows, Mod, Func, Args, NewRes); - {error, ErrorStatus, Index} -> - validate_err(row_set, {ErrorStatus, Index}, {Mod, Func, Args}) - end; -get_value_all_rows([], _Mod, _Func, _Args, Res) -> - ?vtrace("get_value_all_rows -> entry when done" - "~n Res: ~p", [Res]), - Res. - -%%----------------------------------------------------------------- -%% Returns: list of {ShortOid, ASN1TYpe} -%%----------------------------------------------------------------- -deletePrefixes(Prefix, [#ivarbind{varbind = Varbind, mibentry = ME} | Vbs]) -> - #varbind{oid = Oid} = Varbind, - [{snmp_misc:diff(Oid, Prefix), ME#me.asn1_type} | - deletePrefixes(Prefix, Vbs)]; -deletePrefixes(_Prefix, []) -> []. - -%%----------------------------------------------------------------- -%% Args: {RowIndex, list of {ShortOid, ASN1Type}} -%% Returns: list of Col -%%----------------------------------------------------------------- -delete_index([{Col, _Val, _OrgIndex} | T]) -> - [Col | delete_index(T)]; -delete_index([]) -> []. - -%%----------------------------------------------------------------- -%% This function is called before 'get' on a table, and removes -%% any duplicate columns. It returns {Cols, DupInfo}. The Cols -%% are the unique columns. The instrumentation function is -%% called to get the values. These values, together with the -%% DupInfo, is later passed to restore_duplicates, which uses -%% the retrieved values to reconstruct the original column list, -%% but with the retrieved value for each column. -%%----------------------------------------------------------------- -remove_duplicates(Cols) -> - remove_duplicates(Cols, [], []). - - -remove_duplicates([{Col, V1, OrgIdx1}, {Col, V2, OrgIdx2} | T], NCols, Dup) -> - remove_duplicates([{Col, V1, OrgIdx1} | T], NCols, - [{Col, V2, OrgIdx2} | Dup]); -remove_duplicates([Col | T], NCols, Dup) -> - remove_duplicates(T, [Col | NCols], Dup); -remove_duplicates([], NCols, Dup) -> - {lists:reverse(NCols), lists:reverse(Dup)}. - -restore_duplicates([], Cols) -> - [{Val, OrgIndex} || {_Col, Val, OrgIndex} <- Cols]; -restore_duplicates([{Col, _Val2, OrgIndex2} | Dup], - [{Col, NVal, OrgIndex1} | Cols]) -> - [{NVal, OrgIndex2} | - restore_duplicates(Dup, [{Col, NVal, OrgIndex1} | Cols])]; -restore_duplicates(Dup, [{_Col, Val, OrgIndex} | T]) -> - [{Val, OrgIndex} | restore_duplicates(Dup, T)]. - -%% Maps the column number to Index. -% col_to_index(0, _) -> 0; -% col_to_index(Col, [{Col, _, Index}|_]) -> -% Index; -% col_to_index(Col, [_|Cols]) -> -% col_to_index(Col, Cols). - -%%----------------------------------------------------------------- -%% Three cases: -%% 1) All values ok -%% 2) table_func returned {Error, ...} -%% 3) Some value in Values list is erroneous. -%% Args: Value is a list of values from table_func(get..) -%% OrgCols is a list with {Col, ASN1Type, OrgIndex} -%% each element in Values and OrgCols correspond to each -%% other. -%%----------------------------------------------------------------- -validate_tab_res(Values, OrgCols, Mfa) when is_list(Values) -> - {_Col, _ASN1Type, OneIdx} = hd(OrgCols), - validate_tab_res(Values, OrgCols, Mfa, [], OneIdx); -validate_tab_res({noValue, Error}, OrgCols, Mfa) -> - Values = lists:duplicate(length(OrgCols), {noValue, Error}), - validate_tab_res(Values, OrgCols, Mfa); -validate_tab_res({genErr, Col}, OrgCols, Mfa) -> - case lists:keysearch(Col, 1, OrgCols) of - {value, {_Col, _ASN1Type, Index}} -> - {error, genErr, Index}; - _ -> - user_err("Invalid column in {genErr, ~w} from ~w (get)", - [Col, Mfa]), - [{_Col, _ASN1Type, Index} | _] = OrgCols, - {error, genErr, Index} - end; -validate_tab_res(genErr, [{_Col, __ASN1Type, Index} | _OrgCols], _Mfa) -> - {error, genErr, Index}; -validate_tab_res(Error, [{_Col, _ASN1Type, Index} | _OrgCols], Mfa) -> - user_err("Invalid return value ~w from ~w (get)",[Error, Mfa]), - {error, genErr, Index}. - -validate_tab_res([Value | Values], - [{Col, ASN1Type, Index} | OrgCols], - Mfa, Res, I) -> - %% This one makes it possible to return a list of genErr, which - %% is not allowed according to the manual. But that's ok, as - %% everything else will generate a genErr! (the only problem is - %% that it won't generate a user_error). - case make_value_a_correct_value(Value, ASN1Type, Mfa) of - {error, ErrorStatus} -> - {error, ErrorStatus, Index}; - CorrectValue -> - NewRes = [{Col, CorrectValue, Index} | Res], - validate_tab_res(Values, OrgCols, Mfa, NewRes, I) - end; -validate_tab_res([], [], _Mfa, Res, _I) -> - lists:reverse(Res); -validate_tab_res([], [{_Col, _ASN1Type, Index}|_], Mfa, _Res, _I) -> - user_err("Too few values returned from ~w (get)", [Mfa]), - {error, genErr, Index}; -validate_tab_res(_TooMany, [], Mfa, _Res, I) -> - user_err("Too many values returned from ~w (get)", [Mfa]), - {error, genErr, I}. %%%----------------------------------------------------------------- @@ -3125,491 +2780,12 @@ validate_tab_res(_TooMany, [], Mfa, _Res, I) -> %% subagent must be considered to be very rare. %%----------------------------------------------------------------- -%% It may be a bit agressive to check this already, -%% but since it is a security measure, it makes sense. -do_get_next(_MibView, UnsortedVarbinds, GbMaxVBs) - when (is_integer(GbMaxVBs) andalso (length(UnsortedVarbinds) > GbMaxVBs)) -> - {tooBig, 0, []}; % What is the correct index in this case? -do_get_next(MibView, UnsortedVBs, GbMaxVBs) -> - ?vt("do_get_next -> entry when" - "~n MibView: ~p" - "~n UnsortedVBs: ~p", [MibView, UnsortedVBs]), - SortedVBs = oid_sort_vbs(UnsortedVBs), - ?vt("do_get_next -> " - "~n SortedVBs: ~p", [SortedVBs]), - next_loop_varbinds([], SortedVBs, MibView, [], [], GbMaxVBs). - -oid_sort_vbs(Vbs) -> - lists:keysort(#varbind.oid, Vbs). - -next_loop_varbinds(_, Vbs, _MibView, Res, _LAVb, GbMaxVBs) - when (is_integer(GbMaxVBs) andalso - ((length(Vbs) + length(Res)) > GbMaxVBs)) -> - {tooBig, 0, []}; % What is the correct index in this case? - -%% LAVb is Last Accessible Vb -next_loop_varbinds([], [Vb | Vbs], MibView, Res, LAVb, GbMaxVBs) -> - ?vt("next_loop_varbinds -> entry when" - "~n Vb: ~p" - "~n MibView: ~p", [Vb, MibView]), - case varbind_next(Vb, MibView) of - endOfMibView -> - ?vt("next_loop_varbind -> endOfMibView", []), - RVb = if LAVb =:= [] -> Vb; - true -> LAVb - end, - NewVb = RVb#varbind{variabletype = 'NULL', value = endOfMibView}, - next_loop_varbinds([], Vbs, MibView, [NewVb | Res], [], GbMaxVBs); - - {variable, ME, VarOid} when ((ME#me.access =/= 'not-accessible') andalso - (ME#me.access =/= 'write-only') andalso - (ME#me.access =/= 'accessible-for-notify')) -> - ?vt("next_loop_varbind -> variable: " - "~n ME: ~p" - "~n VarOid: ~p", [ME, VarOid]), - case try_get_instance(Vb, ME) of - {value, noValue, _NoSuchSomething} -> - ?vt("next_loop_varbind -> noValue", []), - %% Try next one - NewVb = Vb#varbind{oid = VarOid, - value = 'NULL'}, - next_loop_varbinds([], [NewVb | Vbs], MibView, Res, [], - GbMaxVBs); - {value, Type, Value} -> - ?vt("next_loop_varbind -> value" - "~n Type: ~p" - "~n Value: ~p", [Type, Value]), - NewVb = Vb#varbind{oid = VarOid, - variabletype = Type, - value = Value}, - next_loop_varbinds([], Vbs, MibView, [NewVb | Res], [], - GbMaxVBs); - {error, ErrorStatus} -> - ?vdebug("next loop varbinds:" - "~n ErrorStatus: ~p",[ErrorStatus]), - {ErrorStatus, Vb#varbind.org_index, []} - end; - {variable, _ME, VarOid} -> - ?vt("next_loop_varbind -> variable: " - "~n VarOid: ~p", [VarOid]), - RVb = if LAVb =:= [] -> Vb; - true -> LAVb - end, - NewVb = Vb#varbind{oid = VarOid, value = 'NULL'}, - next_loop_varbinds([], [NewVb | Vbs], MibView, Res, RVb, GbMaxVBs); - {table, TableOid, TableRestOid, ME} -> - ?vt("next_loop_varbind -> table: " - "~n TableOid: ~p" - "~n TableRestOid: ~p" - "~n ME: ~p", [TableOid, TableRestOid, ME]), - next_loop_varbinds({table, TableOid, ME, - [{tab_oid(TableRestOid), Vb}]}, - Vbs, MibView, Res, [], GbMaxVBs); - {subagent, SubAgentPid, SAOid} -> - ?vt("next_loop_varbind -> subagent: " - "~n SubAgentPid: ~p" - "~n SAOid: ~p", [SubAgentPid, SAOid]), - NewVb = Vb#varbind{variabletype = 'NULL', value = 'NULL'}, - next_loop_varbinds({subagent, SubAgentPid, SAOid, [NewVb]}, - Vbs, MibView, Res, [], GbMaxVBs) - end; -next_loop_varbinds({table, TableOid, ME, TabOids}, - [Vb | Vbs], MibView, Res, _LAVb, GbMaxVBs) -> - ?vt("next_loop_varbinds(table) -> entry with" - "~n TableOid: ~p" - "~n Vb: ~p", [TableOid, Vb]), - case varbind_next(Vb, MibView) of - {table, TableOid, TableRestOid, _ME} -> - next_loop_varbinds({table, TableOid, ME, - [{tab_oid(TableRestOid), Vb} | TabOids]}, - Vbs, MibView, Res, [], GbMaxVBs); - _ -> - case get_next_table(ME, TableOid, TabOids, MibView) of - {ok, TabRes, TabEndOfTabVbs} -> - NewVbs = lists:append(TabEndOfTabVbs, [Vb | Vbs]), - NewRes = lists:append(TabRes, Res), - next_loop_varbinds([], NewVbs, MibView, NewRes, [], - GbMaxVBs); - {ErrorStatus, OrgIndex} -> - ?vdebug("next loop varbinds: next varbind" - "~n ErrorStatus: ~p" - "~n OrgIndex: ~p", - [ErrorStatus,OrgIndex]), - {ErrorStatus, OrgIndex, []} - end - end; -next_loop_varbinds({table, TableOid, ME, TabOids}, - [], MibView, Res, _LAVb, GbMaxVBs) -> - ?vt("next_loop_varbinds(table) -> entry with" - "~n TableOid: ~p", [TableOid]), - case get_next_table(ME, TableOid, TabOids, MibView) of - {ok, TabRes, TabEndOfTabVbs} -> - ?vt("next_loop_varbinds(table) -> get_next_table result:" - "~n TabRes: ~p" - "~n TabEndOfTabVbs: ~p", [TabRes, TabEndOfTabVbs]), - NewRes = lists:append(TabRes, Res), - next_loop_varbinds([], TabEndOfTabVbs, MibView, NewRes, [], - GbMaxVBs); - {ErrorStatus, OrgIndex} -> - ?vdebug("next loop varbinds: next table" - "~n ErrorStatus: ~p" - "~n OrgIndex: ~p", - [ErrorStatus,OrgIndex]), - {ErrorStatus, OrgIndex, []} - end; -next_loop_varbinds({subagent, SAPid, SAOid, SAVbs}, - [Vb | Vbs], MibView, Res, _LAVb, GbMaxVBs) -> - ?vt("next_loop_varbinds(subagent) -> entry with" - "~n SAPid: ~p" - "~n SAOid: ~p" - "~n Vb: ~p", [SAPid, SAOid, Vb]), - case varbind_next(Vb, MibView) of - {subagent, _SubAgentPid, SAOid} -> - next_loop_varbinds({subagent, SAPid, SAOid, - [Vb | SAVbs]}, - Vbs, MibView, Res, [], GbMaxVBs); - _ -> - case get_next_sa(SAPid, SAOid, SAVbs, MibView) of - {ok, SARes, SAEndOfMibViewVbs} -> - NewVbs = lists:append(SAEndOfMibViewVbs, [Vb | Vbs]), - NewRes = lists:append(SARes, Res), - next_loop_varbinds([], NewVbs, MibView, NewRes, [], - GbMaxVBs); - {noSuchName, OrgIndex} -> - %% v1 reply, treat this Vb as endOfMibView, and try again - %% for the others. - case lists:keysearch(OrgIndex, #varbind.org_index, SAVbs) of - {value, EVb} -> - NextOid = next_oid(SAOid), - EndOfVb = - EVb#varbind{oid = NextOid, - value = {endOfMibView, NextOid}}, - case lists:delete(EVb, SAVbs) of - [] -> - next_loop_varbinds([], [EndOfVb, Vb | Vbs], - MibView, Res, [], - GbMaxVBs); - TryAgainVbs -> - next_loop_varbinds({subagent, SAPid, SAOid, - TryAgainVbs}, - [EndOfVb, Vb | Vbs], - MibView, Res, [], - GbMaxVBs) - end; - false -> - %% bad index from subagent - {genErr, (hd(SAVbs))#varbind.org_index, []} - end; - {ErrorStatus, OrgIndex} -> - ?vdebug("next loop varbinds: next subagent" - "~n Vb: ~p" - "~n ErrorStatus: ~p" - "~n OrgIndex: ~p", - [Vb,ErrorStatus,OrgIndex]), - {ErrorStatus, OrgIndex, []} - end - end; -next_loop_varbinds({subagent, SAPid, SAOid, SAVbs}, - [], MibView, Res, _LAVb, GbMaxVBs) -> - ?vt("next_loop_varbinds(subagent) -> entry with" - "~n SAPid: ~p" - "~n SAOid: ~p", [SAPid, SAOid]), - case get_next_sa(SAPid, SAOid, SAVbs, MibView) of - {ok, SARes, SAEndOfMibViewVbs} -> - NewRes = lists:append(SARes, Res), - next_loop_varbinds([], SAEndOfMibViewVbs, MibView, NewRes, [], - GbMaxVBs); - {noSuchName, OrgIndex} -> - %% v1 reply, treat this Vb as endOfMibView, and try again for - %% the others. - case lists:keysearch(OrgIndex, #varbind.org_index, SAVbs) of - {value, EVb} -> - NextOid = next_oid(SAOid), - EndOfVb = EVb#varbind{oid = NextOid, - value = {endOfMibView, NextOid}}, - case lists:delete(EVb, SAVbs) of - [] -> - next_loop_varbinds([], [EndOfVb], MibView, Res, [], - GbMaxVBs); - TryAgainVbs -> - next_loop_varbinds({subagent, SAPid, SAOid, - TryAgainVbs}, - [EndOfVb], MibView, Res, [], - GbMaxVBs) - end; - false -> - %% bad index from subagent - {genErr, (hd(SAVbs))#varbind.org_index, []} - end; - {ErrorStatus, OrgIndex} -> - ?vdebug("next loop varbinds: next subagent" - "~n ErrorStatus: ~p" - "~n OrgIndex: ~p", - [ErrorStatus,OrgIndex]), - {ErrorStatus, OrgIndex, []} - end; -next_loop_varbinds([], [], _MibView, Res, _LAVb, _GbMaxVBs) -> - ?vt("next_loop_varbinds -> entry when done", []), - {noError, 0, Res}. - -try_get_instance(_Vb, #me{mfa = {M, F, A}, asn1_type = ASN1Type}) -> - ?vtrace("try_get_instance -> entry with" - "~n M: ~p" - "~n F: ~p" - "~n A: ~p", [M,F,A]), - Result = (catch dbg_apply(M, F, [get | A])), - % mib shall return {value, <a-nice-value-within-range>} | - % {noValue, noSuchName} (v1) | - % {noValue, noSuchObject | noSuchInstance} (v2, v1) - % everything else (including 'genErr') will generate 'genErr'. - make_value_a_correct_value(Result, ASN1Type, {M, F, A}). - -tab_oid([]) -> [0]; -tab_oid(X) -> X. - - -%%----------------------------------------------------------------- -%% Perform a next, using the varbinds Oid if value is simple -%% value. If value is {endOf<something>, NextOid}, use NextOid. -%% This case happens when a table has returned endOfTable, or -%% a subagent has returned endOfMibView. -%%----------------------------------------------------------------- -varbind_next(#varbind{value = Value, oid = Oid}, MibView) -> - ?vt("varbind_next -> entry with" - "~n Value: ~p" - "~n Oid: ~p" - "~n MibView: ~p", [Value, Oid, MibView]), - case Value of - {endOfTable, NextOid} -> - snmpa_mib:next(get(mibserver), NextOid, MibView); - {endOfMibView, NextOid} -> - snmpa_mib:next(get(mibserver), NextOid, MibView); - _ -> - snmpa_mib:next(get(mibserver), Oid, MibView) - end. - -get_next_table(#me{mfa = {M, F, A}}, TableOid, TableOids, MibView) -> - % We know that all TableOids have at least a column number as oid - ?vt("get_next_table -> entry with" - "~n M: ~p" - "~n F: ~p" - "~n A: ~p" - "~n TableOid: ~p" - "~n TableOids: ~p" - "~n MibView: ~p", [M, F, A, TableOid, TableOids, MibView]), - Sorted = snmpa_svbl:sort_varbinds_rows(TableOids), - case get_next_values_all_rows(Sorted, M,F,A, [], TableOid) of - NewVbs when is_list(NewVbs) -> - ?vt("get_next_table -> " - "~n NewVbs: ~p", [NewVbs]), - % We must now check each Vb for endOfTable and that it is - % in the MibView. If not, it becomes a endOfTable. We - % collect all of these together. - transform_tab_next_result(NewVbs, {[], []}, MibView); - {ErrorStatus, OrgIndex} -> - {ErrorStatus, OrgIndex} - end. - -get_next_values_all_rows([Row | Rows], M, F, A, Res, TabOid) -> - {RowIndex, TableOids} = Row, - Cols = delete_index(TableOids), - ?vt("get_next_values_all_rows -> " - "~n Cols: ~p", [Cols]), - Result = (catch dbg_apply(M, F, [get_next, RowIndex, Cols | A])), - ?vt("get_next_values_all_rows -> " - "~n Result: ~p", [Result]), - case validate_tab_next_res(Result, TableOids, {M, F, A}, TabOid) of - Values when is_list(Values) -> - ?vt("get_next_values_all_rows -> " - "~n Values: ~p", [Values]), - NewRes = lists:append(Values, Res), - get_next_values_all_rows(Rows, M, F, A, NewRes, TabOid); - {ErrorStatus, OrgIndex} -> - {ErrorStatus, OrgIndex} - end; -get_next_values_all_rows([], _M, _F, _A, Res, _TabOid) -> - Res. - -transform_tab_next_result([Vb | Vbs], {Res, EndOfs}, MibView) -> - case Vb#varbind.value of - {endOfTable, _} -> -%% ?vtrace("transform_tab_next_result -> endOfTable: " -%% "split varbinds",[]), -%% R = split_varbinds(Vbs, Res, [Vb | EndOfs]), -%% ?vtrace("transform_tab_next_result -> " -%% "~n R: ~p", [R]), -%% R; - split_varbinds(Vbs, Res, [Vb | EndOfs]); - _ -> - case snmpa_acm:validate_mib_view(Vb#varbind.oid, MibView) of - true -> - transform_tab_next_result(Vbs, {[Vb|Res], EndOfs},MibView); - _ -> - Oid = Vb#varbind.oid, - NewEndOf = Vb#varbind{value = {endOfTable, Oid}}, - transform_tab_next_result(Vbs, {Res, [NewEndOf | EndOfs]}, - MibView) - end - end; -transform_tab_next_result([], {Res, EndOfs}, _MibView) -> - ?vt("transform_tab_next_result -> entry with: " - "~n Res: ~p" - "~n EndIfs: ~p",[Res, EndOfs]), - {ok, Res, EndOfs}. - -%%----------------------------------------------------------------- -%% Three cases: -%% 1) All values ok -%% 2) table_func returned {Error, ...} -%% 3) Some value in Values list is erroneous. -%% Args: Value is a list of values from table_func(get_next, ...) -%% TableOids is a list of {TabRestOid, OrgVb} -%% each element in Values and TableOids correspond to each -%% other. -%% Returns: List of NewVarbinds | -%% {ErrorStatus, OrgIndex} -%% (In the NewVarbinds list, the value may be endOfTable) -%%----------------------------------------------------------------- -validate_tab_next_res(Values, TableOids, Mfa, TabOid) -> - ?vt("validate_tab_next_res -> entry with: " - "~n Values: ~p" - "~n TableOids: ~p" - "~n Mfa: ~p" - "~n TabOid: ~p", [Values, TableOids, Mfa, TabOid]), - {_Col, _ASN1Type, OneIdx} = hd(TableOids), - validate_tab_next_res(Values, TableOids, Mfa, [], TabOid, - next_oid(TabOid), OneIdx). -validate_tab_next_res([{NextOid, Value} | Values], - [{_ColNo, OrgVb, _Index} | TableOids], - Mfa, Res, TabOid, TabNextOid, I) -> - ?vt("validate_tab_next_res -> entry with: " - "~n NextOid: ~p" - "~n Value: ~p" - "~n Values: ~p" - "~n TableOids: ~p" - "~n Mfa: ~p" - "~n TabOid: ~p", - [NextOid, Value, Values, TableOids, Mfa, TabOid]), - #varbind{org_index = OrgIndex} = OrgVb, - ?vt("validate_tab_next_res -> OrgIndex: ~p", [OrgIndex]), - NextCompleteOid = lists:append(TabOid, NextOid), - case snmpa_mib:lookup(get(mibserver), NextCompleteOid) of - {table_column, #me{asn1_type = ASN1Type}, _TableEntryOid} -> - ?vt("validate_tab_next_res -> ASN1Type: ~p", [ASN1Type]), - case make_value_a_correct_value({value, Value}, ASN1Type, Mfa) of - {error, ErrorStatus} -> - ?vt("validate_tab_next_res -> " - "~n ErrorStatus: ~p", [ErrorStatus]), - {ErrorStatus, OrgIndex}; - {value, Type, NValue} -> - ?vt("validate_tab_next_res -> " - "~n Type: ~p" - "~n NValue: ~p", [Type, NValue]), - NewVb = OrgVb#varbind{oid = NextCompleteOid, - variabletype = Type, value = NValue}, - validate_tab_next_res(Values, TableOids, Mfa, - [NewVb | Res], TabOid, TabNextOid, I) - end; - Error -> - user_err("Invalid oid ~w from ~w (get_next). Using genErr => ~p", - [NextOid, Mfa, Error]), - {genErr, OrgIndex} - end; -validate_tab_next_res([endOfTable | Values], - [{_ColNo, OrgVb, _Index} | TableOids], - Mfa, Res, TabOid, TabNextOid, I) -> - ?vt("validate_tab_next_res(endOfTable) -> entry with: " - "~n Values: ~p" - "~n OrgVb: ~p" - "~n TableOids: ~p" - "~n Mfa: ~p" - "~n Res: ~p" - "~n TabOid: ~p" - "~n TabNextOid: ~p" - "~n I: ~p", - [Values, OrgVb, TableOids, Mfa, Res, TabOid, TabNextOid, I]), - NewVb = OrgVb#varbind{value = {endOfTable, TabNextOid}}, - validate_tab_next_res(Values, TableOids, Mfa, [NewVb | Res], - TabOid, TabNextOid, I); -validate_tab_next_res([], [], _Mfa, Res, _TabOid, _TabNextOid, _I) -> - Res; -validate_tab_next_res([], [{_Col, _OrgVb, Index}|_], Mfa, _Res, _, _, _I) -> - user_err("Too few values returned from ~w (get_next)", [Mfa]), - {genErr, Index}; -validate_tab_next_res({genErr, ColNumber}, OrgCols, - Mfa, _Res, _TabOid, _TabNextOid, _I) -> - OrgIndex = snmpa_svbl:col_to_orgindex(ColNumber, OrgCols), - validate_err(table_next, {genErr, OrgIndex}, Mfa); -validate_tab_next_res({error, Reason}, [{_ColNo, OrgVb, _Index} | _TableOids], - Mfa, _Res, _TabOid, _TabNextOid, _I) -> - #varbind{org_index = OrgIndex} = OrgVb, - user_err("Erroneous return value ~w from ~w (get_next)", - [Reason, Mfa]), - {genErr, OrgIndex}; -validate_tab_next_res(Error, [{_ColNo, OrgVb, _Index} | _TableOids], - Mfa, _Res, _TabOid, _TabNextOid, _I) -> - #varbind{org_index = OrgIndex} = OrgVb, - user_err("Invalid return value ~w from ~w (get_next)", - [Error, Mfa]), - {genErr, OrgIndex}; -validate_tab_next_res(TooMany, [], Mfa, _Res, _, _, I) -> - user_err("Too many values ~w returned from ~w (get_next)", - [TooMany, Mfa]), - {genErr, I}. - -%%----------------------------------------------------------------- -%% Func: get_next_sa/4 -%% Purpose: Loop the list of varbinds for the subagent. -%% Call subagent_get_next to retreive -%% the next varbinds. -%% Returns: {ok, ListOfNewVbs, ListOfEndOfMibViewsVbs} | -%% {ErrorStatus, ErrorIndex} -%%----------------------------------------------------------------- -get_next_sa(SAPid, SAOid, SAVbs, MibView) -> - case catch subagent_get_next(SAPid, MibView, SAVbs) of - {noError, 0, NewVbs} -> - NewerVbs = transform_sa_next_result(NewVbs,SAOid,next_oid(SAOid)), - split_varbinds(NewerVbs, [], []); - {ErrorStatus, ErrorIndex, _} -> - {ErrorStatus, ErrorIndex}; - {'EXIT', Reason} -> - user_err("Lost contact with subagent (next) ~w. Using genErr", - [Reason]), - {genErr, 0} - end. -%%----------------------------------------------------------------- -%% Check for wrong prefix returned or endOfMibView, and convert -%% into {endOfMibView, SANextOid}. -%%----------------------------------------------------------------- -transform_sa_next_result([Vb | Vbs], SAOid, SANextOid) - when Vb#varbind.value =:= endOfMibView -> - [Vb#varbind{value = {endOfMibView, SANextOid}} | - transform_sa_next_result(Vbs, SAOid, SANextOid)]; -transform_sa_next_result([Vb | Vbs], SAOid, SANextOid) -> - case lists:prefix(SAOid, Vb#varbind.oid) of - true -> - [Vb | transform_sa_next_result(Vbs, SAOid, SANextOid)]; - _ -> - [Vb#varbind{oid = SANextOid, value = {endOfMibView, SANextOid}} | - transform_sa_next_result(Vbs, SAOid, SANextOid)] - end; -transform_sa_next_result([], _SAOid, _SANextOid) -> - []. - -split_varbinds([Vb | Vbs], Res, EndOfs) -> - case Vb#varbind.value of - {endOfMibView, _} -> split_varbinds(Vbs, Res, [Vb | EndOfs]); - {endOfTable, _} -> split_varbinds(Vbs, Res, [Vb | EndOfs]); - _ -> split_varbinds(Vbs, [Vb | Res], EndOfs) - end; -split_varbinds([], Res, EndOfs) -> {ok, Res, EndOfs}. +do_get_next(MibView, UnsortedVarbinds) -> + Extra = get(net_if_data), + GetModule = get(get_module), + GetModule:do_get_next(MibView, UnsortedVarbinds, Extra). -next_oid(Oid) -> - case lists:reverse(Oid) of - [H | T] -> lists:reverse([H+1 | T]); - [] -> [] - end. %%%----------------------------------------------------------------- @@ -3623,200 +2799,12 @@ next_oid(Oid) -> %%%----------------------------------------------------------------- do_get_bulk(MibView, NonRepeaters, MaxRepetitions, PduMS, Varbinds, GbMaxVBs) -> - ?vtrace("do_get_bulk -> entry with" - "~n MibView: ~p" - "~n NonRepeaters: ~p" - "~n MaxRepetitions: ~p" - "~n PduMS: ~p" - "~n Varbinds: ~p" - "~n GbMaxVBs: ~p", - [MibView, NonRepeaters, MaxRepetitions, PduMS, Varbinds, GbMaxVBs]), - {NonRepVbs, RestVbs} = split_vbs(NonRepeaters, Varbinds, []), - ?vt("do_get_bulk -> split: " - "~n NonRepVbs: ~p" - "~n RestVbs: ~p", [NonRepVbs, RestVbs]), - case do_get_next(MibView, NonRepVbs, GbMaxVBs) of - {noError, 0, UResNonRepVbs} -> - ?vt("do_get_bulk -> next noError: " - "~n UResNonRepVbs: ~p", [UResNonRepVbs]), - ResNonRepVbs = lists:keysort(#varbind.org_index, UResNonRepVbs), - %% Decode the first varbinds, produce a reversed list of - %% listOfBytes. - case (catch enc_vbs(PduMS - ?empty_pdu_size, ResNonRepVbs)) of - {error, Idx, Reason} -> - user_err("failed encoding varbind ~w:~n~p", [Idx, Reason]), - {genErr, Idx, []}; - {SizeLeft, Res} when is_integer(SizeLeft) and is_list(Res) -> - ?vtrace("do_get_bulk -> encoded: " - "~n SizeLeft: ~p" - "~n Res: ~w", [SizeLeft, Res]), - case (catch do_get_rep(SizeLeft, MibView, MaxRepetitions, - RestVbs, Res, - length(UResNonRepVbs), GbMaxVBs)) of - {error, Idx, Reason} -> - user_err("failed encoding varbind ~w:~n~p", - [Idx, Reason]), - {genErr, Idx, []}; - Res when is_list(Res) -> - ?vtrace("do get bulk -> Res: " - "~n ~w", [Res]), - {noError, 0, conv_res(Res)}; - {noError, 0, Data} = OK -> - ?vtrace("do get bulk -> OK: " - "~n length(Data): ~w", [length(Data)]), - OK; - Else -> - ?vtrace("do get bulk -> Else: " - "~n ~w", [Else]), - Else - end; - Res when is_list(Res) -> - {noError, 0, conv_res(Res)} - end; + Extra = get(net_if_data), + GetModule = get(get_module), + GetModule:do_get_bulk(MibView, NonRepeaters, MaxRepetitions, + PduMS, Varbinds, GbMaxVBs, + Extra). - {ErrorStatus, Index, _} -> - ?vdebug("do get bulk: " - "~n ErrorStatus: ~p" - "~n Index: ~p",[ErrorStatus, Index]), - {ErrorStatus, Index, []} - end. - -% sz(L) when list(L) -> length(L); -% sz(B) when binary(B) -> size(B); -% sz(_) -> unknown. - -split_vbs(N, Varbinds, Res) when N =< 0 -> {Res, Varbinds}; -split_vbs(N, [H | T], Res) -> split_vbs(N-1, T, [H | Res]); -split_vbs(_N, [], Res) -> {Res, []}. - -enc_vbs(SizeLeft, Vbs) -> - ?vt("enc_vbs -> entry with" - "~n SizeLeft: ~w", [SizeLeft]), - Fun = fun(Vb, {Sz, Res}) when Sz > 0 -> - ?vt("enc_vbs -> (fun) entry with" - "~n Vb: ~p" - "~n Sz: ~p" - "~n Res: ~w", [Vb, Sz, Res]), - case (catch snmp_pdus:enc_varbind(Vb)) of - {'EXIT', Reason} -> - ?vtrace("enc_vbs -> encode failed: " - "~n Reason: ~p", [Reason]), - throw({error, Vb#varbind.org_index, Reason}); - X -> - ?vt("enc_vbs -> X: ~w", [X]), - Lx = length(X), - ?vt("enc_vbs -> Lx: ~w", [Lx]), - if - Lx < Sz -> - {Sz - length(X), [X | Res]}; - true -> - throw(Res) - end - end; - (_Vb, {_Sz, [_H | T]}) -> - ?vt("enc_vbs -> (fun) entry with" - "~n T: ~p", [T]), - throw(T); - (_Vb, {_Sz, []}) -> - ?vt("enc_vbs -> (fun) entry", []), - throw([]) - end, - lists:foldl(Fun, {SizeLeft, []}, Vbs). - -do_get_rep(Sz, MibView, MaxRepetitions, Varbinds, Res, GbNumVBs, GbMaxVBs) - when MaxRepetitions >= 0 -> - do_get_rep(Sz, MibView, 0, MaxRepetitions, Varbinds, Res, - GbNumVBs, GbMaxVBs); -do_get_rep(Sz, MibView, _MaxRepetitions, Varbinds, Res, GbNumVBs, GbMaxVBs) -> - do_get_rep(Sz, MibView, 0, 0, Varbinds, Res, GbNumVBs, GbMaxVBs). - -conv_res(ResVarbinds) -> - conv_res(ResVarbinds, []). -conv_res([VbListOfBytes | T], Bytes) -> - conv_res(T, VbListOfBytes ++ Bytes); -conv_res([], Bytes) -> - Bytes. - -%% The only other value, then a positive integer, is infinity. -do_get_rep(_Sz, _MibView, Count, Max, _, _Res, GbNumVBs, GbMaxVBs) - when (is_integer(GbMaxVBs) andalso (GbNumVBs > GbMaxVBs)) -> - ?vinfo("Max Get-BULK VBs limit (~w) exceeded (~w) when:" - "~n Count: ~p" - "~n Max: ~p", [GbMaxVBs, GbNumVBs, Count, Max]), - {tooBig, 0, []}; -do_get_rep(_Sz, _MibView, Max, Max, _, Res, _GbNumVBs, _GbMaxVBs) -> - ?vt("do_get_rep -> done when: " - "~n Res: ~p", [Res]), - {noError, 0, conv_res(Res)}; -do_get_rep(Sz, MibView, Count, Max, Varbinds, Res, GbNumVBs, GbMaxVBs) -> - ?vt("do_get_rep -> entry when: " - "~n Sz: ~p" - "~n Count: ~p" - "~n Res: ~w", [Sz, Count, Res]), - case try_get_bulk(Sz, MibView, Varbinds, GbMaxVBs) of - {noError, NextVarbinds, SizeLeft, Res2} -> - ?vt("do_get_rep -> noError: " - "~n SizeLeft: ~p" - "~n Res2: ~p", [SizeLeft, Res2]), - do_get_rep(SizeLeft, MibView, Count+1, Max, NextVarbinds, - Res2 ++ Res, - GbNumVBs + length(Varbinds), GbMaxVBs); - {endOfMibView, _NextVarbinds, _SizeLeft, Res2} -> - ?vt("do_get_rep -> endOfMibView: " - "~n Res2: ~p", [Res2]), - {noError, 0, conv_res(Res2 ++ Res)}; - {ErrorStatus, Index} -> - ?vtrace("do_get_rep -> done when error: " - "~n ErrorStatus: ~p" - "~n Index: ~p", [ErrorStatus, Index]), - {ErrorStatus, Index, []} - end. - -org_index_sort_vbs(Vbs) -> - lists:keysort(#varbind.org_index, Vbs). - -try_get_bulk(Sz, MibView, Varbinds, GbMaxVBs) -> - ?vt("try_get_bulk -> entry with" - "~n Sz: ~w" - "~n MibView: ~w" - "~n Varbinds: ~w", [Sz, MibView, Varbinds]), - case do_get_next(MibView, Varbinds, GbMaxVBs) of - {noError, 0, UNextVarbinds} -> - ?vt("try_get_bulk -> noError: " - "~n UNextVarbinds: ~p", [UNextVarbinds]), - NextVarbinds = org_index_sort_vbs(UNextVarbinds), - case (catch enc_vbs(Sz, NextVarbinds)) of - {error, Idx, Reason} -> - user_err("failed encoding varbind ~w:~n~p", [Idx, Reason]), - ?vtrace("try_get_bulk -> encode error: " - "~n Idx: ~p" - "~n Reason: ~p", [Idx, Reason]), - {genErr, Idx}; - {SizeLeft, Res} when is_integer(SizeLeft) andalso - is_list(Res) -> - ?vt("try get bulk -> encode ok: " - "~n SizeLeft: ~w" - "~n Res: ~w", [SizeLeft, Res]), - {check_end_of_mibview(NextVarbinds), - NextVarbinds, SizeLeft, Res}; - Res when is_list(Res) -> - ?vt("try get bulk -> Res: " - "~n ~w", [Res]), - {endOfMibView, [], 0, Res} - end; - {ErrorStatus, Index, _} -> - ?vt("try_get_bulk -> error: " - "~n ErrorStatus: ~p" - "~n Index: ~p", [ErrorStatus, Index]), - {ErrorStatus, Index} - end. - -%% If all variables in this pass are endOfMibView, -%% there is no reason to continue. -check_end_of_mibview([#varbind{value = endOfMibView} | T]) -> - check_end_of_mibview(T); -check_end_of_mibview([]) -> endOfMibView; -check_end_of_mibview(_) -> noError. %%%-------------------------------------------------- @@ -3834,14 +2822,11 @@ do_subagent_set(Arguments) -> SetModule = get(set_module), apply(SetModule, do_subagent_set, [Arguments]). + + %%%----------------------------------------------------------------- %%% 7. Misc functions %%%----------------------------------------------------------------- -sort_varbindlist(Varbinds) -> - snmpa_svbl:sort_varbindlist(get(mibserver), Varbinds). - -sa_split(SubagentVarbinds) -> - snmpa_svbl:sa_split(SubagentVarbinds). make_response_pdu(ReqId, ErrStatus, ErrIndex, OrgVarbinds, _ResponseVarbinds) when ErrIndex =/= 0 -> @@ -4139,6 +3124,7 @@ report_err(Val, Mfa, Err) -> user_err("Got ~p from ~w. Using ~w", [Val, Mfa, Err]), {error, Err}. + is_valid_pdu_type('get-request') -> true; is_valid_pdu_type('get-next-request') -> true; is_valid_pdu_type('get-bulk-request') -> true; @@ -4176,33 +3162,8 @@ mapfoldl(F, Eas, Accu0, [Hd|Tail]) -> mapfoldl(_F, _Eas, Accu, []) -> {Accu,[]}. -%%----------------------------------------------------------------- -%% Runtime debugging of the agent. -%%----------------------------------------------------------------- - -dbg_apply(M,F,A) -> - case get(verbosity) of - silence -> - apply(M,F,A); - _ -> - ?vlog("~n apply: ~w,~w,~p~n", [M,F,A]), - Res = (catch apply(M,F,A)), - case Res of - {'EXIT', Reason} -> - ?vinfo("Call to: " - "~n Module: ~p" - "~n Function: ~p" - "~n Args: ~p" - "~n" - "~nresulted in an exit" - "~n" - "~n ~p", [M, F, A, Reason]); - _ -> - ?vlog("~n returned: ~p", [Res]) - end, - Res - end. +%% --------------------------------------------------------------------- short_name(none) -> ma; short_name(_Pid) -> sa. @@ -4450,6 +3411,9 @@ get_mib_storage(Opts) -> get_set_mechanism(Opts) -> get_option(set_mechanism, Opts, snmpa_set). +get_get_mechanism(Opts) -> + get_option(get_mechanism, Opts, snmpa_get). + get_authentication_service(Opts) -> get_option(authentication_service, Opts, snmpa_acm). diff --git a/lib/snmp/src/agent/snmpa_app.erl b/lib/snmp/src/agent/snmpa_app.erl index 86ff145e93..c00929c334 100644 --- a/lib/snmp/src/agent/snmpa_app.erl +++ b/lib/snmp/src/agent/snmpa_app.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -67,6 +67,7 @@ convert_config(Opts) -> SaVerb = get_sub_agent_verbosity(Opts), [{agent_type, AgentType}, {agent_verbosity, SaVerb}, + {get_mechanism, snmpa_get}, {set_mechanism, SetModule}, {authentication_service, AuthModule}, {priority, Prio}, @@ -97,6 +98,7 @@ convert_config(Opts) -> {verbosity, ConfVerb}], [{agent_type, AgentType}, {agent_verbosity, MaVerb}, + {get_mechanism, snmpa_get}, {set_mechanism, SetModule}, {authentication_service, AuthModule}, {db_dir, DbDir}, diff --git a/lib/snmp/src/agent/snmpa_get.erl b/lib/snmp/src/agent/snmpa_get.erl new file mode 100644 index 0000000000..e67975a67d --- /dev/null +++ b/lib/snmp/src/agent/snmpa_get.erl @@ -0,0 +1,1150 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-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(snmpa_get). + +-behaviour(snmpa_get_mechanism). + + +%%%----------------------------------------------------------------- +%%% snmpa_get_mechanism exports +%%%----------------------------------------------------------------- + +-export([ + do_get/3, do_get/4, + do_get_next/3, + do_get_bulk/7 + ]). + +-define(VMODULE,"GET"). +-include("snmpa_internal.hrl"). +-include("snmp_types.hrl"). +-include("snmp_debug.hrl"). +-include("snmp_verbosity.hrl"). + +-ifndef(default_verbosity). +-define(default_verbosity,silence). +-endif. + +-define(empty_pdu_size, 21). + +-ifdef(snmp_extended_verbosity). +-define(vt(F,A), ?vtrace(F, A)). +-else. +-define(vt(_F, _A), ok). +-endif. + + +-define(AGENT, snmpa_agent). +-define(LIB, snmpa_get_lib). + + + +%%%----------------------------------------------------------------- +%%% 3. GET REQUEST +%%% -------------- +%%% According to RFC1157, section 4.1.2 and RFC1905, section 4.2.1. +%%% In rfc1157:4.1.2 it isn't specified if noSuchName should be +%%% returned even if some other varbind generates a genErr. +%%% In rfc1905:4.2.1 this is not a problem since exceptions are +%%% used, and thus a genErr will be returned anyway. +%%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Func: do_get/2 +%% Purpose: Handles all VBs in a request that is inside the +%% mibview (local). +%% Returns: {noError, 0, ListOfNewVarbinds} | +%% {ErrorStatus, ErrorIndex, []} +%%----------------------------------------------------------------- + +do_get(UnsortedVarbinds, IsNotification, _Extra) -> + {MyVarbinds, SubagentVarbinds} = ?LIB:agent_sort_vbs(UnsortedVarbinds), + case do_get_local(MyVarbinds, IsNotification) of + {noError, 0, NewMyVarbinds} -> + case do_get_subagents(SubagentVarbinds, IsNotification) of + {noError, 0, NewSubagentVarbinds} -> + {noError, 0, NewMyVarbinds ++ NewSubagentVarbinds}; + {ErrorStatus, ErrorIndex, _} -> + {ErrorStatus, ErrorIndex, []} + end; + {ErrorStatus, ErrorIndex, _} -> + {ErrorStatus, ErrorIndex, []} + end. + + +%%----------------------------------------------------------------- +%% Func: do_get/3 +%% Purpose: do_get handles "getRequests". +%% Pre: incoming varbinds have type == 'NULL', value == unSpecified +%% Returns: {noError, 0, ListOfNewVarbinds} | +%% {ErrorStatus, ErrorIndex, []} +%%----------------------------------------------------------------- + +do_get(MibView, UnsortedVarbinds, IsNotification, Extra) -> + ?vtrace("do_get -> entry with" + "~n MibView: ~p" + "~n UnsortedVarbinds: ~p" + "~n IsNotification: ~p", + [MibView, UnsortedVarbinds, IsNotification]), + %% This is me, the master, so go ahead + {OutSideView, InSideView} = ?LIB:split_vbs_view(UnsortedVarbinds, MibView), + {Error, Index, NewVbs} = do_get(InSideView, IsNotification, Extra), + {Error, Index, NewVbs ++ OutSideView}. + + + +%%----------------------------------------------------------------- +%% Func: do_get_local/2,3 +%% Purpose: Loop the variablebindings list. We know that each varbind +%% in that list belongs to us. +%% Returns: {noError, 0, ListOfNewVarbinds} | +%% {ErrorStatus, ErrorIndex, []} +%%----------------------------------------------------------------- + +do_get_local(VBs, IsNotification) -> + do_get_local(VBs, [], IsNotification). + +do_get_local([Vb | Vbs], Res, IsNotification) -> + case try_get(Vb, IsNotification) of + NewVb when is_record(NewVb, varbind) -> + do_get_local(Vbs, [NewVb | Res], IsNotification); + ListOfNewVb when is_list(ListOfNewVb) -> + do_get_local(Vbs, lists:append(ListOfNewVb, Res), IsNotification); + {error, Error, OrgIndex} -> + {Error, OrgIndex, []} + end; +do_get_local([], Res, _IsNotification) -> + {noError, 0, Res}. + + + +%%----------------------------------------------------------------- +%% Func: do_get_subagents/2 +%% Purpose: Loop the list of varbinds for different subagents. +%% For each of them, call sub_agent_get to retreive +%% the values for them. +%% Returns: {noError, 0, ListOfNewVarbinds} | +%% {ErrorStatus, ErrorIndex, []} +%%----------------------------------------------------------------- +do_get_subagents(SubagentVarbinds, IsNotification) -> + do_get_subagents(SubagentVarbinds, [], IsNotification). +do_get_subagents([{SubAgentPid, SAVbs} | Tail], Res, IsNotification) -> + {_SAOids, Vbs} = ?LIB:sa_split(SAVbs), + case catch ?AGENT:subagent_get(SubAgentPid, Vbs, IsNotification) of + {noError, 0, NewVbs} -> + do_get_subagents(Tail, lists:append(NewVbs, Res), IsNotification); + {ErrorStatus, ErrorIndex, _} -> + {ErrorStatus, ErrorIndex, []}; + {'EXIT', Reason} -> + ?LIB:user_err("Lost contact with subagent (get) ~w. Using genErr", + [Reason]), + {genErr, 0, []} + end; +do_get_subagents([], Res, _IsNotification) -> + {noError, 0, Res}. + + +%%----------------------------------------------------------------- +%% Func: try_get/2 +%% Returns: {error, ErrorStatus, OrgIndex} | +%% #varbind | +%% List of #varbind +%%----------------------------------------------------------------- +try_get(IVb, IsNotification) when is_record(IVb, ivarbind) -> + ?vtrace("try_get(ivarbind) -> entry with" + "~n IVb: ~p", [IVb]), + get_var_value_from_ivb(IVb, IsNotification); +try_get({TableOid, TableVbs}, IsNotification) -> + ?vtrace("try_get(table) -> entry with" + "~n TableOid: ~p" + "~n TableVbs: ~p", [TableOid, TableVbs]), + [#ivarbind{mibentry = MibEntry}|_] = TableVbs, + {NoAccessVbs, AccessVbs} = + check_all_table_vbs(TableVbs, IsNotification, [], []), + case get_tab_value_from_mib(MibEntry, TableOid, AccessVbs) of + {error, ErrorStatus, OrgIndex} -> + {error, ErrorStatus, OrgIndex}; + NVbs -> + NVbs ++ NoAccessVbs + end. + +%%----------------------------------------------------------------- +%% Make sure all requested columns are accessible. +%%----------------------------------------------------------------- +check_all_table_vbs([IVb| IVbs], IsNotification, NoA, A) -> + #ivarbind{mibentry = Me, varbind = Vb} = IVb, + case Me#me.access of + 'not-accessible' -> + NNoA = [Vb#varbind{value = noSuchInstance} | NoA], + check_all_table_vbs(IVbs, IsNotification, NNoA, A); + 'accessible-for-notify' when IsNotification =:= false -> + NNoA = [Vb#varbind{value = noSuchInstance} | NoA], + check_all_table_vbs(IVbs, IsNotification, NNoA, A); + 'write-only' -> + NNoA = [Vb#varbind{value = noSuchInstance} | NoA], + check_all_table_vbs(IVbs, IsNotification, NNoA, A); + _ -> + check_all_table_vbs(IVbs, IsNotification, NoA, [IVb | A]) + end; +check_all_table_vbs([], _IsNotification, NoA, A) -> {NoA, A}. + +%%----------------------------------------------------------------- +%% Returns: {error, ErrorStatus, OrgIndex} | +%% #varbind +%%----------------------------------------------------------------- +get_var_value_from_ivb(IVb, IsNotification) + when IVb#ivarbind.status =:= noError -> + ?vtrace("get_var_value_from_ivb(noError) -> entry", []), + #ivarbind{mibentry = Me, varbind = Vb} = IVb, + #varbind{org_index = OrgIndex, oid = Oid} = Vb, + case Me#me.access of + 'not-accessible' -> + Vb#varbind{value = noSuchInstance}; + 'accessible-for-notify' when IsNotification =:= false -> + Vb#varbind{value = noSuchInstance}; + 'write-only' -> + Vb#varbind{value = noSuchInstance}; + _ -> + case get_var_value_from_mib(Me, Oid) of + {value, Type, Value} -> + Vb#varbind{variabletype = Type, value = Value}; + {error, ErrorStatus} -> + {error, ErrorStatus, OrgIndex} + end + end; +get_var_value_from_ivb(#ivarbind{status = Status, varbind = Vb}, _) -> + ?vtrace("get_var_value_from_ivb(~p) -> entry", [Status]), + Vb#varbind{value = Status}. + +%%----------------------------------------------------------------- +%% Func: get_var_value_from_mib/1 +%% Purpose: +%% Returns: {error, ErrorStatus} | +%% {value, Type, Value} +%%----------------------------------------------------------------- +%% Pre: Oid is a correct instance Oid (lookup checked that). +%% Returns: A correct return value (see ?AGENT:make_value_a_correct_value) +get_var_value_from_mib(#me{entrytype = variable, + asn1_type = ASN1Type, + mfa = {Mod, Func, Args}}, + _Oid) -> + ?vtrace("get_var_value_from_mib(variable) -> entry when" + "~n Mod: ~p" + "~n Func: ~p" + "~n Args: ~p", [Mod, Func, Args]), + Result = (catch ?LIB:dbg_apply(Mod, Func, [get | Args])), + %% mib shall return {value, <a-nice-value-within-range>} | + %% {noValue, noSuchName} (v1) | + %% {noValue, noSuchObject | noSuchInstance} (v2, v1) + %% everything else (including 'genErr') will generate 'genErr'. + ?AGENT:make_value_a_correct_value(Result, ASN1Type, {Mod, Func, Args}); + +get_var_value_from_mib(#me{entrytype = table_column, + oid = MeOid, + asn1_type = ASN1Type, + mfa = {Mod, Func, Args}}, + Oid) -> + ?vtrace("get_var_value_from_mib(table_column) -> entry when" + "~n MeOid: ~p" + "~n Mod: ~p" + "~n Func: ~p" + "~n Args: ~p" + "~n Oid: ~p", [MeOid, Mod, Func, Args, Oid]), + Col = lists:last(MeOid), + Indexes = snmp_misc:diff(Oid, MeOid), + [Result] = (catch ?LIB:dbg_apply(Mod, Func, [get, Indexes, [Col] | Args])), + ?AGENT:make_value_a_correct_value(Result, ASN1Type, + {Mod, Func, Args, Indexes, Col}). + + +%% For table operations we need to pass RestOid down to the table-function. +%% Its up to the table-function to check for noSuchInstance (ex: a +%% non-existing row). +%% Returns: {error, ErrorStatus, OrgIndex} | +%% {value, Type, Value} +get_tab_value_from_mib(#me{mfa = {Mod, Func, Args}}, TableOid, TableVbs) -> + ?vtrace("get_tab_value_from_mib -> entry when" + "~n Mod: ~p" + "~n Func: ~p" + "~n Args: ~p", [Mod, Func, Args]), + TableOpsWithShortOids = ?LIB:delete_prefixes(TableOid, TableVbs), + SortedVBsRows = snmpa_svbl:sort_varbinds_rows(TableOpsWithShortOids), + case get_value_all_rows(SortedVBsRows, Mod, Func, Args, []) of + {Error, Index} -> + #ivarbind{varbind = Vb} = lists:nth(Index, TableVbs), + {error, Error, Vb#varbind.org_index}; + ListOfValues -> + merge_varbinds_and_value(TableVbs, ListOfValues) + end. + +%%----------------------------------------------------------------- +%% Values is a scrambled list of {CorrectValue, Index}, where Index +%% is index into the #ivarbind list. So for each Value, we must +%% find the corresponding #ivarbind, and merge them into a new +%% #varbind. +%% The Values list comes from validate_tab_res. +%%----------------------------------------------------------------- +merge_varbinds_and_value(IVbs, [{{value, Type, Value}, Index} | Values]) -> + #ivarbind{varbind = Vb} = lists:nth(Index, IVbs), + [Vb#varbind{variabletype = Type, value = Value} | + merge_varbinds_and_value(IVbs, Values)]; +merge_varbinds_and_value(_, []) -> []. + +get_value_all_rows([{[], OrgCols} | Rows], Mod, Func, Args, Res) -> + ?vtrace("get_value_all_rows -> entry when" + "~n OrgCols: ~p", [OrgCols]), + Cols = [{{value, noValue, noSuchInstance}, Index} || + {_Col, _ASN1Type, Index} <- OrgCols], + NewRes = lists:append(Cols, Res), + get_value_all_rows(Rows, Mod, Func, Args, NewRes); +get_value_all_rows([{RowIndex, OrgCols} | Rows], Mod, Func, Args, Res) -> + ?vtrace("get_value_all_rows -> entry when" + "~n RowIndex: ~p" + "~n OrgCols: ~p", [RowIndex, OrgCols]), + {DOrgCols, Dup} = remove_duplicates(OrgCols), + Cols = delete_index(DOrgCols), + Result = (catch ?LIB:dbg_apply(Mod, Func, [get, RowIndex, Cols | Args])), + case validate_tab_res(Result, DOrgCols, {Mod, Func, Args}) of + Values when is_list(Values) -> + NVals = restore_duplicates(Dup, Values), + NewRes = lists:append(NVals, Res), + get_value_all_rows(Rows, Mod, Func, Args, NewRes); + {error, ErrorStatus, Index} -> + ?AGENT:validate_err(row_set, {ErrorStatus, Index}, {Mod, Func, Args}) + end; +get_value_all_rows([], _Mod, _Func, _Args, Res) -> + ?vtrace("get_value_all_rows -> entry when done" + "~n Res: ~p", [Res]), + Res. + +%%----------------------------------------------------------------- +%% Args: {RowIndex, list of {ShortOid, ASN1Type}} +%% Returns: list of Col +%%----------------------------------------------------------------- +delete_index([{Col, _Val, _OrgIndex} | T]) -> + [Col | delete_index(T)]; +delete_index([]) -> []. + +%%----------------------------------------------------------------- +%% This function is called before 'get' on a table, and removes +%% any duplicate columns. It returns {Cols, DupInfo}. The Cols +%% are the unique columns. The instrumentation function is +%% called to get the values. These values, together with the +%% DupInfo, is later passed to restore_duplicates, which uses +%% the retrieved values to reconstruct the original column list, +%% but with the retrieved value for each column. +%%----------------------------------------------------------------- +remove_duplicates(Cols) -> + remove_duplicates(Cols, [], []). + + +remove_duplicates([{Col, V1, OrgIdx1}, {Col, V2, OrgIdx2} | T], NCols, Dup) -> + remove_duplicates([{Col, V1, OrgIdx1} | T], NCols, + [{Col, V2, OrgIdx2} | Dup]); +remove_duplicates([Col | T], NCols, Dup) -> + remove_duplicates(T, [Col | NCols], Dup); +remove_duplicates([], NCols, Dup) -> + {lists:reverse(NCols), lists:reverse(Dup)}. + +restore_duplicates([], Cols) -> + [{Val, OrgIndex} || {_Col, Val, OrgIndex} <- Cols]; +restore_duplicates([{Col, _Val2, OrgIndex2} | Dup], + [{Col, NVal, OrgIndex1} | Cols]) -> + [{NVal, OrgIndex2} | + restore_duplicates(Dup, [{Col, NVal, OrgIndex1} | Cols])]; +restore_duplicates(Dup, [{_Col, Val, OrgIndex} | T]) -> + [{Val, OrgIndex} | restore_duplicates(Dup, T)]. + + + +%%----------------------------------------------------------------- +%% Three cases: +%% 1) All values ok +%% 2) table_func returned {Error, ...} +%% 3) Some value in Values list is erroneous. +%% Args: Value is a list of values from table_func(get..) +%% OrgCols is a list with {Col, ASN1Type, OrgIndex} +%% each element in Values and OrgCols correspond to each +%% other. +%%----------------------------------------------------------------- +validate_tab_res(Values, OrgCols, Mfa) when is_list(Values) -> + {_Col, _ASN1Type, OneIdx} = hd(OrgCols), + validate_tab_res(Values, OrgCols, Mfa, [], OneIdx); +validate_tab_res({noValue, Error}, OrgCols, Mfa) -> + Values = lists:duplicate(length(OrgCols), {noValue, Error}), + validate_tab_res(Values, OrgCols, Mfa); +validate_tab_res({genErr, Col}, OrgCols, Mfa) -> + case lists:keysearch(Col, 1, OrgCols) of + {value, {_Col, _ASN1Type, Index}} -> + {error, genErr, Index}; + _ -> + ?LIB:user_err("Invalid column in {genErr, ~w} from ~w (get)", + [Col, Mfa]), + [{_Col, _ASN1Type, Index} | _] = OrgCols, + {error, genErr, Index} + end; +validate_tab_res(genErr, [{_Col, __ASN1Type, Index} | _OrgCols], _Mfa) -> + {error, genErr, Index}; +validate_tab_res(Error, [{_Col, _ASN1Type, Index} | _OrgCols], Mfa) -> + ?LIB:user_err("Invalid return value ~w from ~w (get)",[Error, Mfa]), + {error, genErr, Index}. + +validate_tab_res([Value | Values], + [{Col, ASN1Type, Index} | OrgCols], + Mfa, Res, I) -> + %% This one makes it possible to return a list of genErr, which + %% is not allowed according to the manual. But that's ok, as + %% everything else will generate a genErr! (the only problem is + %% that it won't generate a user_error). + case ?AGENT:make_value_a_correct_value(Value, ASN1Type, Mfa) of + {error, ErrorStatus} -> + {error, ErrorStatus, Index}; + CorrectValue -> + NewRes = [{Col, CorrectValue, Index} | Res], + validate_tab_res(Values, OrgCols, Mfa, NewRes, I) + end; +validate_tab_res([], [], _Mfa, Res, _I) -> + lists:reverse(Res); +validate_tab_res([], [{_Col, _ASN1Type, Index}|_], Mfa, _Res, _I) -> + ?LIB:user_err("Too few values returned from ~w (get)", [Mfa]), + {error, genErr, Index}; +validate_tab_res(_TooMany, [], Mfa, _Res, I) -> + ?LIB:user_err("Too many values returned from ~w (get)", [Mfa]), + {error, genErr, I}. + + + +%%%----------------------------------------------------------------- +%%% 4. GET-NEXT REQUEST +%%% -------------- +%%% According to RFC1157, section 4.1.3 and RFC1905, section 4.2.2. +%%%----------------------------------------------------------------- +%%----------------------------------------------------------------- +%% Func: do_get_next/3 +%% Purpose: do_get_next handles "getNextRequests". +%% Note: Even if it is SNMPv1, a varbind's value can be +%% endOfMibView. This is converted to noSuchName in process_pdu. +%% Returns: {noError, 0, ListOfNewVarbinds} | +%% {ErrorStatus, ErrorIndex, []} +%% Note2: ListOfNewVarbinds is not sorted in any order!!! +%% Alg: First, the variables are sorted in OID order. +%% +%% Second, next in the MIB is performed for each OID, and +%% the result is collected as: if next oid is a variable, +%% perform a get to retrieve its value; if next oid is in a +%% table, save this value and continue until we get an oid +%% outside this table. Then perform get_next on the table, +%% and continue with all endOfTables and the oid outside the +%% table; if next oid is an subagent, save this value and +%% continue as in the table case. +%% +%% Third, each response is checked for endOfMibView, or (for +%% subagents) that the Oid returned has the correct prefix. +%% (This is necessary since an SA can be registered under many +%% separated subtrees, and if the last variable in the first +%% subtree is requested in a next, the SA will return the first +%% variable in the second subtree. This might be working, since +%% there may be a variable in between these subtrees.) For each +%% of these, a new get-next is performed, one at a time. +%% This alg. might be optimised in several ways. The most +%% striking one is that the same SA might be called several +%% times, when one time should be enough. But it isn't clear +%% that this really matters, since many nexts across the same +%% subagent must be considered to be very rare. +%%----------------------------------------------------------------- + +do_get_next(MibView, UnsortedVBs, _Extra) -> + do_get_next2(MibView, UnsortedVBs, infinity). + +%% The third argument is only used if we are called as result +%% of a get-bulk request. +do_get_next2(_MibView, UnsortedVarbinds, GbMaxVBs) + when (is_integer(GbMaxVBs) andalso (length(UnsortedVarbinds) > GbMaxVBs)) -> + {tooBig, 0, []}; % What is the correct index in this case? +do_get_next2(MibView, UnsortedVBs, GbMaxVBs) -> + ?vt("do_get_next2 -> entry when" + "~n MibView: ~p" + "~n UnsortedVBs: ~p", [MibView, UnsortedVBs]), + SortedVBs = ?LIB:oid_sort_vbs(UnsortedVBs), + ?vt("do_get_next -> " + "~n SortedVBs: ~p", [SortedVBs]), + next_loop_varbinds([], SortedVBs, MibView, [], [], GbMaxVBs). + +next_loop_varbinds(_, Vbs, _MibView, Res, _LAVb, GbMaxVBs) + when (is_integer(GbMaxVBs) andalso + ((length(Vbs) + length(Res)) > GbMaxVBs)) -> + {tooBig, 0, []}; % What is the correct index in this case? + +%% LAVb is Last Accessible Vb +next_loop_varbinds([], [Vb | Vbs], MibView, Res, LAVb, GbMaxVBs) -> + ?vt("next_loop_varbinds -> entry when" + "~n Vb: ~p" + "~n MibView: ~p", [Vb, MibView]), + case varbind_next(Vb, MibView) of + endOfMibView -> + ?vt("next_loop_varbind -> endOfMibView", []), + RVb = if LAVb =:= [] -> Vb; + true -> LAVb + end, + NewVb = RVb#varbind{variabletype = 'NULL', value = endOfMibView}, + next_loop_varbinds([], Vbs, MibView, [NewVb | Res], [], GbMaxVBs); + + {variable, ME, VarOid} when ((ME#me.access =/= 'not-accessible') andalso + (ME#me.access =/= 'write-only') andalso + (ME#me.access =/= 'accessible-for-notify')) -> + ?vt("next_loop_varbind -> variable: " + "~n ME: ~p" + "~n VarOid: ~p", [ME, VarOid]), + case try_get_instance(Vb, ME) of + {value, noValue, _NoSuchSomething} -> + ?vt("next_loop_varbind -> noValue", []), + %% Try next one + NewVb = Vb#varbind{oid = VarOid, + value = 'NULL'}, + next_loop_varbinds([], [NewVb | Vbs], MibView, Res, [], + GbMaxVBs); + {value, Type, Value} -> + ?vt("next_loop_varbind -> value" + "~n Type: ~p" + "~n Value: ~p", [Type, Value]), + NewVb = Vb#varbind{oid = VarOid, + variabletype = Type, + value = Value}, + next_loop_varbinds([], Vbs, MibView, [NewVb | Res], [], + GbMaxVBs); + {error, ErrorStatus} -> + ?vdebug("next loop varbinds:" + "~n ErrorStatus: ~p",[ErrorStatus]), + {ErrorStatus, Vb#varbind.org_index, []} + end; + {variable, _ME, VarOid} -> + ?vt("next_loop_varbind -> variable: " + "~n VarOid: ~p", [VarOid]), + RVb = if LAVb =:= [] -> Vb; + true -> LAVb + end, + NewVb = Vb#varbind{oid = VarOid, value = 'NULL'}, + next_loop_varbinds([], [NewVb | Vbs], MibView, Res, RVb, GbMaxVBs); + {table, TableOid, TableRestOid, ME} -> + ?vt("next_loop_varbind -> table: " + "~n TableOid: ~p" + "~n TableRestOid: ~p" + "~n ME: ~p", [TableOid, TableRestOid, ME]), + next_loop_varbinds({table, TableOid, ME, + [{tab_oid(TableRestOid), Vb}]}, + Vbs, MibView, Res, [], GbMaxVBs); + {subagent, SubAgentPid, SAOid} -> + ?vt("next_loop_varbind -> subagent: " + "~n SubAgentPid: ~p" + "~n SAOid: ~p", [SubAgentPid, SAOid]), + NewVb = Vb#varbind{variabletype = 'NULL', value = 'NULL'}, + next_loop_varbinds({subagent, SubAgentPid, SAOid, [NewVb]}, + Vbs, MibView, Res, [], GbMaxVBs) + end; +next_loop_varbinds({table, TableOid, ME, TabOids}, + [Vb | Vbs], MibView, Res, _LAVb, GbMaxVBs) -> + ?vt("next_loop_varbinds(table) -> entry with" + "~n TableOid: ~p" + "~n Vb: ~p", [TableOid, Vb]), + case varbind_next(Vb, MibView) of + {table, TableOid, TableRestOid, _ME} -> + next_loop_varbinds({table, TableOid, ME, + [{tab_oid(TableRestOid), Vb} | TabOids]}, + Vbs, MibView, Res, [], GbMaxVBs); + _ -> + case get_next_table(ME, TableOid, TabOids, MibView) of + {ok, TabRes, TabEndOfTabVbs} -> + NewVbs = lists:append(TabEndOfTabVbs, [Vb | Vbs]), + NewRes = lists:append(TabRes, Res), + next_loop_varbinds([], NewVbs, MibView, NewRes, [], + GbMaxVBs); + {ErrorStatus, OrgIndex} -> + ?vdebug("next loop varbinds: next varbind" + "~n ErrorStatus: ~p" + "~n OrgIndex: ~p", + [ErrorStatus,OrgIndex]), + {ErrorStatus, OrgIndex, []} + end + end; +next_loop_varbinds({table, TableOid, ME, TabOids}, + [], MibView, Res, _LAVb, GbMaxVBs) -> + ?vt("next_loop_varbinds(table) -> entry with" + "~n TableOid: ~p", [TableOid]), + case get_next_table(ME, TableOid, TabOids, MibView) of + {ok, TabRes, TabEndOfTabVbs} -> + ?vt("next_loop_varbinds(table) -> get_next_table result:" + "~n TabRes: ~p" + "~n TabEndOfTabVbs: ~p", [TabRes, TabEndOfTabVbs]), + NewRes = lists:append(TabRes, Res), + next_loop_varbinds([], TabEndOfTabVbs, MibView, NewRes, [], + GbMaxVBs); + {ErrorStatus, OrgIndex} -> + ?vdebug("next loop varbinds: next table" + "~n ErrorStatus: ~p" + "~n OrgIndex: ~p", + [ErrorStatus,OrgIndex]), + {ErrorStatus, OrgIndex, []} + end; +next_loop_varbinds({subagent, SAPid, SAOid, SAVbs}, + [Vb | Vbs], MibView, Res, _LAVb, GbMaxVBs) -> + ?vt("next_loop_varbinds(subagent) -> entry with" + "~n SAPid: ~p" + "~n SAOid: ~p" + "~n Vb: ~p", [SAPid, SAOid, Vb]), + case varbind_next(Vb, MibView) of + {subagent, _SubAgentPid, SAOid} -> + next_loop_varbinds({subagent, SAPid, SAOid, + [Vb | SAVbs]}, + Vbs, MibView, Res, [], GbMaxVBs); + _ -> + case get_next_sa(SAPid, SAOid, SAVbs, MibView) of + {ok, SARes, SAEndOfMibViewVbs} -> + NewVbs = lists:append(SAEndOfMibViewVbs, [Vb | Vbs]), + NewRes = lists:append(SARes, Res), + next_loop_varbinds([], NewVbs, MibView, NewRes, [], + GbMaxVBs); + {noSuchName, OrgIndex} -> + %% v1 reply, treat this Vb as endOfMibView, and try again + %% for the others. + case lists:keysearch(OrgIndex, #varbind.org_index, SAVbs) of + {value, EVb} -> + NextOid = next_oid(SAOid), + EndOfVb = + EVb#varbind{oid = NextOid, + value = {endOfMibView, NextOid}}, + case lists:delete(EVb, SAVbs) of + [] -> + next_loop_varbinds([], [EndOfVb, Vb | Vbs], + MibView, Res, [], + GbMaxVBs); + TryAgainVbs -> + next_loop_varbinds({subagent, SAPid, SAOid, + TryAgainVbs}, + [EndOfVb, Vb | Vbs], + MibView, Res, [], + GbMaxVBs) + end; + false -> + %% bad index from subagent + {genErr, (hd(SAVbs))#varbind.org_index, []} + end; + {ErrorStatus, OrgIndex} -> + ?vdebug("next loop varbinds: next subagent" + "~n Vb: ~p" + "~n ErrorStatus: ~p" + "~n OrgIndex: ~p", + [Vb,ErrorStatus,OrgIndex]), + {ErrorStatus, OrgIndex, []} + end + end; +next_loop_varbinds({subagent, SAPid, SAOid, SAVbs}, + [], MibView, Res, _LAVb, GbMaxVBs) -> + ?vt("next_loop_varbinds(subagent) -> entry with" + "~n SAPid: ~p" + "~n SAOid: ~p", [SAPid, SAOid]), + case get_next_sa(SAPid, SAOid, SAVbs, MibView) of + {ok, SARes, SAEndOfMibViewVbs} -> + NewRes = lists:append(SARes, Res), + next_loop_varbinds([], SAEndOfMibViewVbs, MibView, NewRes, [], + GbMaxVBs); + {noSuchName, OrgIndex} -> + %% v1 reply, treat this Vb as endOfMibView, and try again for + %% the others. + case lists:keysearch(OrgIndex, #varbind.org_index, SAVbs) of + {value, EVb} -> + NextOid = next_oid(SAOid), + EndOfVb = EVb#varbind{oid = NextOid, + value = {endOfMibView, NextOid}}, + case lists:delete(EVb, SAVbs) of + [] -> + next_loop_varbinds([], [EndOfVb], MibView, Res, [], + GbMaxVBs); + TryAgainVbs -> + next_loop_varbinds({subagent, SAPid, SAOid, + TryAgainVbs}, + [EndOfVb], MibView, Res, [], + GbMaxVBs) + end; + false -> + %% bad index from subagent + {genErr, (hd(SAVbs))#varbind.org_index, []} + end; + {ErrorStatus, OrgIndex} -> + ?vdebug("next loop varbinds: next subagent" + "~n ErrorStatus: ~p" + "~n OrgIndex: ~p", + [ErrorStatus,OrgIndex]), + {ErrorStatus, OrgIndex, []} + end; +next_loop_varbinds([], [], _MibView, Res, _LAVb, _GbMaxVBs) -> + ?vt("next_loop_varbinds -> entry when done", []), + {noError, 0, Res}. + +try_get_instance(_Vb, #me{mfa = {M, F, A}, asn1_type = ASN1Type}) -> + ?vtrace("try_get_instance -> entry with" + "~n M: ~p" + "~n F: ~p" + "~n A: ~p", [M,F,A]), + Result = (catch ?LIB:dbg_apply(M, F, [get | A])), + % mib shall return {value, <a-nice-value-within-range>} | + % {noValue, noSuchName} (v1) | + % {noValue, noSuchObject | noSuchInstance} (v2, v1) + % everything else (including 'genErr') will generate 'genErr'. + ?AGENT:make_value_a_correct_value(Result, ASN1Type, {M, F, A}). + +tab_oid([]) -> [0]; +tab_oid(X) -> X. + + +%%----------------------------------------------------------------- +%% Perform a next, using the varbinds Oid if value is simple +%% value. If value is {endOf<something>, NextOid}, use NextOid. +%% This case happens when a table has returned endOfTable, or +%% a subagent has returned endOfMibView. +%%----------------------------------------------------------------- +varbind_next(#varbind{value = Value, oid = Oid}, MibView) -> + ?vt("varbind_next -> entry with" + "~n Value: ~p" + "~n Oid: ~p" + "~n MibView: ~p", [Value, Oid, MibView]), + case Value of + {endOfTable, NextOid} -> + snmpa_mib:next(get(mibserver), NextOid, MibView); + {endOfMibView, NextOid} -> + snmpa_mib:next(get(mibserver), NextOid, MibView); + _ -> + snmpa_mib:next(get(mibserver), Oid, MibView) + end. + +get_next_table(#me{mfa = {M, F, A}}, TableOid, TableOids, MibView) -> + % We know that all TableOids have at least a column number as oid + ?vt("get_next_table -> entry with" + "~n M: ~p" + "~n F: ~p" + "~n A: ~p" + "~n TableOid: ~p" + "~n TableOids: ~p" + "~n MibView: ~p", [M, F, A, TableOid, TableOids, MibView]), + Sorted = snmpa_svbl:sort_varbinds_rows(TableOids), + case get_next_values_all_rows(Sorted, M,F,A, [], TableOid) of + NewVbs when is_list(NewVbs) -> + ?vt("get_next_table -> " + "~n NewVbs: ~p", [NewVbs]), + % We must now check each Vb for endOfTable and that it is + % in the MibView. If not, it becomes a endOfTable. We + % collect all of these together. + transform_tab_next_result(NewVbs, {[], []}, MibView); + {ErrorStatus, OrgIndex} -> + {ErrorStatus, OrgIndex} + end. + +get_next_values_all_rows([Row | Rows], M, F, A, Res, TabOid) -> + {RowIndex, TableOids} = Row, + Cols = delete_index(TableOids), + ?vt("get_next_values_all_rows -> " + "~n Cols: ~p", [Cols]), + Result = (catch ?LIB:dbg_apply(M, F, [get_next, RowIndex, Cols | A])), + ?vt("get_next_values_all_rows -> " + "~n Result: ~p", [Result]), + case validate_tab_next_res(Result, TableOids, {M, F, A}, TabOid) of + Values when is_list(Values) -> + ?vt("get_next_values_all_rows -> " + "~n Values: ~p", [Values]), + NewRes = lists:append(Values, Res), + get_next_values_all_rows(Rows, M, F, A, NewRes, TabOid); + {ErrorStatus, OrgIndex} -> + {ErrorStatus, OrgIndex} + end; +get_next_values_all_rows([], _M, _F, _A, Res, _TabOid) -> + Res. + +transform_tab_next_result([Vb | Vbs], {Res, EndOfs}, MibView) -> + case Vb#varbind.value of + {endOfTable, _} -> + {ResVBs, EndOfVBs} = ?LIB:split_vbs(Vbs, Res, [Vb | EndOfs]), + {ok, ResVBs, EndOfVBs}; + _ -> + case snmpa_acm:validate_mib_view(Vb#varbind.oid, MibView) of + true -> + transform_tab_next_result(Vbs, {[Vb|Res], EndOfs},MibView); + _ -> + Oid = Vb#varbind.oid, + NewEndOf = Vb#varbind{value = {endOfTable, Oid}}, + transform_tab_next_result(Vbs, {Res, [NewEndOf | EndOfs]}, + MibView) + end + end; +transform_tab_next_result([], {Res, EndOfs}, _MibView) -> + ?vt("transform_tab_next_result -> entry with: " + "~n Res: ~p" + "~n EndIfs: ~p",[Res, EndOfs]), + {ok, Res, EndOfs}. + + + +%%----------------------------------------------------------------- +%% Three cases: +%% 1) All values ok +%% 2) table_func returned {Error, ...} +%% 3) Some value in Values list is erroneous. +%% Args: Value is a list of values from table_func(get_next, ...) +%% TableOids is a list of {TabRestOid, OrgVb} +%% each element in Values and TableOids correspond to each +%% other. +%% Returns: List of NewVarbinds | +%% {ErrorStatus, OrgIndex} +%% (In the NewVarbinds list, the value may be endOfTable) +%%----------------------------------------------------------------- +validate_tab_next_res(Values, TableOids, Mfa, TabOid) -> + ?vt("validate_tab_next_res -> entry with: " + "~n Values: ~p" + "~n TableOids: ~p" + "~n Mfa: ~p" + "~n TabOid: ~p", [Values, TableOids, Mfa, TabOid]), + {_Col, _ASN1Type, OneIdx} = hd(TableOids), + validate_tab_next_res(Values, TableOids, Mfa, [], TabOid, + next_oid(TabOid), OneIdx). +validate_tab_next_res([{NextOid, Value} | Values], + [{_ColNo, OrgVb, _Index} | TableOids], + Mfa, Res, TabOid, TabNextOid, I) -> + ?vt("validate_tab_next_res -> entry with: " + "~n NextOid: ~p" + "~n Value: ~p" + "~n Values: ~p" + "~n TableOids: ~p" + "~n Mfa: ~p" + "~n TabOid: ~p", + [NextOid, Value, Values, TableOids, Mfa, TabOid]), + #varbind{org_index = OrgIndex} = OrgVb, + ?vt("validate_tab_next_res -> OrgIndex: ~p", [OrgIndex]), + NextCompleteOid = lists:append(TabOid, NextOid), + case snmpa_mib:lookup(get(mibserver), NextCompleteOid) of + {table_column, #me{asn1_type = ASN1Type}, _TableEntryOid} -> + ?vt("validate_tab_next_res -> ASN1Type: ~p", [ASN1Type]), + case ?AGENT:make_value_a_correct_value({value, Value}, ASN1Type, Mfa) of + {error, ErrorStatus} -> + ?vt("validate_tab_next_res -> " + "~n ErrorStatus: ~p", [ErrorStatus]), + {ErrorStatus, OrgIndex}; + {value, Type, NValue} -> + ?vt("validate_tab_next_res -> " + "~n Type: ~p" + "~n NValue: ~p", [Type, NValue]), + NewVb = OrgVb#varbind{oid = NextCompleteOid, + variabletype = Type, value = NValue}, + validate_tab_next_res(Values, TableOids, Mfa, + [NewVb | Res], TabOid, TabNextOid, I) + end; + Error -> + ?LIB:user_err("Invalid oid ~w from ~w (get_next). Using genErr => ~p", + [NextOid, Mfa, Error]), + {genErr, OrgIndex} + end; +validate_tab_next_res([endOfTable | Values], + [{_ColNo, OrgVb, _Index} | TableOids], + Mfa, Res, TabOid, TabNextOid, I) -> + ?vt("validate_tab_next_res(endOfTable) -> entry with: " + "~n Values: ~p" + "~n OrgVb: ~p" + "~n TableOids: ~p" + "~n Mfa: ~p" + "~n Res: ~p" + "~n TabOid: ~p" + "~n TabNextOid: ~p" + "~n I: ~p", + [Values, OrgVb, TableOids, Mfa, Res, TabOid, TabNextOid, I]), + NewVb = OrgVb#varbind{value = {endOfTable, TabNextOid}}, + validate_tab_next_res(Values, TableOids, Mfa, [NewVb | Res], + TabOid, TabNextOid, I); +validate_tab_next_res([], [], _Mfa, Res, _TabOid, _TabNextOid, _I) -> + Res; +validate_tab_next_res([], [{_Col, _OrgVb, Index}|_], Mfa, _Res, _, _, _I) -> + ?LIB:user_err("Too few values returned from ~w (get_next)", [Mfa]), + {genErr, Index}; +validate_tab_next_res({genErr, ColNumber}, OrgCols, + Mfa, _Res, _TabOid, _TabNextOid, _I) -> + OrgIndex = snmpa_svbl:col_to_orgindex(ColNumber, OrgCols), + ?AGENT:validate_err(table_next, {genErr, OrgIndex}, Mfa); +validate_tab_next_res({error, Reason}, [{_ColNo, OrgVb, _Index} | _TableOids], + Mfa, _Res, _TabOid, _TabNextOid, _I) -> + #varbind{org_index = OrgIndex} = OrgVb, + ?LIB:user_err("Erroneous return value ~w from ~w (get_next)", + [Reason, Mfa]), + {genErr, OrgIndex}; +validate_tab_next_res(Error, [{_ColNo, OrgVb, _Index} | _TableOids], + Mfa, _Res, _TabOid, _TabNextOid, _I) -> + #varbind{org_index = OrgIndex} = OrgVb, + ?LIB:user_err("Invalid return value ~w from ~w (get_next)", + [Error, Mfa]), + {genErr, OrgIndex}; +validate_tab_next_res(TooMany, [], Mfa, _Res, _, _, I) -> + ?LIB:user_err("Too many values ~w returned from ~w (get_next)", + [TooMany, Mfa]), + {genErr, I}. + +%%----------------------------------------------------------------- +%% Func: get_next_sa/4 +%% Purpose: Loop the list of varbinds for the subagent. +%% Call subagent_get_next to retreive +%% the next varbinds. +%% Returns: {ok, ListOfNewVbs, ListOfEndOfMibViewsVbs} | +%% {ErrorStatus, ErrorIndex} +%%----------------------------------------------------------------- +get_next_sa(SAPid, SAOid, SAVbs, MibView) -> + case catch ?AGENT:subagent_get_next(SAPid, MibView, SAVbs) of + {noError, 0, NewVbs} -> + NewerVbs = transform_sa_next_result(NewVbs,SAOid,next_oid(SAOid)), + {ResVBs, EndOfVBs} = ?LIB:split_vbs(NewerVbs), + {ok, ResVBs, EndOfVBs}; + {ErrorStatus, ErrorIndex, _} -> + {ErrorStatus, ErrorIndex}; + {'EXIT', Reason} -> + ?LIB:user_err("Lost contact with subagent (next) ~w. Using genErr", + [Reason]), + {genErr, 0} + end. + +%%----------------------------------------------------------------- +%% Check for wrong prefix returned or endOfMibView, and convert +%% into {endOfMibView, SANextOid}. +%%----------------------------------------------------------------- +transform_sa_next_result([Vb | Vbs], SAOid, SANextOid) + when Vb#varbind.value =:= endOfMibView -> + [Vb#varbind{value = {endOfMibView, SANextOid}} | + transform_sa_next_result(Vbs, SAOid, SANextOid)]; +transform_sa_next_result([Vb | Vbs], SAOid, SANextOid) -> + case lists:prefix(SAOid, Vb#varbind.oid) of + true -> + [Vb | transform_sa_next_result(Vbs, SAOid, SANextOid)]; + _ -> + [Vb#varbind{oid = SANextOid, value = {endOfMibView, SANextOid}} | + transform_sa_next_result(Vbs, SAOid, SANextOid)] + end; +transform_sa_next_result([], _SAOid, _SANextOid) -> + []. + + +next_oid(Oid) -> + case lists:reverse(Oid) of + [H | T] -> lists:reverse([H+1 | T]); + [] -> [] + end. + + + +%%%----------------------------------------------------------------- +%%% 5. GET-BULK REQUEST +%%% +%%% In order to prevent excesses in reply sizes there are two +%%% preventive methods in place. One is to check that the encode +%%% size does not exceed Max PDU size (this is mentioned in the +%%% standard). The other is a simple VBs limit. That is, the +%%% resulting response cannot contain more then this number of VBs. +%%%----------------------------------------------------------------- + +do_get_bulk(MibView, NonRepeaters, MaxRepetitions, + PduMS, Varbinds, GbMaxVBs, _Extra) -> + ?vtrace("do_get_bulk -> entry with" + "~n MibView: ~p" + "~n NonRepeaters: ~p" + "~n MaxRepetitions: ~p" + "~n PduMS: ~p" + "~n Varbinds: ~p" + "~n GbMaxVBs: ~p", + [MibView, NonRepeaters, MaxRepetitions, PduMS, Varbinds, GbMaxVBs]), + {NonRepVbs, RestVbs} = ?LIB:split_vbs_gb(NonRepeaters, Varbinds), + ?vt("do_get_bulk -> split: " + "~n NonRepVbs: ~p" + "~n RestVbs: ~p", [NonRepVbs, RestVbs]), + case do_get_next2(MibView, NonRepVbs, GbMaxVBs) of + {noError, 0, UResNonRepVbs} -> + ?vt("do_get_bulk -> next noError: " + "~n UResNonRepVbs: ~p", [UResNonRepVbs]), + ResNonRepVbs = lists:keysort(#varbind.org_index, UResNonRepVbs), + %% Decode the first varbinds, produce a reversed list of + %% listOfBytes. + case (catch enc_vbs(PduMS - ?empty_pdu_size, ResNonRepVbs)) of + {error, Idx, Reason} -> + ?LIB:user_err("failed encoding varbind ~w:~n~p", [Idx, Reason]), + {genErr, Idx, []}; + {SizeLeft, Res} when is_integer(SizeLeft) and is_list(Res) -> + ?vtrace("do_get_bulk -> encoded: " + "~n SizeLeft: ~p" + "~n Res: ~w", [SizeLeft, Res]), + case (catch do_get_rep(SizeLeft, MibView, MaxRepetitions, + RestVbs, Res, + length(UResNonRepVbs), GbMaxVBs)) of + {error, Idx, Reason} -> + ?LIB:user_err("failed encoding varbind ~w:~n~p", + [Idx, Reason]), + {genErr, Idx, []}; + Res when is_list(Res) -> + ?vtrace("do get bulk -> Res: " + "~n ~w", [Res]), + {noError, 0, conv_res(Res)}; + {noError, 0, Data} = OK -> + ?vtrace("do get bulk -> OK: " + "~n length(Data): ~w", [length(Data)]), + OK; + Else -> + ?vtrace("do get bulk -> Else: " + "~n ~w", [Else]), + Else + end; + Res when is_list(Res) -> + {noError, 0, conv_res(Res)} + end; + + {ErrorStatus, Index, _} -> + ?vdebug("do get bulk: " + "~n ErrorStatus: ~p" + "~n Index: ~p",[ErrorStatus, Index]), + {ErrorStatus, Index, []} + end. + +enc_vbs(SizeLeft, Vbs) -> + ?vt("enc_vbs -> entry with" + "~n SizeLeft: ~w", [SizeLeft]), + Fun = fun(Vb, {Sz, Res}) when Sz > 0 -> + ?vt("enc_vbs -> (fun) entry with" + "~n Vb: ~p" + "~n Sz: ~p" + "~n Res: ~w", [Vb, Sz, Res]), + case (catch snmp_pdus:enc_varbind(Vb)) of + {'EXIT', Reason} -> + ?vtrace("enc_vbs -> encode failed: " + "~n Reason: ~p", [Reason]), + throw({error, Vb#varbind.org_index, Reason}); + X -> + ?vt("enc_vbs -> X: ~w", [X]), + Lx = length(X), + ?vt("enc_vbs -> Lx: ~w", [Lx]), + if + Lx < Sz -> + {Sz - length(X), [X | Res]}; + true -> + throw(Res) + end + end; + (_Vb, {_Sz, [_H | T]}) -> + ?vt("enc_vbs -> (fun) entry with" + "~n T: ~p", [T]), + throw(T); + (_Vb, {_Sz, []}) -> + ?vt("enc_vbs -> (fun) entry", []), + throw([]) + end, + lists:foldl(Fun, {SizeLeft, []}, Vbs). + +do_get_rep(Sz, MibView, MaxRepetitions, Varbinds, Res, GbNumVBs, GbMaxVBs) + when MaxRepetitions >= 0 -> + do_get_rep(Sz, MibView, 0, MaxRepetitions, Varbinds, Res, + GbNumVBs, GbMaxVBs); +do_get_rep(Sz, MibView, _MaxRepetitions, Varbinds, Res, GbNumVBs, GbMaxVBs) -> + do_get_rep(Sz, MibView, 0, 0, Varbinds, Res, GbNumVBs, GbMaxVBs). + +conv_res(ResVarbinds) -> + conv_res(ResVarbinds, []). +conv_res([VbListOfBytes | T], Bytes) -> + conv_res(T, VbListOfBytes ++ Bytes); +conv_res([], Bytes) -> + Bytes. + +%% The only other value, then a positive integer, is infinity. +do_get_rep(_Sz, _MibView, Count, Max, _, _Res, GbNumVBs, GbMaxVBs) + when (is_integer(GbMaxVBs) andalso (GbNumVBs > GbMaxVBs)) -> + ?vinfo("Max Get-BULK VBs limit (~w) exceeded (~w) when:" + "~n Count: ~p" + "~n Max: ~p", [GbMaxVBs, GbNumVBs, Count, Max]), + {tooBig, 0, []}; +do_get_rep(_Sz, _MibView, Max, Max, _, Res, _GbNumVBs, _GbMaxVBs) -> + ?vt("do_get_rep -> done when: " + "~n Res: ~p", [Res]), + {noError, 0, conv_res(Res)}; +do_get_rep(Sz, MibView, Count, Max, Varbinds, Res, GbNumVBs, GbMaxVBs) -> + ?vt("do_get_rep -> entry when: " + "~n Sz: ~p" + "~n Count: ~p" + "~n Res: ~w", [Sz, Count, Res]), + case try_get_bulk(Sz, MibView, Varbinds, GbMaxVBs) of + {noError, NextVarbinds, SizeLeft, Res2} -> + ?vt("do_get_rep -> noError: " + "~n SizeLeft: ~p" + "~n Res2: ~p", [SizeLeft, Res2]), + do_get_rep(SizeLeft, MibView, Count+1, Max, NextVarbinds, + Res2 ++ Res, + GbNumVBs + length(Varbinds), GbMaxVBs); + {endOfMibView, _NextVarbinds, _SizeLeft, Res2} -> + ?vt("do_get_rep -> endOfMibView: " + "~n Res2: ~p", [Res2]), + {noError, 0, conv_res(Res2 ++ Res)}; + {ErrorStatus, Index} -> + ?vtrace("do_get_rep -> done when error: " + "~n ErrorStatus: ~p" + "~n Index: ~p", [ErrorStatus, Index]), + {ErrorStatus, Index, []} + end. + +try_get_bulk(Sz, MibView, Varbinds, GbMaxVBs) -> + ?vt("try_get_bulk -> entry with" + "~n Sz: ~w" + "~n MibView: ~w" + "~n Varbinds: ~w", [Sz, MibView, Varbinds]), + case do_get_next2(MibView, Varbinds, GbMaxVBs) of + {noError, 0, UNextVarbinds} -> + ?vt("try_get_bulk -> noError: " + "~n UNextVarbinds: ~p", [UNextVarbinds]), + NextVarbinds = ?LIB:org_index_sort_vbs(UNextVarbinds), + case (catch enc_vbs(Sz, NextVarbinds)) of + {error, Idx, Reason} -> + ?LIB:user_err("failed encoding varbind ~w:~n~p", [Idx, Reason]), + ?vtrace("try_get_bulk -> encode error: " + "~n Idx: ~p" + "~n Reason: ~p", [Idx, Reason]), + {genErr, Idx}; + {SizeLeft, Res} when is_integer(SizeLeft) andalso + is_list(Res) -> + ?vt("try get bulk -> encode ok: " + "~n SizeLeft: ~w" + "~n Res: ~w", [SizeLeft, Res]), + {check_end_of_mibview(NextVarbinds), + NextVarbinds, SizeLeft, Res}; + Res when is_list(Res) -> + ?vt("try get bulk -> Res: " + "~n ~w", [Res]), + {endOfMibView, [], 0, Res} + end; + {ErrorStatus, Index, _} -> + ?vt("try_get_bulk -> error: " + "~n ErrorStatus: ~p" + "~n Index: ~p", [ErrorStatus, Index]), + {ErrorStatus, Index} + end. + +%% If all variables in this pass are endOfMibView, +%% there is no reason to continue. +check_end_of_mibview([#varbind{value = endOfMibView} | T]) -> + check_end_of_mibview(T); +check_end_of_mibview([]) -> endOfMibView; +check_end_of_mibview(_) -> noError. + + + diff --git a/lib/snmp/src/agent/snmpa_get_lib.erl b/lib/snmp/src/agent/snmpa_get_lib.erl new file mode 100644 index 0000000000..eaf7fe2641 --- /dev/null +++ b/lib/snmp/src/agent/snmpa_get_lib.erl @@ -0,0 +1,254 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-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% +%% + +%% +%% Note that most of these functions *assume* that they are executed +%% by the agent. If they are not they may note work as they require +%% some properties to be set in the process dictionary! +%% + +-module(snmpa_get_lib). + +-export([ + split_vbs/1, split_vbs/3, + split_vbs_view/2, + split_vbs_gb/2, + + agent_sort_vbs/1, + oid_sort_vbs/1, org_index_sort_vbs/1, + + sa_split/1, + + delete_prefixes/2, + + dbg_apply/3, + + user_err/2 + ]). + +-define(VMODULE,"GET-LIB"). +-include("snmpa_internal.hrl"). +-include("snmp_types.hrl"). +-include("snmp_debug.hrl"). +-include("snmp_verbosity.hrl"). + + + + +%%----------------------------------------------------------------- +%% split_vbs/1,3 +%% +%% Splits the list of varbinds (basically) into two lists. One +%% of 'end of'-varbinds (mib view and tables) and then the rest +%% of the varbinds. +%%----------------------------------------------------------------- + +-spec split_vbs(VBs :: [snmp:varbind()]) -> + {ResVBs :: [snmp:varbind()], + EndOfVBs :: [snmp:varbind()]}. + +split_vbs(VBs) -> + split_vbs(VBs, [], []). + +-spec split_vbs(VBs :: [snmp:varbind()], + Res :: [snmp:varbind()], + EndOfs :: [snmp:varbind()]) -> + {ResVBs :: [snmp:varbind()], + EndOfVBs :: [snmp:varbind()]}. + +split_vbs([], ResVBs, EndOfVBs) -> + {ResVBs, EndOfVBs}; +split_vbs([VB | VBs], Res, EndOfs) -> + case VB#varbind.value of + {endOfMibView, _} -> split_vbs(VBs, Res, [VB | EndOfs]); + {endOfTable, _} -> split_vbs(VBs, Res, [VB | EndOfs]); + _ -> split_vbs(VBs, [VB | Res], EndOfs) + end. + + + +%%----------------------------------------------------------------- +%% split_vbs_view/2 +%% +%% Splits a list of varbinds into two lists based on the provided +%% MibView. One list of varbinds inside the MibView and one of +%% varbinds outside the MibView. +%%----------------------------------------------------------------- + +-spec split_vbs_view(VBs :: [snmp:varbind()], + MibView :: snmp_view_based_acm_mib:mibview()) -> + {OutSideView :: [snmp:varbind()], + InSideView :: [snmp:varbind()]}. + +split_vbs_view(VBs, MibView) -> + ?vtrace("split the varbinds view", []), + split_vbs_view(VBs, MibView, [], []). + +split_vbs_view([], _MibView, Out, In) -> + {Out, In}; +split_vbs_view([VB | VBs], MibView, Out, In) -> + case snmpa_acm:validate_mib_view(VB#varbind.oid, MibView) of + true -> + split_vbs_view(VBs, MibView, Out, [VB | In]); + false -> + VB2 = VB#varbind{value = noSuchObject}, + split_vbs_view(VBs, MibView, [VB2 | Out], In) + end. + + + +%%----------------------------------------------------------------- +%% split_vbs_gb/2 +%% +%% Performs a get-bulk split of the varbinds +%%----------------------------------------------------------------- + +-spec split_vbs_gb(NonRepeaters :: integer(), + VBs :: [snmp:varbind()]) -> + {NonRepVBs :: [snmp:varbind()], + RestVBs :: [snmp:varbind()]}. + +split_vbs_gb(N, VBs) -> + split_vbs_gb(N, VBs, []). + +split_vbs_gb(N, Varbinds, Res) when N =< 0 -> + {Res, Varbinds}; +split_vbs_gb(N, [H | T], Res) -> + split_vbs_gb(N-1, T, [H | Res]); +split_vbs_gb(_N, [], Res) -> + {Res, []}. + + + +%%----------------------------------------------------------------- +%% agent_sort_vbs/1 +%% +%% Sorts the varbinds into two categories. The first is varbinds +%% belonging to "our" agent and the other is varbinds for +%% subagents. +%%----------------------------------------------------------------- + +-spec agent_sort_vbs(VBs :: [snmp:varbind()]) -> + {AgentVBs :: [snmp:varbind()], + SubAgentVBs :: [snmp:varbind()]}. + +agent_sort_vbs(VBs) -> + snmpa_svbl:sort_varbindlist(get(mibserver), VBs). + + +%%----------------------------------------------------------------- +%% oid_sort_vbs/1 +%% +%% Sorts the varbinds based on their oid. +%%----------------------------------------------------------------- + +-spec oid_sort_vbs(VBs :: [snmp:varbind()]) -> SortedVBs :: [snmp:varbind()]. + +oid_sort_vbs(VBs) -> + lists:keysort(#varbind.oid, VBs). + + +%%----------------------------------------------------------------- +%% org_index_sort_vbs/1 +%% +%% Sorts the varbinds based on their org_index. +%%----------------------------------------------------------------- + +-spec org_index_sort_vbs(VBs :: [snmp:varbind()]) -> SortedVBs :: [snmp:varbind()]. + +org_index_sort_vbs(Vbs) -> + lists:keysort(#varbind.org_index, Vbs). + + + +%%----------------------------------------------------------------- +%% sa_split/1 +%% +%% Splits a list of {oid(), varbind()} into two lists of oid() +%% and varbind. The resulting lists are reversed! +%%----------------------------------------------------------------- + +-spec sa_split(SAVBs :: [{SAOid :: snmp:oid(), snmp:varbind()}]) -> + {Oids :: [snmp:oid()], VBs :: [snmp:varbind()]}. + +sa_split(SAVBs) -> + snmpa_svbl:sa_split(SAVBs). + + + +%%----------------------------------------------------------------- +%% delete_prefixes/2 +%% +%% Takes an Oid prefix and a list of ivarbinds and produces a list +%% of {ShortOid, ASN1Type}. The ShortOid is basically the oid with +%% the OidPrefix removed. +%%----------------------------------------------------------------- + +-spec delete_prefixes(OidPrefix :: snmp:oid(), + VBs :: [snmp:ivarbind()]) -> + [{ShortOid :: snmp:oid(), + ASN1Type :: snmp:asn1_type()}]. + +delete_prefixes(OidPrefix, IVBs) -> + [{snmp_misc:diff(Oid, OidPrefix), ME#me.asn1_type} || + #ivarbind{varbind = #varbind{oid = Oid}, mibentry = ME} <- IVBs]. + + + +%%----------------------------------------------------------------- +%% dbg_apply/3 +%% +%% Call instrumentation functions, but allow for debug printing +%% of useful debug info. +%%----------------------------------------------------------------- + +-spec dbg_apply(M :: atom(), F :: atom(), A :: list()) -> + any(). + +dbg_apply(M, F, A) -> + case get(verbosity) of + silence -> + apply(M,F,A); + _ -> + ?vlog("~n apply: ~w, ~w, ~p~n", [M,F,A]), + Res = (catch apply(M,F,A)), + case Res of + {'EXIT', Reason} -> + ?vinfo("Call to: " + "~n Module: ~p" + "~n Function: ~p" + "~n Args: ~p" + "~n" + "~nresulted in an exit" + "~n" + "~n ~p~n", [M, F, A, Reason]); + _ -> + ?vlog("~n returned: ~p~n", [Res]) + end, + Res + end. + + +%% --------------------------------------------------------------------- + +user_err(F, A) -> + snmpa_error:user_err(F, A). + + diff --git a/lib/snmp/src/agent/snmpa_get_mechanism.erl b/lib/snmp/src/agent/snmpa_get_mechanism.erl new file mode 100644 index 0000000000..744a6529e1 --- /dev/null +++ b/lib/snmp/src/agent/snmpa_get_mechanism.erl @@ -0,0 +1,79 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-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(snmpa_get_mechanism). + +%% +%% This module defines the behaviour for the undocumented (hidden) +%% get-mechanism feature. This allows for implementing your own +%% handling of get, get-next and get-bulk requests. +%% Probably only useful for special cases (e.g. optimization). +%% + + + +%% ----------- do_get/2,3 ----------------------------------------------------- + +%% Purpose: Handles all VBs in a request that is inside the +%% mibview (local). + +-callback do_get(UnsortedVBs :: [snmp:varbind()], + IsNotification :: boolean(), + Extra :: term()) -> + {noError, 0, ResVBs :: [snmp:varbind()]} | + {ErrStatus :: snmp:error_status(), ErrIndex :: snmp:error_index(), []}. + + +%% Purpose: Handles "get-requests". + +-callback do_get(MibView :: snmp_view_based_acm_mib:mibview(), + UnsortedVBs :: [snmp:varbind()], + IsNotification :: boolean(), + Extra :: term()) -> + {noError, 0, ResVBs :: [snmp:varbind()]} | + {ErrStatus :: snmp:error_status(), ErrIndex :: snmp:error_index(), []}. + + + + +%% ----------- do_get_next/2 ------------------------------------------------ + +%% Purpose: Handles "get-next-requests". + +-callback do_get_next(MibView :: snmp_view_based_acm_mib:mibview(), + UnsortedVBs :: [snmp:varbind()], + Extra :: term()) -> + {noError, 0, ResVBs :: [snmp:varbind()]} | + {ErrStatus :: snmp:error_status(), ErrIndex :: snmp:error_index(), []}. + + + + +%% ----------- do_get_bulk/6 ------------------------------------------------ + +-callback do_get_bulk(MibView :: snmp_view_based_acm_mib:mibview(), + NonRepeaters :: non_neg_integer(), + MaxRepetitions :: non_neg_integer(), + PduMS :: pos_integer(), + VBs :: [snmp:varbind()], + MaxVBs :: pos_integer(), + Extra :: term()) -> + {noError, 0, ResVBs :: [snmp:varbind()]} | + {ErrStatus :: snmp:error_status(), ErrIndex :: snmp:error_index(), []}. diff --git a/lib/snmp/src/agent/snmpa_set_lib.erl b/lib/snmp/src/agent/snmpa_set_lib.erl index 97b8ddf7c4..94120f4c7d 100644 --- a/lib/snmp/src/agent/snmpa_set_lib.erl +++ b/lib/snmp/src/agent/snmpa_set_lib.erl @@ -390,7 +390,7 @@ dbg_apply(M,F,A) -> {'EXIT', {function_clause, [{M, F, A} | _]}} -> {'EXIT', {hook_function_clause, {M, F, A}}}; - % XYZ: Older format for compatibility + %% XYZ: Older format for compatibility {'EXIT', {undef, {M, F, A}}} -> {'EXIT', {hook_undef, {M, F, A}}}; {'EXIT', {function_clause, {M, F, A}}} -> diff --git a/lib/snmp/src/agent/snmpa_supervisor.erl b/lib/snmp/src/agent/snmpa_supervisor.erl index cdb5ca840d..2cb0556001 100644 --- a/lib/snmp/src/agent/snmpa_supervisor.erl +++ b/lib/snmp/src/agent/snmpa_supervisor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -193,36 +193,36 @@ init([AgentType, Opts]) -> ?vdebug("agent restart type: ~w", [Restart]), %% -- Agent type -- - ets:insert(snmp_agent_table, {agent_type, AgentType}), + store(agent_type, AgentType), %% -- Prio -- Prio = get_opt(priority, Opts, normal), ?vdebug("[agent table] store priority: ~p",[Prio]), - ets:insert(snmp_agent_table, {priority, Prio}), + store(priority, Prio), %% -- Versions -- Vsns = get_opt(versions, Opts, [v1,v2,v3]), ?vdebug("[agent table] store versions: ~p",[Vsns]), - ets:insert(snmp_agent_table, {versions, Vsns}), + store(versions, Vsns), %% -- Max number of VBs in a Get-BULK response -- GbMaxVBs = get_gb_max_vbs(Opts), ?vdebug("[agent table] Get-BULK max VBs: ~p", [GbMaxVBs]), - ets:insert(snmp_agent_table, {gb_max_vbs, GbMaxVBs}), + store(gb_max_vbs, GbMaxVBs), %% -- DB-directory -- DbDir = get_opt(db_dir, Opts), ?vdebug("[agent table] store db_dir: ~n ~p",[DbDir]), - ets:insert(snmp_agent_table, {db_dir, filename:join([DbDir])}), + store(db_dir, filename:join([DbDir])), DbInitError = get_opt(db_init_error, Opts, terminate), ?vdebug("[agent table] store db_init_error: ~n ~p",[DbInitError]), - ets:insert(snmp_agent_table, {db_init_error, DbInitError}), + store(db_init_error, DbInitError), %% -- Error report module -- ErrorReportMod = get_opt(error_report_mod, Opts, snmpa_error_logger), ?vdebug("[agent table] store error report module: ~w",[ErrorReportMod]), - ets:insert(snmp_agent_table, {error_report_mod, ErrorReportMod}), + store(error_report_mod, ErrorReportMod), %% -- mib storage -- %% MibStorage has only one mandatory part: module @@ -320,31 +320,31 @@ init([AgentType, Opts]) -> end, ?vdebug("[agent table] store mib storage: ~w", [MibStorage]), - ets:insert(snmp_agent_table, {mib_storage, MibStorage}), + store(mib_storage, MibStorage), %% -- Agent mib storage -- AgentMibStorage = get_opt(agent_mib_storage, Opts, persistent), %% ?vdebug("[agent table] store agent mib storage: ~w",[AgentMibStorage]), - ets:insert(snmp_agent_table, {agent_mib_storage, AgentMibStorage}), + store(agent_mib_storage, AgentMibStorage), %% -- System start time -- ?vdebug("[agent table] store system start time",[]), - ets:insert(snmp_agent_table, {system_start_time, snmp_misc:now(cs)}), + store(system_start_time, snmp_misc:now(cs)), %% -- Symbolic store options -- SsOpts = get_opt(symbolic_store, Opts, []), ?vdebug("[agent table] store symbolic store options: ~w",[SsOpts]), - ets:insert(snmp_agent_table, {symbolic_store, SsOpts}), + store(symbolic_store, SsOpts), %% -- Local DB options -- LdbOpts = get_opt(local_db, Opts, []), ?vdebug("[agent table] store local db options: ~w",[LdbOpts]), - ets:insert(snmp_agent_table, {local_db, LdbOpts}), + store(local_db, LdbOpts), %% -- Target cache options -- TargetCacheOpts = get_opt(target_cache, Opts, []), ?vdebug("[agent table] store target cache options: ~w",[TargetCacheOpts]), - ets:insert(snmp_agent_table, {target_cache, TargetCacheOpts}), + store(target_cache, TargetCacheOpts), %% -- Specs -- SupFlags = {one_for_all, 0, 3600}, @@ -377,7 +377,7 @@ init([AgentType, Opts]) -> %% -- Config -- ConfOpts = get_opt(config, Opts, []), ?vdebug("[agent table] store config options: ~p", [ConfOpts]), - ets:insert(snmp_agent_table, {config, ConfOpts}), + store(config, ConfOpts), ConfigArgs = [Vsns, ConfOpts], ConfigSpec = @@ -390,43 +390,46 @@ init([AgentType, Opts]) -> %% -- Discovery processing -- DiscoOpts = get_opt(discovery, Opts, []), ?vdebug("[agent table] store discovery options: ~p", [DiscoOpts]), - ets:insert(snmp_agent_table, {discovery, DiscoOpts}), + store(discovery, DiscoOpts), %% -- Mibs -- Mibs = get_mibs(get_opt(mibs, Opts, []), Vsns), ?vdebug("[agent table] store mibs: ~n ~p",[Mibs]), - ets:insert(snmp_agent_table, {mibs, Mibs}), + store(mibs, Mibs), Ref = make_ref(), + %% -- Get module -- + GetModule = get_opt(get_mechanism, Opts, snmpa_get), + ?vdebug("[agent table] store get-module: ~p", [GetModule]), + store(get_mechanism, GetModule), + %% -- Set module -- SetModule = get_opt(set_mechanism, Opts, snmpa_set), ?vdebug("[agent table] store set-module: ~p",[SetModule]), - ets:insert(snmp_agent_table, {set_mechanism, ConfOpts}), + store(set_mechanism, SetModule), %% -- Authentication service -- AuthModule = get_opt(authentication_service, Opts, snmpa_acm), ?vdebug("[agent table] store authentication service: ~w", [AuthModule]), - ets:insert(snmp_agent_table, - {authentication_service, AuthModule}), + store(authentication_service, AuthModule), %% -- Multi-threaded -- MultiT = get_opt(multi_threaded, Opts, false), - ?vdebug("[agent table] store multi-threaded: ~p",[MultiT]), - ets:insert(snmp_agent_table, {multi_threaded, MultiT}), + ?vdebug("[agent table] store multi-threaded: ~p", [MultiT]), + store(multi_threaded, MultiT), %% -- Audit trail log -- case get_opt(audit_trail_log, Opts, not_found) of not_found -> - ?vdebug("[agent table] no audit trail log",[]), + ?vdebug("[agent table] no audit trail log", []), ok; AtlOpts -> ?vdebug("[agent table] " "store audit trail log options: ~p", [AtlOpts]), - ets:insert(snmp_agent_table, - {audit_trail_log, AtlOpts}), + store(audit_trail_log, AtlOpts), ok end, @@ -434,24 +437,25 @@ init([AgentType, Opts]) -> MibsOpts = get_opt(mib_server, Opts, []), ?vdebug("[agent table] store mib-server options: " "~n ~p", [MibsOpts]), - ets:insert(snmp_agent_table, {mib_server, MibsOpts}), + store(mib_server, MibsOpts), %% -- Network interface -- NiOpts = get_opt(net_if, Opts, []), ?vdebug("[agent table] store net-if options: " "~n ~p", [NiOpts]), - ets:insert(snmp_agent_table, {net_if, NiOpts}), + store(net_if, NiOpts), %% -- Note store -- NsOpts = get_opt(note_store, Opts, []), ?vdebug("[agent table] store note-store options: " "~n ~p",[NsOpts]), - ets:insert(snmp_agent_table, {note_store, NsOpts}), + store(note_store, NsOpts), AgentOpts = [{verbosity, AgentVerb}, {mibs, Mibs}, {mib_storage, MibStorage}, + {get_mechanism, GetModule}, {set_mechanism, SetModule}, {authentication_service, AuthModule}, {multi_threaded, MultiT}, @@ -480,6 +484,10 @@ init([AgentType, Opts]) -> {ok, {SupFlags, [MiscSupSpec, SymStoreSpec, LocalDbSpec, TargetCacheSpec | Rest]}}. + +store(Key, Value) -> + ets:insert(snmp_agent_table, {Key, Value}). + get_mibs(Mibs, Vsns) -> MibDir = filename:join(code:priv_dir(snmp), "mibs"), StdMib = diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index 293d1f3ccf..d04b6a206e 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -917,7 +917,7 @@ do_send_v2_trap(Recvs, Vbs, ExtraInfo, NetIf) -> TrapPdu = make_v2_notif_pdu(Vbs, 'snmpv2-trap'), AddrCommunities = mk_addr_communities(Recvs), lists:foreach(fun({Community, Addrs}) -> - ?vtrace("~n send v2 trap to ~p",[Addrs]), + ?vtrace("send v2 trap to ~p",[Addrs]), NetIf ! {send_pdu, 'version-2', TrapPdu, {community, Community}, Addrs, ExtraInfo} end, AddrCommunities), diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src index d4bf0de61a..178309b488 100644 --- a/lib/snmp/src/app/snmp.app.src +++ b/lib/snmp/src/app/snmp.app.src @@ -49,6 +49,9 @@ snmpa_error_io, snmpa_error_logger, snmpa_error_report, + snmpa_get, + snmpa_get_lib, + snmpa_get_mechanism, snmpa_local_db, snmpa_mib, snmpa_mib_data, diff --git a/lib/snmp/src/app/snmp.config b/lib/snmp/src/app/snmp.config index b66ef5d7df..f35a636157 100644 --- a/lib/snmp/src/app/snmp.config +++ b/lib/snmp/src/app/snmp.config @@ -8,6 +8,7 @@ %% {agent_verbosity, verbosity()} | %% {versions, versions()} | %% {priority, atom()} | +%% {get_mechanism, module()} | %% {set_mechanism, module()} | %% {authentication_service, module()} | %% {multi_threaded, bool()} | diff --git a/lib/snmp/src/app/snmp.erl b/lib/snmp/src/app/snmp.erl index 8a736f688b..216452afdd 100644 --- a/lib/snmp/src/app/snmp.erl +++ b/lib/snmp/src/app/snmp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -116,7 +116,10 @@ pdu/0, trappdu/0, mib/0, - mib_name/0, + mib_name/0, + + error_status/0, + error_index/0, void/0 ]). @@ -208,6 +211,23 @@ -type pdu() :: #pdu{}. -type trappdu() :: #trappdu{}. +%% We should really specify all of these, but they are so numerous... +%% See the validate_err/1 function in the snmpa_agent. +%% Here are a number of them: +%% badValue | +%% commitFailed | +%% genErr | +%% inconsistentName | inconsistentValue | +%% noAccess | noCreation | +%% noSuchInstance | noSuchName | noSuchObject | +%% notWritable | +%% resourceUnavailable | +%% undoFailed | +%% wrongValue + +-type error_status() :: atom(). +-type error_index() :: pos_integer(). + -type void() :: term(). diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk index 0f54e67c65..8b6547f9a9 100644 --- a/lib/snmp/test/modules.mk +++ b/lib/snmp/test/modules.mk @@ -31,6 +31,7 @@ SUITE_MODULES = \ snmp_agent_mibs_test \ snmp_agent_nfilter_test \ snmp_agent_test \ + snmp_agent_test_get \ snmp_agent_conf_test \ snmp_agent_test_lib \ snmp_manager_config_test \ diff --git a/lib/snmp/test/snmp_agent_test_get.erl b/lib/snmp/test/snmp_agent_test_get.erl new file mode 100644 index 0000000000..517c71507a --- /dev/null +++ b/lib/snmp/test/snmp_agent_test_get.erl @@ -0,0 +1,58 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-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(snmp_agent_test_get). + +-behaviour(snmpa_get_mechanism). + + +%%%----------------------------------------------------------------- +%%% snmpa_get_mechanism exports +%%%----------------------------------------------------------------- + +-export([ + do_get/3, do_get/4, + do_get_next/3, + do_get_bulk/7 + ]). + + + +do_get(UnsortedVarbinds, IsNotification, Extra) -> + snmpa_get:do_get(UnsortedVarbinds, IsNotification, Extra). + + + +do_get(MibView, UnsortedVarbinds, IsNotification, Extra) -> + snmpa_get:do_get(MibView, UnsortedVarbinds, IsNotification, Extra). + + + +do_get_next(MibView, UnsortedVBs, Extra) -> + snmpa_get:do_get_next(MibView, UnsortedVBs, Extra). + + + + +do_get_bulk(MibView, NonRepeaters, MaxRepetitions, + PduMS, Varbinds, GbMaxVBs, Extra) -> + snmpa_get:do_get_bulk(MibView, NonRepeaters, MaxRepetitions, + PduMS, Varbinds, GbMaxVBs, + Extra). diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl index 66211d7105..c19c88528f 100644 --- a/lib/snmp/test/snmp_agent_test_lib.erl +++ b/lib/snmp/test/snmp_agent_test_lib.erl @@ -445,6 +445,7 @@ start_agent(Config, Vsns, Opts) -> [{versions, Vsns}, {agent_type, master}, {agent_verbosity, trace}, + {get_mechanism, snmp_agent_test_get}, {db_dir, AgentDbDir}, {audit_trail_log, [{type, read_write}, {dir, AgentLogDir}, diff --git a/lib/snmp/test/snmp_compiler_test.erl b/lib/snmp/test/snmp_compiler_test.erl index 2e48d5134d..a28f925a22 100644 --- a/lib/snmp/test/snmp_compiler_test.erl +++ b/lib/snmp/test/snmp_compiler_test.erl @@ -226,10 +226,8 @@ agent_capabilities(Config) when is_list(Config) -> put(tname,agent_capabilities), p("starting with Config: ~p~n", [Config]), - SnmpPrivDir = code:priv_dir(snmp), + SnmpPrivDir = which_priv_dir(snmp), SnmpMibsDir = join(SnmpPrivDir, "mibs"), - OtpMibsPrivDir = code:priv_dir(otp_mibs), - OtpMibsMibsDir = join(OtpMibsPrivDir, "mibs"), Dir = ?config(mib_dir, Config), AcMib = join(Dir,"AC-TEST-MIB.mib"), ?line {ok, MibFile1} = snmpc:compile(AcMib, [options, @@ -269,22 +267,20 @@ module_compliance(Config) when is_list(Config) -> put(tname,module_compliance), p("starting with Config: ~p~n", [Config]), - SnmpPrivDir = code:priv_dir(snmp), - SnmpMibsDir = join(SnmpPrivDir, "mibs"), - OtpMibsPrivDir = code:priv_dir(otp_mibs), - OtpMibsMibsDir = join(OtpMibsPrivDir, "mibs"), - Dir = ?config(mib_dir, Config), - AcMib = join(Dir,"MC-TEST-MIB.mib"), + SnmpPrivDir = which_priv_dir(snmp), + SnmpMibsDir = join(SnmpPrivDir, "mibs"), + Dir = ?config(mib_dir, Config), + AcMib = join(Dir,"MC-TEST-MIB.mib"), ?line {ok, MibFile1} = snmpc:compile(AcMib, [options, version, - {i, [SnmpMibsDir, OtpMibsMibsDir]}, + {i, [SnmpMibsDir]}, {outdir, Dir}, {verbosity, trace}]), ?line {ok, Mib1} = snmp_misc:read_mib(MibFile1), ?line {ok, MibFile2} = snmpc:compile(AcMib, [options, version, module_compliance, - {i, [SnmpMibsDir, OtpMibsMibsDir]}, + {i, [SnmpMibsDir]}, {outdir, Dir}, {verbosity, trace}]), ?line {ok, Mib2} = snmp_misc:read_mib(MibFile2), @@ -731,6 +727,15 @@ check_desc(Desc1, Desc2) -> exit({'description not equal', Desc1, Desc2}). +which_priv_dir(App) -> + case code:priv_dir(App) of + Dir when is_list(Dir) -> + Dir; + {error, Reason} -> + exit({App, priv_dir_not_found, Reason}) + end. + + %% join(Comp) -> %% filename:join(Comp). diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 6ced55f0cc..bb9b05b89f 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -6179,7 +6179,12 @@ start_agent(Node, Vsns, Conf0, _Opts) -> {mib_server, [{verbosity, MSV}]}, {note_store, [{verbosity, NSV}]}, {stymbolic_store, [{verbosity, SSV}]}, - {net_if, [{verbosity, NIV}]}, + {net_if, [{verbosity, NIV}, + %% On some linux "they" add a 127.0.1.1 or somthing + %% similar, so if we don't specify bind_to + %% we don't know which address will be selected + %% (which will cause problems for some test cases). + {options, [{bind_to, true}]}]}, {multi_threaded, true}], ?line ok = set_agent_env(Node, Env), diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 7993be8a74..e070006900 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -67,7 +67,7 @@ %% Setup %%==================================================================== start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts, - User, {CbModule, _,_, _} = CbInfo, + User, {CbModule, _, _, _, _} = CbInfo, Timeout) -> try {ok, Pid} = dtls_connection_sup:start_child([Role, Host, Port, Socket, @@ -147,13 +147,16 @@ next_record(#state{static_env = #static_env{role = server, socket = {Listener, {Client, _}}}} = State) -> dtls_packet_demux:active_once(Listener, Client, self()), {no_record, State}; -next_record(#state{static_env = #static_env{role = client, +next_record(#state{protocol_specific = #{active_n_toggle := true, + active_n := N} = ProtocolSpec, + static_env = #static_env{role = client, socket = {_Server, Socket} = DTLSSocket, close_tag = CloseTag, transport_cb = Transport}} = State) -> - case dtls_socket:setopts(Transport, Socket, [{active,once}]) of + case dtls_socket:setopts(Transport, Socket, [{active,N}]) of ok -> - {no_record, State}; + {no_record, State#state{protocol_specific = + ProtocolSpec#{active_n_toggle => false}}}; _ -> self() ! {CloseTag, DTLSSocket}, {no_record, State} @@ -291,9 +294,10 @@ handle_protocol_record(#ssl_tls{type = _Unknown}, StateName, State) -> %% Handshake handling %%==================================================================== -renegotiate(#state{static_env = #static_env{role = client}} = State, Actions) -> +renegotiate(#state{static_env = #static_env{role = client}} = State0, Actions) -> %% Handle same way as if server requested %% the renegotiation + State = reinit_handshake_data(State0), {next_state, connection, State, [{next_event, internal, #hello_request{}} | Actions]}; @@ -451,8 +455,7 @@ init({call, From}, {start, Timeout}, session = Session0#session{session_id = Hello#client_hello.session_id}, start_or_recv_from = From}, - {Record, State} = next_record(State3), - next_event(hello, Record, State, [{{timeout, handshake}, Timeout, close} | Actions]); + next_event(hello, no_record, State3, [{{timeout, handshake}, Timeout, close} | Actions]); init({call, _} = Type, Event, #state{static_env = #static_env{role = server}, protocol_specific = PS} = State) -> Result = gen_handshake(?FUNCTION_NAME, Type, Event, @@ -510,9 +513,8 @@ hello(internal, #client_hello{cookie = <<>>, %% negotiated. VerifyRequest = dtls_handshake:hello_verify_request(Cookie, ?HELLO_VERIFY_REQUEST_VERSION), State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}), - {State2, Actions} = send_handshake(VerifyRequest, State1), - {Record, State} = next_record(State2), - next_event(?FUNCTION_NAME, Record, + {State, Actions} = send_handshake(VerifyRequest, State1), + next_event(?FUNCTION_NAME, no_record, State#state{handshake_env = HsEnv#handshake_env{ tls_handshake_history = ssl_handshake:init_handshake_history()}}, @@ -714,12 +716,10 @@ connection(internal, #hello_request{}, #state{static_env = #static_env{host = Ho HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions), State1 = prepare_flight(State0), {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}), - {Record, State} = - next_record( - State2#state{protocol_specific = PS#{flight_state => initial_flight_state(DataTag)}, - session = Session0#session{session_id - = Hello#client_hello.session_id}}), - next_event(hello, Record, State, Actions); + State = State2#state{protocol_specific = PS#{flight_state => initial_flight_state(DataTag)}, + session = Session0#session{session_id + = Hello#client_hello.session_id}}, + next_event(hello, no_record, State, Actions); connection(internal, #client_hello{} = Hello, #state{static_env = #static_env{role = server}, handshake_env = #handshake_env{allow_renegotiate = true} = HsEnv} = State) -> %% Mitigate Computational DoS attack @@ -775,7 +775,7 @@ format_status(Type, Data) -> %%% Internal functions %%-------------------------------------------------------------------- initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, - {CbModule, DataTag, CloseTag, ErrorTag}) -> + {CbModule, DataTag, CloseTag, ErrorTag, PassiveTag}) -> #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions, ConnectionStates = dtls_record:init_connection_states(Role, BeastMitigation), @@ -785,7 +785,12 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, _ -> ssl_session_cache end, - + InternalActiveN = case application:get_env(ssl, internal_active_n) of + {ok, N} when is_integer(N) -> + N; + _ -> + ?INTERNAL_ACTIVE_N + end, Monitor = erlang:monitor(process, User), InitStatEnv = #static_env{ role = Role, @@ -794,6 +799,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, data_tag = DataTag, close_tag = CloseTag, error_tag = ErrorTag, + passive_tag = PassiveTag, host = Host, port = Port, socket = Socket, @@ -817,7 +823,9 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, user_data_buffer = {[],0,[]}, start_or_recv_from = undefined, flight_buffer = new_flight(), - protocol_specific = #{flight_state => initial_flight_state(DataTag)} + protocol_specific = #{active_n => InternalActiveN, + active_n_toggle => true, + flight_state => initial_flight_state(DataTag)} }. initial_flight_state(udp)-> @@ -914,12 +922,21 @@ handle_info({Protocol, _, _, _, Data}, StateName, ssl_connection:handle_normal_shutdown(Alert, StateName, State0), {stop, {shutdown, own_alert}, State0} end; + +handle_info({PassiveTag, Socket}, StateName, + #state{static_env = #static_env{socket = {_, Socket}, + passive_tag = PassiveTag}, + protocol_specific = PS} = State) -> + next_event(StateName, no_record, + State#state{protocol_specific = PS#{active_n_toggle => true}}); + handle_info({CloseTag, Socket}, StateName, #state{static_env = #static_env{socket = Socket, close_tag = CloseTag}, connection_env = #connection_env{negotiated_version = Version}, socket_options = #socket_options{active = Active}, - protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs}} = State) -> + protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs}, + protocol_specific = PS} = State) -> %% Note that as of DTLS 1.2 (TLS 1.1), %% failure to properly close a connection no longer requires that a %% session not be resumed. This is a change from DTLS 1.0 to conform @@ -942,7 +959,8 @@ handle_info({CloseTag, Socket}, StateName, %% Fixes non-delivery of final DTLS record in {active, once}. %% Basically allows the application the opportunity to set {active, once} again %% and then receive the final message. - next_event(StateName, no_record, State) + next_event(StateName, no_record, State#state{ + protocol_specific = PS#{active_n_toggle => true}}) end; handle_info(new_cookie_secret, StateName, diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index 46e8348ce0..0a0c6f0c2e 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -427,74 +427,135 @@ merge_fragment(Frag0, [Frag1 | Rest]) -> Frag -> merge_fragment(Frag, Rest) end. -%% Duplicate + + +%% Duplicate (fully contained fragment) +%% 2,5 _ _ P P P P P +%% 2,5 _ _ C C C C C merge_fragments(#handshake_fragment{ - fragment_offset = PreviousOffSet, + fragment_offset = PreviousOffSet, fragment_length = PreviousLen, fragment = PreviousData - } = Previous, + } = Previous, #handshake_fragment{ fragment_offset = PreviousOffSet, fragment_length = PreviousLen, fragment = PreviousData}) -> Previous; -%% Lager fragment save new data +%% Duplicate (fully contained fragment) +%% 2,5 _ _ P P P P P +%% 2,2 _ _ C C +%% 0,3 X X X +%% 5,3 _ _ _ _ _ X X X merge_fragments(#handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = PreviousLen, + fragment_offset = PreviousOffset, + fragment_length = PreviousLen + } = Previous, + #handshake_fragment{ + fragment_offset = CurrentOffset, + fragment_length = CurrentLen}) + when PreviousOffset =< CurrentOffset andalso + CurrentOffset =< PreviousOffset + PreviousLen andalso + CurrentOffset + CurrentLen =< PreviousOffset + PreviousLen -> + Previous; + +%% Fully overlapping fragments +%% 2,5 _ _ P P P P P +%% 0,8 C C C C C C C C +merge_fragments(#handshake_fragment{ + fragment_offset = PreviousOffset, + fragment_length = PreviousLen + }, + #handshake_fragment{ + fragment_offset = CurrentOffset, + fragment_length = CurrentLen} = Current) + when CurrentOffset =< PreviousOffset andalso + CurrentOffset + CurrentLen >= PreviousOffset + PreviousLen -> + Current; + +%% Overlapping fragments +%% 2,5 _ _ P P P P P +%% 0,3 C C C +merge_fragments(#handshake_fragment{ + fragment_offset = PreviousOffset, + fragment_length = PreviousLen, fragment = PreviousData - } = Previous, - #handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = CurrentLen, - fragment = CurrentData}) when CurrentLen > PreviousLen -> - NewLength = CurrentLen - PreviousLen, - <<_:PreviousLen/binary, NewData/binary>> = CurrentData, + } = Previous, + #handshake_fragment{ + fragment_offset = CurrentOffset, + fragment_length = CurrentLen, + fragment = CurrentData}) + when CurrentOffset < PreviousOffset andalso + CurrentOffset + CurrentLen < PreviousOffset + PreviousLen -> + NewDataLen = PreviousOffset - CurrentOffset, + <<NewData:NewDataLen/binary, _/binary>> = CurrentData, Previous#handshake_fragment{ - fragment_length = PreviousLen + NewLength, - fragment = <<PreviousData/binary, NewData/binary>> + fragment_length = PreviousLen + NewDataLen, + fragment = <<NewData/binary, PreviousData/binary>> }; -%% Smaller fragment +%% Overlapping fragments +%% 2,5 _ _ P P P P P +%% 5,3 _ _ _ _ _ C C C merge_fragments(#handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = PreviousLen - } = Previous, - #handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = CurrentLen}) when CurrentLen < PreviousLen -> - Previous; -%% Next fragment, might be overlapping + fragment_offset = PreviousOffset, + fragment_length = PreviousLen, + fragment = PreviousData + } = Previous, + #handshake_fragment{ + fragment_offset = CurrentOffset, + fragment_length = CurrentLen, + fragment = CurrentData}) + when CurrentOffset > PreviousOffset andalso + CurrentOffset < PreviousOffset + PreviousLen -> + NewDataLen = CurrentOffset + CurrentLen - (PreviousOffset + PreviousLen), + DropLen = CurrentLen - NewDataLen, + <<_:DropLen/binary, NewData/binary>> = CurrentData, + Previous#handshake_fragment{ + fragment_length = PreviousLen + NewDataLen, + fragment = <<PreviousData/binary, NewData/binary>> + }; + +%% Adjacent fragments +%% 2,5 _ _ P P P P P +%% 7,3 _ _ _ _ _ _ _ C C C merge_fragments(#handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = PreviousLen, + fragment_offset = PreviousOffset, + fragment_length = PreviousLen, fragment = PreviousData - } = Previous, - #handshake_fragment{ - fragment_offset = CurrentOffSet, - fragment_length = CurrentLen, - fragment = CurrentData}) - when PreviousOffSet + PreviousLen >= CurrentOffSet andalso - PreviousOffSet + PreviousLen < CurrentOffSet + CurrentLen -> - CurrentStart = PreviousOffSet + PreviousLen - CurrentOffSet, - <<_:CurrentStart/bytes, Data/binary>> = CurrentData, + } = Previous, + #handshake_fragment{ + fragment_offset = CurrentOffset, + fragment_length = CurrentLen, + fragment = CurrentData}) + when CurrentOffset =:= PreviousOffset + PreviousLen -> Previous#handshake_fragment{ - fragment_length = PreviousLen + CurrentLen - CurrentStart, - fragment = <<PreviousData/binary, Data/binary>>}; -%% already fully contained fragment + fragment_length = PreviousLen + CurrentLen, + fragment = <<PreviousData/binary, CurrentData/binary>> + }; + +%% Adjacent fragments +%% 2,5 _ _ P P P P P +%% 0,2 C C merge_fragments(#handshake_fragment{ - fragment_offset = PreviousOffSet, - fragment_length = PreviousLen - } = Previous, + fragment_offset = PreviousOffset, + fragment_length = PreviousLen, + fragment = PreviousData + } = Previous, #handshake_fragment{ - fragment_offset = CurrentOffSet, - fragment_length = CurrentLen}) - when PreviousOffSet + PreviousLen >= CurrentOffSet andalso - PreviousOffSet + PreviousLen >= CurrentOffSet + CurrentLen -> - Previous; + fragment_offset = CurrentOffset, + fragment_length = CurrentLen, + fragment = CurrentData}) + when PreviousOffset =:= CurrentOffset + CurrentLen -> + Previous#handshake_fragment{ + fragment_length = PreviousLen + CurrentLen, + fragment = <<CurrentData/binary, PreviousData/binary>> + }; %% No merge there is a gap +%% 3,5 _ _ _ P P P P +%% 0,2 C C merge_fragments(Previous, Current) -> [Previous, Current]. diff --git a/lib/ssl/src/dtls_packet_demux.erl b/lib/ssl/src/dtls_packet_demux.erl index 2e9184b7ac..c6431b55a9 100644 --- a/lib/ssl/src/dtls_packet_demux.erl +++ b/lib/ssl/src/dtls_packet_demux.erl @@ -35,7 +35,8 @@ terminate/2, code_change/3]). -record(state, - {port, + {active_n, + port, listener, transport, dtls_options, @@ -76,10 +77,18 @@ set_sock_opts(PacketSocket, Opts) -> %%% gen_server callbacks %%%=================================================================== -init([Port, {TransportModule, _,_,_} = TransportInfo, EmOpts, InetOptions, DTLSOptions]) -> +init([Port, {TransportModule, _,_,_,_} = TransportInfo, EmOpts, InetOptions, DTLSOptions]) -> try {ok, Socket} = TransportModule:open(Port, InetOptions), - {ok, #state{port = Port, + InternalActiveN = case application:get_env(ssl, internal_active_n) of + {ok, N} when is_integer(N) -> + N; + _ -> + ?INTERNAL_ACTIVE_N + end, + + {ok, #state{active_n = InternalActiveN, + port = Port, first = true, transport = TransportInfo, dtls_options = DTLSOptions, @@ -92,10 +101,11 @@ init([Port, {TransportModule, _,_,_} = TransportInfo, EmOpts, InetOptions, DTLSO handle_call({accept, _}, _, #state{close = true} = State) -> {reply, {error, closed}, State}; -handle_call({accept, Accepter}, From, #state{first = true, +handle_call({accept, Accepter}, From, #state{active_n = N, + first = true, accepters = Accepters, listener = Socket} = State0) -> - next_datagram(Socket), + next_datagram(Socket, N), State = State0#state{first = false, accepters = queue:in({Accepter, From}, Accepters)}, {noreply, State}; @@ -137,19 +147,24 @@ handle_cast({active_once, Client, Pid}, State0) -> State = handle_active_once(Client, Pid, State0), {noreply, State}. -handle_info({Transport, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket, transport = {_,Transport,_,_}} = State0) -> +handle_info({Transport, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket, transport = {_,Transport,_,_,_}} = State0) -> State = handle_datagram({IP, InPortNo}, Msg, State0), - next_datagram(Socket), {noreply, State}; +handle_info({PassiveTag, Socket}, + #state{active_n = N, + listener = Socket, + transport = {_,_,_, udp_error, PassiveTag}}) -> + next_datagram(Socket, N); + %% UDP socket does not have a connection and should not receive an econnreset %% This does however happens on some windows versions. Just ignoring it %% appears to make things work as expected! -handle_info({udp_error, Socket, econnreset = Error}, #state{listener = Socket, transport = {_,_,_, udp_error}} = State) -> +handle_info({udp_error, Socket, econnreset = Error}, #state{listener = Socket, transport = {_,_,_, udp_error,_}} = State) -> Report = io_lib:format("Ignore SSL UDP Listener: Socket error: ~p ~n", [Error]), ?LOG_NOTICE(Report), {noreply, State}; -handle_info({ErrorTag, Socket, Error}, #state{listener = Socket, transport = {_,_,_, ErrorTag}} = State) -> +handle_info({ErrorTag, Socket, Error}, #state{listener = Socket, transport = {_,_,_, ErrorTag,_}} = State) -> Report = io_lib:format("SSL Packet muliplxer shutdown: Socket error: ~p ~n", [Error]), ?LOG_NOTICE(Report), {noreply, State#state{close=true}}; @@ -211,8 +226,8 @@ dispatch(Client, Msg, #state{dtls_msq_queues = MsgQueues} = State) -> kv_update(Client, queue:in(Msg, Queue), MsgQueues)} end end. -next_datagram(Socket) -> - inet:setopts(Socket, [{active, once}]). +next_datagram(Socket, N) -> + inet:setopts(Socket, [{active, N}]). handle_active_once(Client, Pid, #state{dtls_msq_queues = MsgQueues} = State0) -> Queue0 = kv_get(Client, MsgQueues), diff --git a/lib/ssl/src/dtls_socket.erl b/lib/ssl/src/dtls_socket.erl index 4d07372e31..b305d08f70 100644 --- a/lib/ssl/src/dtls_socket.erl +++ b/lib/ssl/src/dtls_socket.erl @@ -45,7 +45,7 @@ listen(Port, #config{transport_info = TransportInfo, Err end. -accept(dtls, #config{transport_info = {Transport,_,_,_}, +accept(dtls, #config{transport_info = {Transport,_,_,_,_}, connection_cb = ConnectionCb, dtls_handler = {Listner, _}}, _Timeout) -> case dtls_packet_demux:accept(Listner, self()) of @@ -55,7 +55,7 @@ accept(dtls, #config{transport_info = {Transport,_,_,_}, {error, Reason} end. -connect(Address, Port, #config{transport_info = {Transport, _, _, _} = CbInfo, +connect(Address, Port, #config{transport_info = {Transport, _, _, _, _} = CbInfo, connection_cb = ConnectionCb, ssl = SslOpts, emulated = EmOpts, @@ -174,7 +174,7 @@ default_inet_values() -> [{active, true}, {mode, list}, {packet, 0}, {packet_size, 0}]. default_cb_info() -> - {gen_udp, udp, udp_closed, udp_error}. + {gen_udp, udp, udp_closed, udp_error, udp_passive}. get_emulated_opts(EmOpts, EmOptNames) -> lists:map(fun(Name) -> {value, Value} = lists:keysearch(Name, 1, EmOpts), diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 8807c575b1..5da924ef16 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -929,7 +929,7 @@ groups(default) -> %%-------------------------------------------------------------------- getopts(#sslsocket{pid = [Pid|_]}, OptionTags) when is_pid(Pid), is_list(OptionTags) -> ssl_connection:get_opts(Pid, OptionTags); -getopts(#sslsocket{pid = {dtls, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, OptionTags) when is_list(OptionTags) -> +getopts(#sslsocket{pid = {dtls, #config{transport_info = {Transport,_,_,_,_}}}} = ListenSocket, OptionTags) when is_list(OptionTags) -> try dtls_socket:getopts(Transport, ListenSocket, OptionTags) of {ok, _} = Result -> Result; @@ -986,7 +986,7 @@ setopts(#sslsocket{pid = [Pid|_]}, Options0) when is_pid(Pid), is_list(Options0) _:_ -> {error, {options, {not_a_proplist, Options0}}} end; -setopts(#sslsocket{pid = {dtls, #config{transport_info = {Transport,_,_,_}}}} = ListenSocket, Options) when is_list(Options) -> +setopts(#sslsocket{pid = {dtls, #config{transport_info = {Transport,_,_,_,_}}}} = ListenSocket, Options) when is_list(Options) -> try dtls_socket:setopts(Transport, ListenSocket, Options) of ok -> ok; @@ -1029,7 +1029,7 @@ getstat(Socket) -> %% %% Description: Get one or more statistic options for a socket. %%-------------------------------------------------------------------- -getstat(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}, Options) when is_port(Listen), is_list(Options) -> +getstat(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _, _}}}}, Options) when is_port(Listen), is_list(Options) -> tls_socket:getstat(Transport, Listen, Options); getstat(#sslsocket{pid = [Pid|_], fd = {Transport, Socket, _, _}}, Options) when is_pid(Pid), is_list(Options) -> @@ -2141,7 +2141,7 @@ default_option_role(_,_,_) -> default_cb_info(tls) -> {gen_tcp, tcp, tcp_closed, tcp_error, tcp_passive}; default_cb_info(dtls) -> - {gen_udp, udp, udp_closed, udp_error}. + {gen_udp, udp, udp_closed, udp_error, udp_passive}. include_security_info([]) -> false; diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 97878431a6..2238b5290d 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). @@ -938,6 +939,11 @@ signature_scheme(?RSA_PSS_PSS_SHA384) -> rsa_pss_pss_sha384; signature_scheme(?RSA_PSS_PSS_SHA512) -> rsa_pss_pss_sha512; signature_scheme(?RSA_PKCS1_SHA1) -> rsa_pkcs1_sha1; signature_scheme(?ECDSA_SHA1) -> ecdsa_sha1; +%% Handling legacy signature algorithms for logging purposes. These algorithms +%% cannot be used in TLS 1.3 handshakes. +signature_scheme(SignAlgo) when is_integer(SignAlgo) -> + <<?BYTE(Hash),?BYTE(Sign)>> = <<?UINT16(SignAlgo)>>, + {ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)}; signature_scheme(_) -> unassigned. %% TODO: reserved code points? diff --git a/lib/ssl/src/ssl_cipher_format.erl b/lib/ssl/src/ssl_cipher_format.erl index 8737181922..e0df3662ef 100644 --- a/lib/ssl/src/ssl_cipher_format.erl +++ b/lib/ssl/src/ssl_cipher_format.erl @@ -1958,6 +1958,22 @@ openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256) -> openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384) -> "ECDH-RSA-AES256-GCM-SHA384"; +%% ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS) RFC7905 +openssl_suite_name(?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) -> + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"; +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) -> + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"; +openssl_suite_name(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) -> + "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"; +openssl_suite_name(?TLS_PSK_WITH_CHACHA20_POLY1305_SHA256) -> + "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256"; +openssl_suite_name(?TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256) -> + "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256"; +openssl_suite_name(?TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256) -> + "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256"; +openssl_suite_name(?TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256) -> + "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256"; + %% TLS 1.3 Cipher Suites RFC8446 openssl_suite_name(?TLS_AES_128_GCM_SHA256) -> "TLS_AES_128_GCM_SHA256"; 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/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 0efedf3400..20d28c33de 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -1323,7 +1323,9 @@ get_signature_scheme_list(#signature_algorithms_cert{ ClientSignatureSchemes; get_signature_scheme_list(#signature_algorithms{ signature_scheme_list = ClientSignatureSchemes}) -> - ClientSignatureSchemes. + %% Filter unassigned and legacy elements + lists:filter(fun (E) -> is_atom(E) andalso E =/= unassigned end, + ClientSignatureSchemes). get_supported_groups(#supported_groups{supported_groups = Groups}) -> Groups. diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml index f1c3aa5a41..0a46139db6 100644 --- a/lib/stdlib/doc/src/erl_pp.xml +++ b/lib/stdlib/doc/src/erl_pp.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2016</year> + <year>2019</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -65,6 +65,10 @@ </datatype> <datatype> <name name="option"/> + <desc> + <p>The option <c>quote_singleton_atom_types</c> + is used to add quotes to all singleton atom types.</p> + </desc> </datatype> <datatype> <name name="options"/> diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 00fd731e1d..e0c37ca030 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2276,6 +2276,9 @@ expr({'fun',Line,Body}, Vt, St) -> case Body of {clauses,Cs} -> fun_clauses(Cs, Vt, St); + {function,record_info,2} -> + %% It is illegal to call record_info/2 with unknown arguments. + {[],add_error(Line, illegal_record_info, St)}; {function,F,A} -> %% BifClash - Fun expression %% N.B. Only allows BIFs here as well, NO IMPORTS!! diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index ada3ff5de3..2630c60859 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -41,10 +41,11 @@ io_lib:chars())). -type(option() :: {hook, hook_function()} - | {encoding, latin1 | unicode | utf8}). + | {encoding, latin1 | unicode | utf8} + | {quote_singleton_atom_types, boolean()}). -type(options() :: hook_function() | [option()]). --record(pp, {value_fun, string_fun, char_fun}). +-record(pp, {value_fun, singleton_atom_type_fun, string_fun, char_fun}). -record(options, {hook, encoding, opts}). @@ -206,22 +207,43 @@ options(Hook) -> #options{hook = Hook, encoding = encoding([]), opts = Hook}. state(Options) when is_list(Options) -> + Quote = proplists:get_bool(quote_singleton_atom_types, Options), case encoding(Options) of - latin1 -> state(); - unicode -> unicode_state() + latin1 -> latin1_state(Quote); + unicode -> unicode_state(Quote) end; state(_Hook) -> - state(). + latin1_state(false). -state() -> +latin1_state(Quote) -> Options = [{encoding,latin1}], - #pp{value_fun = fun(V) -> io_lib_pretty:print(V, Options) end, + ValueFun = fun(V) -> io_lib_pretty:print(V, Options) end, + SingletonFun = + case Quote of + true -> + fun(A) -> + io_lib:write_string_as_latin1(atom_to_list(A), $') + end; %' + false -> + ValueFun + end, + #pp{value_fun = ValueFun, + singleton_atom_type_fun = SingletonFun, string_fun = fun io_lib:write_string_as_latin1/1, char_fun = fun io_lib:write_char_as_latin1/1}. -unicode_state() -> +unicode_state(Quote) -> Options = [{encoding,unicode}], - #pp{value_fun = fun(V) -> io_lib_pretty:print(V, Options) end, + ValueFun = fun(V) -> io_lib_pretty:print(V, Options) end, + SingletonFun = + case Quote of + true -> + fun(A) -> io_lib:write_string(atom_to_list(A), $') end; %' + false -> + ValueFun + end, + #pp{value_fun = ValueFun, + singleton_atom_type_fun = SingletonFun, string_fun = fun io_lib:write_string/1, char_fun = fun io_lib:write_char/1}. @@ -350,7 +372,7 @@ ltype({user_type,Line,T,Ts}, _) -> ltype({remote_type,Line,[M,F,Ts]}, _) -> simple_type({remote,Line,M,F}, Ts); ltype({atom,_,T}, _) -> - {atom,T}; + {singleton_atom_type,T}; ltype(E, P) -> lexpr(E, P, options(none)). @@ -808,12 +830,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), @@ -939,6 +955,7 @@ frmt(Item, I, PP) -> %%% - {prefer_nl,Sep,IPs}: forces linebreak between Is unlesss negative %%% indentation. %%% - {atom,A}: an atom +%%% - {singleton_atom_type,A}: an singleton atom type %%% - {char,C}: a character %%% - {string,S}: a string. %%% - {value,T}: a term. @@ -1006,6 +1023,8 @@ f({value,V}, I, ST, WT, PP) -> f(write_a_value(V, PP), I, ST, WT, PP); f({atom,A}, I, ST, WT, PP) -> f(write_an_atom(A, PP), I, ST, WT, PP); +f({singleton_atom_type,A}, I, ST, WT, PP) -> + f(write_a_singleton_atom_type(A, PP), I, ST, WT, PP); f({char,C}, I, ST, WT, PP) -> f(write_a_char(C, PP), I, ST, WT, PP); f({string,S}, I, ST, WT, PP) -> @@ -1150,6 +1169,9 @@ write_a_value(V, PP) -> write_an_atom(A, PP) -> flat_leaf(write_atom(A, PP)). +write_a_singleton_atom_type(A, PP) -> + flat_leaf(write_singleton_atom_type(A, PP)). + write_a_char(C, PP) -> flat_leaf(write_char(C, PP)). @@ -1184,6 +1206,9 @@ write_value(V, PP) -> write_atom(A, PP) -> (PP#pp.value_fun)(A). +write_singleton_atom_type(A, PP) -> + (PP#pp.singleton_atom_type_fun)(A). + write_string(S, PP) -> (PP#pp.string_fun)(S). diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 939cc1024c..fe98a3796d 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -3576,10 +3576,12 @@ basic_errors(Config) -> {illegal_record_info, <<"f1() -> record_info(42, record). - f2() -> record_info(shoe_size, record).">>, + f2() -> record_info(shoe_size, record). + f3() -> fun record_info/2.">>, [], {errors,[{1,erl_lint,illegal_record_info}, - {2,erl_lint,illegal_record_info}],[]}}, + {2,erl_lint,illegal_record_info}, + {3,erl_lint,illegal_record_info}],[]}}, {illegal_expr, <<"f() -> a:b.">>, diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index f5d80e7e68..c79e29eb11 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -47,11 +47,12 @@ hook/1, neg_indent/1, maps_syntax/1, + quoted_atom_types/1, 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]). @@ -74,14 +75,14 @@ groups() -> [{expr, [], [func, call, recs, try_catch, if_then, receive_after, bits, head_tail, cond1, block, case1, ops, - messages, maps_syntax + messages, maps_syntax, quoted_atom_types ]}, {attributes, [], [misc_attrs, import_export, dialyzer_attrs]}, {tickets, [], [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. @@ -912,6 +913,21 @@ maps_syntax(Config) when is_list(Config) -> ok = pp_forms(F), ok. +quoted_atom_types(Config) when is_list(Config) -> + Q = [{quote_singleton_atom_types, true}], + U = [{encoding,unicode}], + L = [{encoding,latin1}], + F = "-type t() :: a | a().", + "-type t() :: 'a' | a().\n" = + lists:flatten(parse_and_pp_forms(F, Q ++ L)), + "-type t() :: 'a' | a().\n" = + lists:flatten(parse_and_pp_forms(F, Q ++ U)), + UF = "-type t() :: '\x{400}' | '\x{400}'().", + "-type t() :: '\\x{400}' | '\\x{400}'().\n" = + lists:flatten(parse_and_pp_forms(UF, Q ++ L)), + "-type t() :: '\x{400}' | '\x{400}'().\n" = + lists:flatten(parse_and_pp_forms(UF, Q ++ U)), + ok. %% OTP_8567. Avoid duplicated 'undefined' in record field types. otp_8567(Config) when is_list(Config) -> @@ -1172,6 +1188,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/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 87ca9bd32c..4640b2b228 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -24,7 +24,7 @@ -export([default/1,setbag/1,badnew/1,verybadnew/1,named/1,keypos2/1, privacy/1]). -export([empty/1,badinsert/1]). --export([time_lookup/1,badlookup/1,lookup_order/1]). +-export([badlookup/1,lookup_order/1]). -export([delete_elem/1,delete_tab/1,delete_large_tab/1, delete_large_named_table/1, evil_delete/1,baddelete/1,match_delete/1,table_leak/1]). @@ -42,6 +42,8 @@ select_bound_chunk/1, t_delete_all_objects/1, t_insert_list/1, t_test_ms/1, t_select_delete/1,t_select_replace/1,t_select_replace_next_bug/1,t_ets_dets/1]). +-export([test_table_size_concurrency/1,test_table_memory_concurrency/1, + test_delete_table_while_size_snapshot/1, test_delete_table_while_size_snapshot_helper/0]). -export([ordered/1, ordered_match/1, interface_equality/1, fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1, @@ -156,14 +158,18 @@ all() -> whereis_table, delete_unfix_race, test_throughput_benchmark, - {group, benchmark}]. + {group, benchmark}, + test_table_size_concurrency, + test_table_memory_concurrency, + test_delete_table_while_size_snapshot]. + groups() -> [{new, [], [default, setbag, badnew, verybadnew, named, keypos2, privacy]}, {insert, [], [empty, badinsert]}, - {lookup, [], [time_lookup, badlookup, lookup_order]}, + {lookup, [], [badlookup, lookup_order]}, {lookup_element, [], [lookup_element_mult]}, {delete, [], [delete_elem, delete_tab, delete_large_tab, @@ -828,7 +834,11 @@ adjust_xmem([_T1,_T2,_T3,_T4], {A0,B0,C0,D0} = _Mem0, EstCnt) -> {TabSz, EstSz} = erts_debug:get_internal_state('DbTable_words'), HTabSz = TabSz + EstCnt*EstSz, - {A0+TabSz, B0+HTabSz, C0+HTabSz, D0+HTabSz}. + OrdSetExtra = case erlang:system_info(wordsize) of + 8 -> 40; % larger stack on 64 bit architectures + _ -> 0 + end, + {A0+TabSz+OrdSetExtra, B0+HTabSz, C0+HTabSz, D0+HTabSz}. %% Misc. whitebox tests t_whitebox(Config) when is_list(Config) -> @@ -3384,31 +3394,6 @@ badinsert_do(Opts) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Test lookup timing. -time_lookup(Config) when is_list(Config) -> - %% just for timing, really - EtsMem = etsmem(), - Values = repeat_for_opts_all_table_types(fun time_lookup_do/1), - verify_etsmem(EtsMem), - {comment,lists:flatten(io_lib:format( - "~p ets lookups/s",[Values]))}. - -time_lookup_do(Opts) -> - Tab = ets_new(foo,Opts), - fill_tab(Tab,foo), - ets:insert(Tab,{{a,key},foo}), - N = 100000, - {Time,_} = timer:tc(fun() -> time_lookup_many(N, Tab) end), - Seconds = Time / 1000000, - true = ets:delete(Tab), - round(N / Seconds). % lookups/s - -time_lookup_many(0, _Tab) -> - ok; -time_lookup_many(N, Tab) -> - ets:lookup(Tab, {a,key}), - time_lookup_many(N-1, Tab). - %% Check proper return values from bad lookups in existing/non existing %% ets tables. badlookup(Config) when is_list(Config) -> @@ -4102,6 +4087,11 @@ slot_do(Opts) -> fill_tab(Tab,foo), Elts = ets:info(Tab,size), Elts = slot_loop(Tab,0,0), + case ets:info(Tab, type) of + ordered_set -> + '$end_of_table' = ets:slot(Tab,Elts); + _ -> ok + end, true = ets:delete(Tab), verify_etsmem(EtsMem). @@ -4453,6 +4443,127 @@ info_do(Opts) -> undefined = ets:info(non_existing_table_xxyy,safe_fixed), verify_etsmem(EtsMem). +size_loop(_T, 0, _, _) -> + ok; +size_loop(T, I, PrevSize, WhatToTest) -> + Size = ets:info(T, WhatToTest), + case Size < PrevSize of + true -> ct:fail("Bad ets:info/2"); + _ -> ok + end, + size_loop(T, I -1, Size, WhatToTest). + +add_loop(_T, 0) -> + ok; +add_loop(T, I) -> + ets:insert(T, {I}), + add_loop(T, I -1). + + +test_table_counter_concurrency(WhatToTest) -> + ItemsToAdd = 1000000, + SizeLoopSize = 1000, + T = ets:new(k, [public, ordered_set, {write_concurrency, true}]), + 0 = ets:info(T, size), + P = self(), + SpawnedSizeProcs = + [spawn(fun() -> + size_loop(T, SizeLoopSize, 0, WhatToTest), + P ! done + end) + || _ <- lists:seq(1, 6)], + spawn(fun() -> + add_loop(T, ItemsToAdd), + P ! done_add + end), + [receive + done -> ok; + done_add -> ok + end + || _ <- [ok|SpawnedSizeProcs]], + case WhatToTest =:= size of + true -> + ItemsToAdd = ets:info(T, size); + _ -> + ok + end, + ok. + +test_table_size_concurrency(Config) when is_list(Config) -> + test_table_counter_concurrency(size). + +test_table_memory_concurrency(Config) when is_list(Config) -> + test_table_counter_concurrency(memory). + +%% Tests that calling the ets:delete operation on a table T with +%% decentralized counters works while ets:info(T, size) operations are +%% active +test_delete_table_while_size_snapshot(Config) when is_list(Config) -> + %% Run test case in a slave node as other test suites in stdlib + %% depend on that pids are ordered in creation order which is no + %% longer the case when many processes have been started before + Node = start_slave(), + ok = rpc:call(Node, ?MODULE, test_delete_table_while_size_snapshot_helper, []), + test_server:stop_node(Node), + ok. + +test_delete_table_while_size_snapshot_helper()-> + TopParent = self(), + repeat_par( + fun() -> + Table = ets:new(t, [public, ordered_set, + {write_concurrency, true}]), + Parent = self(), + NrOfSizeProcs = 100, + Pids = [ spawn(fun()-> size_process(Table, Parent) end) + || _ <- lists:seq(1, NrOfSizeProcs)], + timer:sleep(1), + ets:delete(Table), + [receive + table_gone -> ok; + Problem -> TopParent ! Problem + end || _ <- Pids] + end, + 15000), + receive + Problem -> throw(Problem) + after 0 -> ok + end. + +size_process(Table, Parent) -> + try ets:info(Table, size) of + N when is_integer(N) -> + size_process(Table, Parent); + undefined -> Parent ! table_gone; + E -> Parent ! {got_unexpected, E} + catch + E -> Parent ! {got_unexpected_exception, E} + end. + +start_slave() -> + MicroSecs = erlang:monotonic_time(), + Name = "ets_" ++ integer_to_list(MicroSecs), + Pa = filename:dirname(code:which(?MODULE)), + {ok, Node} = test_server:start_node(list_to_atom(Name), slave, [{args, "-pa " ++ Pa}]), + Node. + +repeat_par(FunToRepeat, NrOfTimes) -> + repeat_par_help(FunToRepeat, NrOfTimes, NrOfTimes). + +repeat_par_help(_FunToRepeat, 0, OrgNrOfTimes) -> + repeat(fun()-> receive done -> ok end end, OrgNrOfTimes); +repeat_par_help(FunToRepeat, NrOfTimes, OrgNrOfTimes) -> + Parent = self(), + case NrOfTimes rem 5 of + 0 -> timer:sleep(1); + _ -> ok + end, + spawn(fun()-> + FunToRepeat(), + Parent ! done + end), + repeat_par_help(FunToRepeat, NrOfTimes-1, OrgNrOfTimes). + %% Test various duplicate_bags stuff. dups(Config) when is_list(Config) -> repeat_for_opts(fun dups_do/1). 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}). |