diff options
40 files changed, 581 insertions, 309 deletions
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md index 8632f46264..8587774c12 100644 --- a/HOWTO/INSTALL.md +++ b/HOWTO/INSTALL.md @@ -564,6 +564,10 @@ as before, but the build process will take a much longer time. > automatically when `make` is invoked from `$ERL_TOP` with either the > `clean` target, or the default target. It is also automatically invoked > if `./otp_build remove_prebuilt_files` is invoked. +> +> If you need to verify the bootstrap beam files match the provided +> source files, use `./otp_build update_primary` to create a new commit that +> contains differences, if any exist. #### How to Build a Debug Enabled Erlang RunTime System #### diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index eb1d24cf12..f39b640c7e 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -250,6 +250,11 @@ environment variable <c><![CDATA[DISPLAY]]></c> set to <c><![CDATA[gin:0]]></c>.</p> </item> + <tag><c><![CDATA[-epmd_module Module]]></c> (init flag)</tag> + <item> + <p>Configures the module responsible to communicate to + <seealso marker="epmd">epmd</seealso>. Defaults to <c>erl_epmd</c>.</p> + </item> <tag><c><![CDATA[-eval Expr]]></c> (init flag)</tag> <item> <p>Makes <c><![CDATA[init]]></c> evaluate the expression diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 5191742bc9..d8bf45c523 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -2039,7 +2039,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> <fsummary>Set and get limits for busy port message queue.</fsummary> <desc> <marker id="erl_drv_busy_msgq_limits"></marker> - <p>Sets and gets limits that will be used for controling the + <p>Sets and gets limits that will be used for controlling the busy state of the port message queue.</p> <p>The port message queue is set into a busy state when the amount of command data queued on the @@ -2112,7 +2112,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> It is used to identify the condition variable in planned future debug functionality.</p> <p>Returns <c>NULL</c> on failure. The driver - creating the condition variable is responsibile for + creating the condition variable is responsible for destroying it before the driver is unloaded.</p> <p>This function is thread-safe.</p> </desc> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 3d1775e973..4d12daa61a 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -9564,6 +9564,10 @@ timestamp() -> <type name="trace_info_item_result"/> <type name="trace_info_flag"/> <type name="trace_match_spec"/> + <type name="match_variable"/> + <type_desc name="match_variable"> + Approximation of '$1' | '$2' | '$3' | ... + </type_desc> <desc> <p>Returns trace information about a port, process, function, or event.</p> @@ -9695,6 +9699,10 @@ timestamp() -> </fsummary> <type name="trace_pattern_mfa"/> <type name="trace_match_spec"/> + <type_desc name="match_variable"> + Approximation of '$1' | '$2' | '$3' | ... + </type_desc> + <type name="match_variable"/> <desc> <p>The same as <seealso marker="#trace_pattern/3"> @@ -9707,6 +9715,10 @@ timestamp() -> <name name="trace_pattern" arity="3" clause_i="1"/> <fsummary>Set trace pattern for message sending.</fsummary> <type name="trace_match_spec"/> + <type name="match_variable"/> + <type_desc name="match_variable"> + Approximation of '$1' | '$2' | '$3' | ... + </type_desc> <desc> <p>Sets trace pattern for <em>message sending</em>. Must be combined with @@ -9774,6 +9786,10 @@ timestamp() -> <name name="trace_pattern" arity="3" clause_i="2"/> <fsummary>Set trace pattern for tracing of message receiving.</fsummary> <type name="trace_match_spec"/> + <type name="match_variable"/> + <type_desc name="match_variable"> + Approximation of '$1' | '$2' | '$3' | ... + </type_desc> <desc> <p>Sets trace pattern for <em>message receiving</em>. Must be combined with @@ -9844,6 +9860,10 @@ timestamp() -> <type name="trace_pattern_mfa"/> <type name="trace_match_spec"/> <type name="trace_pattern_flag"/> + <type name="match_variable"/> + <type_desc name="match_variable"> + Approximation of '$1' | '$2' | '$3' | ... + </type_desc> <desc> <p>Enables or disables <em>call tracing</em> for one or more functions. Must be combined with diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 099abad4e1..e7301b1b4b 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1397,10 +1397,10 @@ erts_purge_state_add_fun(ErlFunEntry *fe) } Export * -erts_suspend_process_on_pending_purge_lambda(Process *c_p) +erts_suspend_process_on_pending_purge_lambda(Process *c_p, ErlFunEntry* fe) { erts_smp_mtx_lock(&purge_state.mtx); - if (is_value(purge_state.module)) { + if (purge_state.module == fe->module) { /* * The process c_p is about to call a fun in the code * that we are trying to purge. Suspend it and call diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 84e2d42af6..7eaa7a0d8b 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -6547,7 +6547,7 @@ call_fun(Process* p, /* Current process. */ * and let it try again when the purge operation is * done (may succeed or not). */ - ep = erts_suspend_process_on_pending_purge_lambda(p); + ep = erts_suspend_process_on_pending_purge_lambda(p, fe); ASSERT(ep); } else { diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 36cf64e08d..3cd395c2c1 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4848,7 +4848,7 @@ final_touch(LoaderState* stp, struct erl_module_instance* inst_p) } fe->address = code_ptr; #ifdef HIPE - hipe_set_closure_stub(fe, stp->lambdas[i].num_free); + hipe_set_closure_stub(fe); #endif } } diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index 6a3110d0f5..d420a4346c 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -116,7 +116,8 @@ int erts_is_module_native(BeamCodeHeader* code); void erts_beam_bif_load_init(void); struct erl_fun_entry; void erts_purge_state_add_fun(struct erl_fun_entry *fe); -Export *erts_suspend_process_on_pending_purge_lambda(Process *c_p); +Export *erts_suspend_process_on_pending_purge_lambda(Process *c_p, + struct erl_fun_entry*); /* * Layout of the line table. diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c index 9deec946d5..d9118d2549 100644 --- a/erts/emulator/beam/erl_fun.c +++ b/erts/emulator/beam/erl_fun.c @@ -31,6 +31,9 @@ static Hash erts_fun_table; #include "erl_smp.h" +#ifdef HIPE +# include "hipe_mode_switch.h" +#endif static erts_smp_rwmtx_t erts_fun_table_lock; @@ -219,6 +222,10 @@ erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end) fe->pend_purge_address = addr; ERTS_SMP_WRITE_MEMORY_BARRIER; fe->address = unloaded_fun; +#ifdef HIPE + fe->pend_purge_native_address = fe->native_address; + hipe_set_closure_stub(fe); +#endif erts_purge_state_add_fun(fe); } b = b->next; @@ -234,9 +241,12 @@ erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no) for (ix = 0; ix < no; ix++) { ErlFunEntry *fe = funs[ix]; - if (fe->address == unloaded_fun) + if (fe->address == unloaded_fun) { fe->address = fe->pend_purge_address; - fe->pend_purge_address = NULL; +#ifdef HIPE + fe->native_address = fe->pend_purge_native_address; +#endif + } } } @@ -245,8 +255,12 @@ erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no) { Uint ix; - for (ix = 0; ix < no; ix++) + for (ix = 0; ix < no; ix++) { funs[ix]->pend_purge_address = NULL; +#ifdef HIPE + funs[ix]->pend_purge_native_address = NULL; +#endif + } } void @@ -257,6 +271,9 @@ erts_fun_purge_complete(ErlFunEntry **funs, Uint no) for (ix = 0; ix < no; ix++) { ErlFunEntry *fe = funs[ix]; fe->pend_purge_address = NULL; +#ifdef HIPE + fe->pend_purge_native_address = NULL; +#endif if (erts_refc_dectest(&fe->refc, 0) == 0) erts_erase_fun_entry(fe); } @@ -325,6 +342,7 @@ fun_alloc(ErlFunEntry* template) obj->pend_purge_address = NULL; #ifdef HIPE obj->native_address = NULL; + obj->pend_purge_native_address = NULL; #endif return obj; } diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h index 6ba055d92c..717bfee0fb 100644 --- a/erts/emulator/beam/erl_fun.h +++ b/erts/emulator/beam/erl_fun.h @@ -45,6 +45,9 @@ typedef struct erl_fun_entry { erts_refc_t refc; /* Reference count: One for code + one for each fun object in each process. */ BeamInstr *pend_purge_address; /* address stored during a pending purge */ +#ifdef HIPE + UWord* pend_purge_native_address; +#endif } ErlFunEntry; /* diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 98df8a0726..a49b242d7c 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3748,7 +3748,7 @@ dec_term_atom_common: funp->arity = arity; #ifdef HIPE if (funp->fe->native_address == NULL) { - hipe_set_closure_stub(funp->fe, num_free); + hipe_set_closure_stub(funp->fe); } #endif hp = factory->hp; diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index ed95045292..0706f8d2c9 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -718,12 +718,9 @@ void hipe_empty_nstack(Process *p) p->hipe.nstend = NULL; } -void hipe_set_closure_stub(ErlFunEntry *fe, unsigned num_free) +void hipe_set_closure_stub(ErlFunEntry *fe) { - unsigned arity; - - arity = fe->arity; - fe->native_address = (Eterm*) hipe_closure_stub_address(arity); + fe->native_address = (Eterm*) hipe_closure_stub_address(fe->arity); } Eterm hipe_build_stacktrace(Process *p, struct StackTrace *s) diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h index c40077d558..334e978307 100644 --- a/erts/emulator/hipe/hipe_mode_switch.h +++ b/erts/emulator/hipe/hipe_mode_switch.h @@ -59,7 +59,7 @@ void hipe_set_call_trap(Uint *bfun, void *nfun, int is_closure); Process *hipe_mode_switch(Process*, unsigned, Eterm*); void hipe_inc_nstack(Process *p); void hipe_empty_nstack(Process *p); -void hipe_set_closure_stub(ErlFunEntry *fe, unsigned num_free); +void hipe_set_closure_stub(ErlFunEntry *fe); Eterm hipe_build_stacktrace(Process *p, struct StackTrace *s); ERTS_GLB_INLINE void hipe_reserve_beam_trap_frame(Process*, Eterm reg[], unsigned arity); diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 465ddfa7a8..774461c525 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -153,127 +153,23 @@ call_purged_fun_code_there(Config) when is_list(Config) -> ok. call_purged_fun_test(Priv, Data, Type) -> - File = filename:join(Data, "my_code_test2"), - Code = filename:join(Priv, "my_code_test2"), - - catch erlang:purge_module(my_code_test2), - catch erlang:delete_module(my_code_test2), - catch erlang:purge_module(my_code_test2), - - {ok,my_code_test2} = c:c(File, [{outdir,Priv}]), - - T = ets:new(my_code_test2_fun_table, []), - ets:insert(T, {my_fun,my_code_test2:make_fun(4711)}), - ets:insert(T, {my_fun2,my_code_test2:make_fun2()}), - - spawn(fun () -> - [{my_fun2,F2}] = ets:lookup(T, my_fun2), - F2(fun () -> - receive after infinity -> ok end - end, - fun () -> ok end), - exit(completed) - end), - - PurgeType = case Type of - code_gone -> - ok = file:delete(Code++".beam"), - true; - code_reload -> - true; - code_there -> - false - end, - - true = erlang:delete_module(my_code_test2), - - Purge = start_purge(my_code_test2, PurgeType), - - {P0, M0} = spawn_monitor(fun () -> - [{my_fun,F}] = ets:lookup(T, my_fun), - 4712 = F(1), - exit(completed) - end), - - wait_until(fun () -> - {status, suspended} - == process_info(P0, status) - end), - - ok = continue_purge(Purge), - - {P1, M1} = spawn_monitor(fun () -> - [{my_fun,F}] = ets:lookup(T, my_fun), - 4713 = F(2), - exit(completed) - end), - {P2, M2} = spawn_monitor(fun () -> - [{my_fun,F}] = ets:lookup(T, my_fun), - 4714 = F(3), - exit(completed) - end), - - wait_until(fun () -> - {status, suspended} - == process_info(P1, status) - end), - wait_until(fun () -> - {status, suspended} - == process_info(P2, status) - end), - - {current_function, - {erts_code_purger, - pending_purge_lambda, - 3}} = process_info(P0, current_function), - {current_function, - {erts_code_purger, - pending_purge_lambda, - 3}} = process_info(P1, current_function), - {current_function, - {erts_code_purger, - pending_purge_lambda, - 3}} = process_info(P2, current_function), - - case Type of - code_there -> - false = complete_purge(Purge); - _ -> - {true, true} = complete_purge(Purge) - end, + OptsList = case erlang:system_info(hipe_architecture) of + undefined -> [[]]; + _ -> [[], [native]] + end, + [call_purged_fun_test_do(Priv, Data, Type, CO, FO) + || CO <- OptsList, FO <- OptsList]. + + +call_purged_fun_test_do(Priv, Data, Type, CallerOpts, FunOpts) -> + io:format("Compile caller as ~p and funs as ~p\n", [CallerOpts, FunOpts]), + SrcFile = filename:join(Data, "call_purged_fun_tester.erl"), + ObjFile = filename:join(Priv, "call_purged_fun_tester.beam"), + {ok,Mod,Code} = compile:file(SrcFile, [binary, report | CallerOpts]), + {module,Mod} = code:load_binary(Mod, ObjFile, Code), + + call_purged_fun_tester:do(Priv, Data, Type, FunOpts). - case Type of - code_gone -> - receive - {'DOWN', M0, process, P0, Reason0} -> - {undef, _} = Reason0 - end, - receive - {'DOWN', M1, process, P1, Reason1} -> - {undef, _} = Reason1 - end, - receive - {'DOWN', M2, process, P2, Reason2} -> - {undef, _} = Reason2 - end; - _ -> - receive - {'DOWN', M0, process, P0, Reason0} -> - completed = Reason0 - end, - receive - {'DOWN', M1, process, P1, Reason1} -> - completed = Reason1 - end, - receive - {'DOWN', M2, process, P2, Reason2} -> - completed = Reason2 - end, - catch erlang:purge_module(my_code_test2), - catch erlang:delete_module(my_code_test2), - catch erlang:purge_module(my_code_test2) - end, - ok. multi_proc_purge(Config) when is_list(Config) -> %% @@ -975,35 +871,3 @@ flush() -> id(I) -> I. -wait_until(Fun) -> - case Fun() of - true -> - ok; - false -> - receive after 100 -> ok end, - wait_until(Fun) - end. - -start_purge(Mod, Type) when is_atom(Mod) - andalso ((Type == true) - orelse (Type == false)) -> - Ref = make_ref(), - erts_code_purger ! {test_purge, Mod, self(), Type, Ref}, - receive - {started, Ref} -> - Ref - end. - -continue_purge(Ref) when is_reference(Ref) -> - erts_code_purger ! {continue, Ref}, - receive - {continued, Ref} -> - ok - end. - -complete_purge(Ref) when is_reference(Ref) -> - erts_code_purger ! {complete, Ref}, - receive - {test_purge, Res, Ref} -> - Res - end. diff --git a/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl b/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl new file mode 100644 index 0000000000..5e031abca8 --- /dev/null +++ b/erts/emulator/test/code_SUITE_data/call_purged_fun_tester.erl @@ -0,0 +1,167 @@ +-module(call_purged_fun_tester). + +-export([do/4]). + +do(Priv, Data, Type, Opts) -> + File = filename:join(Data, "my_code_test2"), + Code = filename:join(Priv, "my_code_test2"), + + catch erlang:purge_module(my_code_test2), + catch erlang:delete_module(my_code_test2), + catch erlang:purge_module(my_code_test2), + + {ok,my_code_test2} = c:c(File, [{outdir,Priv} | Opts]), + + IsNative = lists:member(native,Opts), + IsNative = code:is_module_native(my_code_test2), + + T = ets:new(my_code_test2_fun_table, []), + ets:insert(T, {my_fun,my_code_test2:make_fun(4711)}), + ets:insert(T, {my_fun2,my_code_test2:make_fun2()}), + + spawn(fun () -> + [{my_fun2,F2}] = ets:lookup(T, my_fun2), + F2(fun () -> + receive after infinity -> ok end + end, + fun () -> ok end), + exit(completed) + end), + + PurgeType = case Type of + code_gone -> + ok = file:delete(Code++".beam"), + true; + code_reload -> + true; + code_there -> + false + end, + + true = erlang:delete_module(my_code_test2), + + Purge = start_purge(my_code_test2, PurgeType), + + {P0, M0} = spawn_monitor(fun () -> + [{my_fun,F}] = ets:lookup(T, my_fun), + 4712 = F(1), + exit(completed) + end), + + wait_until(fun () -> + {status, suspended} + == process_info(P0, status) + end), + + ok = continue_purge(Purge), + + {P1, M1} = spawn_monitor(fun () -> + [{my_fun,F}] = ets:lookup(T, my_fun), + 4713 = F(2), + exit(completed) + end), + {P2, M2} = spawn_monitor(fun () -> + [{my_fun,F}] = ets:lookup(T, my_fun), + 4714 = F(3), + exit(completed) + end), + + wait_until(fun () -> + {status, suspended} + == process_info(P1, status) + end), + wait_until(fun () -> + {status, suspended} + == process_info(P2, status) + end), + + {current_function, + {erts_code_purger, + pending_purge_lambda, + 3}} = process_info(P0, current_function), + {current_function, + {erts_code_purger, + pending_purge_lambda, + 3}} = process_info(P1, current_function), + {current_function, + {erts_code_purger, + pending_purge_lambda, + 3}} = process_info(P2, current_function), + + case Type of + code_there -> + false = complete_purge(Purge); + _ -> + {true, true} = complete_purge(Purge) + end, + + case Type of + code_gone -> + receive + {'DOWN', M0, process, P0, Reason0} -> + {undef, _} = Reason0 + end, + receive + {'DOWN', M1, process, P1, Reason1} -> + {undef, _} = Reason1 + end, + receive + {'DOWN', M2, process, P2, Reason2} -> + {undef, _} = Reason2 + end; + _ -> + receive + {'DOWN', M0, process, P0, Reason0} -> + completed = Reason0 + end, + receive + {'DOWN', M1, process, P1, Reason1} -> + completed = Reason1 + end, + receive + {'DOWN', M2, process, P2, Reason2} -> + completed = Reason2 + end, + catch erlang:purge_module(my_code_test2), + catch erlang:delete_module(my_code_test2), + catch erlang:purge_module(my_code_test2) + end, + ok. + +wait_until(Fun) -> + ok = wait_until(Fun, 20). + +wait_until(Fun, N) -> + case {Fun(),N} of + {true, _} -> + ok; + {false, 0} -> + timeout; + {false, _} -> + receive after 100 -> ok end, + wait_until(Fun, N-1) + end. + +start_purge(Mod, Type) when is_atom(Mod) + andalso ((Type == true) + orelse (Type == false)) -> + Ref = make_ref(), + erts_code_purger ! {test_purge, Mod, self(), Type, Ref}, + receive + {started, Ref} -> + Ref + end. + +continue_purge(Ref) when is_reference(Ref) -> + erts_code_purger ! {continue, Ref}, + receive + {continued, Ref} -> + ok + end. + +complete_purge(Ref) when is_reference(Ref) -> + erts_code_purger ! {complete, Ref}, + receive + {test_purge, Res, Ref} -> + Res + end. diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex fa5960dffd..6159228bff 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex 0a78c5b6c3..aae3976298 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index da08ab383b..f502a7cd36 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2428,10 +2428,11 @@ term_to_binary(_Term, _Options) -> tl(_List) -> erlang:nif_error(undefined). +-type match_variable() :: atom(). % Approximation of '$1' | '$2' | ... -type trace_pattern_mfa() :: {atom(),atom(),arity() | '_'} | on_load. -type trace_match_spec() :: - [{[term()] | '_' ,[term()],[term()]}]. + [{[term()] | '_' | match_variable() ,[term()],[term()]}]. -spec erlang:trace_pattern(MFA, MatchSpec) -> non_neg_integer() when MFA :: trace_pattern_mfa() | send | 'receive', diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index fecdfc348c..a27cb2ba38 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -436,10 +436,11 @@ microstate_accounting(Ref, Threads) -> trace(_PidSpec, _How, _FlagList) -> erlang:nif_error(undefined). +-type match_variable() :: atom(). % Approximation of '$1' | '$2' | ... -type trace_pattern_mfa() :: {atom(),atom(),arity() | '_'} | on_load. -type trace_match_spec() :: - [{[term()] | '_' ,[term()],[term()]}]. + [{[term()] | '_' | match_variable() ,[term()],[term()]}]. -spec trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when MFA :: trace_pattern_mfa(), diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index feb34b364f..5d7fd37270 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -172,13 +172,23 @@ guard(Expr, Sub) -> %% opt_guard_try(#c_seq{arg=Arg,body=Body0}=Seq) -> Body = opt_guard_try(Body0), - case {Arg,Body} of - {#c_call{module=#c_literal{val=Mod}, - name=#c_literal{val=Name}, - args=Args},#c_literal{val=false}} -> + WillFail = case Body of + #c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=error}, + args=[_]} -> + true; + #c_literal{val=false} -> + true; + _ -> + false + end, + case Arg of + #c_call{module=#c_literal{val=Mod}, + name=#c_literal{val=Name}, + args=Args} when WillFail -> %% We have sequence consisting of a call (evaluated %% for a possible exception and/or side effect only), - %% followed by 'false'. + %% followed by 'false' or a call to error/1. %% Since the sequence is inside a try block that will %% default to 'false' if any exception occurs, not %% evalutating the call will not change the behaviour @@ -193,7 +203,7 @@ opt_guard_try(#c_seq{arg=Arg,body=Body0}=Seq) -> %% be safely removed. Body end; - {_,_} -> + _ -> Seq#c_seq{body=Body} end; opt_guard_try(#c_case{clauses=Cs}=Term) -> @@ -1142,7 +1152,13 @@ clause_1(#c_clause{guard=G0,body=B0}=Cl, Ps1, Cexpr, Ctxt, Sub1) -> %% %% case A of NewVar when true -> ... %% - sub_set_var(Var, Cexpr, Sub2); + case cerl:is_c_fname(Cexpr) of + false -> + sub_set_var(Var, Cexpr, Sub2); + true -> + %% We must not copy funs, and especially not into guards. + Sub2 + end; _ -> Sub2 end, @@ -2135,7 +2151,7 @@ letify(Bs, Body) -> -spec opt_not_in_let(cerl:c_let()) -> cerl:cerl(). opt_not_in_let(#c_let{vars=[_]=Vs0,arg=Arg0,body=Body0}=Let) -> - case opt_not_in_let(Vs0, Arg0, Body0) of + case opt_not_in_let_0(Vs0, Arg0, Body0) of {[],#c_values{es=[]},Body} -> Body; {Vs,Arg,Body} -> @@ -2143,13 +2159,7 @@ opt_not_in_let(#c_let{vars=[_]=Vs0,arg=Arg0,body=Body0}=Let) -> end; opt_not_in_let(Let) -> Let. -%% opt_not_in_let(Vs, Arg, Body) -> {Vs',Arg',Body'} -%% Try to optimize away a 'not' operator in a 'let'. - --spec opt_not_in_let([cerl:c_var()], cerl:cerl(), cerl:cerl()) -> - {[cerl:c_var()],cerl:cerl(),cerl:cerl()}. - -opt_not_in_let([#c_var{name=V}]=Vs0, Arg0, Body0) -> +opt_not_in_let_0([#c_var{name=V}]=Vs0, Arg0, Body0) -> case cerl:type(Body0) of call -> %% let <V> = Expr in not V ==> @@ -2180,9 +2190,7 @@ opt_not_in_let([#c_var{name=V}]=Vs0, Arg0, Body0) -> end; _ -> {Vs0,Arg0,Body0} - end; -opt_not_in_let(Vs, Arg, Body) -> - {Vs,Arg,Body}. + end. opt_not_in_let_1(V, Call, Body) -> case Call of @@ -2636,11 +2644,10 @@ opt_simple_let_0(#c_let{arg=Arg0}=Let, Ctxt, Sub) -> opt_simple_let_1(#c_let{vars=Vs0,body=B0}=Let, Arg0, Ctxt, Sub0) -> %% Optimise let and add new substitutions. - {Vs1,Args,Sub1} = let_substs(Vs0, Arg0, Sub0), - BodySub = update_let_types(Vs1, Args, Sub1), - B1 = body(B0, Ctxt, BodySub), - Arg1 = core_lib:make_values(Args), - {Vs,Arg,B} = opt_not_in_let(Vs1, Arg1, B1), + {Vs,Args,Sub1} = let_substs(Vs0, Arg0, Sub0), + BodySub = update_let_types(Vs, Args, Sub1), + B = body(B0, Ctxt, BodySub), + Arg = core_lib:make_values(Args), opt_simple_let_2(Let, Vs, Arg, B, B0, Ctxt, Sub1). opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) -> diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index ced0e39d06..0097e28d4d 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -26,7 +26,7 @@ unused_multiple_values_error/1,unused_multiple_values/1, multiple_aliases/1,redundant_boolean_clauses/1, mixed_matching_clauses/1,unnecessary_building/1, - no_no_file/1,configuration/1]). + no_no_file/1,configuration/1,supplies/1]). -export([foo/0,foo/1,foo/2,foo/3]). @@ -45,7 +45,7 @@ groups() -> unused_multiple_values_error,unused_multiple_values, multiple_aliases,redundant_boolean_clauses, mixed_matching_clauses,unnecessary_building, - no_no_file,configuration]}]. + no_no_file,configuration,supplies]}]. init_per_suite(Config) -> @@ -511,4 +511,20 @@ configuration() -> art() -> creating. +%% core_lint would complain after optimization. A call to error/1 +%% must not occur unconditionally in a guard. +supplies(_Config) -> + case ?MODULE of + core_fold_inline_SUITE -> + %% Other error behaviour when inlined. + ok; + _ -> + {'EXIT',{function_clause,_}} = (catch do_supplies(#{1 => <<1,2,3>>})), + {'EXIT',{function_clause,_}} = (catch do_supplies(#{1 => a})), + {'EXIT',{function_clause,_}} = (catch do_supplies(42)), + ok + end. + +do_supplies(#{1 := Value}) when byte_size(Value), byte_size(kg) -> working. + id(I) -> I. diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl index 6302f82f29..429d6b79e0 100644 --- a/lib/compiler/test/guard_SUITE.erl +++ b/lib/compiler/test/guard_SUITE.erl @@ -87,6 +87,7 @@ misc(Config) when is_list(Config) -> {ok,buf,<<>>} = get_data({o,true,0}, 42, buf), {ok,buf,<<>>} = get_data({o,false,0}, 0, buf), error = get_data({o,false,0}, 42, buf), + ok. @@ -343,6 +344,11 @@ complex_semicolon(Config) when is_list(Config) -> ok = csemi7(#{a=>1}, 3, 3), ok = csemi7(#{a=>1, b=>3}, 0, 0), + %% 8: Make sure that funs cannot be copied into guards. + ok = csemi8(true), + error = csemi8(false), + error = csemi8(42), + ok. csemi1(Type, Val) when is_list(Val), Type == float; @@ -457,6 +463,13 @@ csemi6(_, _) -> error. csemi7(A, B, C) when A#{a:=B} > #{a=>1}; abs(C) > 2 -> ok; csemi7(_, _, _) -> error. +csemi8(Together) -> + case fun csemi8/1 of + Typically when Together; Typically, Together -> ok; + _ -> error + end. + + comma(Config) when is_list(Config) -> %% ',' combinations of literal true/false. diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index f884e6e7d6..35f7db82bd 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -281,7 +281,6 @@ bad_arith(Config) when is_list(Config) -> {3,sys_core_fold,{eval_failure,badarith}}, {9,sys_core_fold,nomatch_guard}, {9,sys_core_fold,{eval_failure,badarith}}, - {9,sys_core_fold,{no_effect,{erlang,is_integer,1}}}, {10,sys_core_fold,nomatch_guard}, {10,sys_core_fold,{eval_failure,badarith}}, {15,sys_core_fold,{eval_failure,badarith}} diff --git a/lib/dialyzer/test/small_SUITE_data/src/ms.erl b/lib/dialyzer/test/small_SUITE_data/src/ms.erl new file mode 100644 index 0000000000..47a5e886cf --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/ms.erl @@ -0,0 +1,8 @@ +-module(ms). +-export([t/0]). + +-include_lib("stdlib/include/ms_transform.hrl"). + +t() -> + MS = dbg:fun2ms(fun(All) -> message(All) end), + erlang:trace_pattern({m, f, '_'}, MS). diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml index 43873e44e2..f2c7889e58 100644 --- a/lib/eldap/doc/src/eldap.xml +++ b/lib/eldap/doc/src/eldap.xml @@ -197,7 +197,7 @@ </type> <desc> <p> Add an entry. The entry must not exist.</p> - <pre> + <code> add(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com", [{"objectclass", ["person"]}, @@ -205,7 +205,7 @@ {"sn", ["Valentine"]}, {"telephoneNumber", ["545 555 00"]}] ) - </pre> + </code> </desc> </func> <func> @@ -216,9 +216,9 @@ </type> <desc> <p> Delete an entry.</p> - <pre> + <code> delete(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com") - </pre> + </code> </desc> </func> @@ -259,11 +259,11 @@ </type> <desc> <p> Modify an entry.</p> - <pre> + <code> modify(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com", [eldap:mod_replace("telephoneNumber", ["555 555 00"]), eldap:mod_add("description", ["LDAP Hacker"]) ]) - </pre> + </code> </desc> </func> <func> @@ -320,10 +320,10 @@ whether the current RDN should be removed from the attribute list after the after operation. <c>NewSupDN</c> is the new parent that the RDN shall be moved to. If the old parent should remain as parent, <c>NewSupDN</c> shall be "".</p> - <pre> + <code> modify_dn(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com ", "cn=Bill Jr Valentine", true, "") - </pre> + </code> </desc> </func> <func> @@ -342,10 +342,10 @@ Default values: scope is <c>wholeSubtree()</c>, deref is <c>derefAlways()</c>, types_only is <c>false</c> and timeout is <c>0</c> (meaning infinity). </p> - <pre> + <code> Filter = eldap:substrings("cn", [{any,"V"}]), search(Handle, [{base, "dc=example, dc=com"}, {filter, Filter}, {attributes, ["cn"]}]), - </pre> + </code> <p>The <c>timeout</c> option in the <c>SearchOptions</c> is for the ldap server, while the timeout in <seealso marker="#open/2">eldap:open/2</seealso> is used for each individual request in the search operation. @@ -454,7 +454,7 @@ </type> <desc> <p>Creates an extensible match filter. For example, </p> <code> - eldap:extensibleMatch("Bar", [{type,"sn"}, {matchingRule,"caseExactMatch"}])) + eldap:extensibleMatch("Bar", [{type,"sn"}, {matchingRule,"caseExactMatch"}])) </code> <p>creates a filter which performs a <c>caseExactMatch</c> on the attribute <c>sn</c> and matches with the value <c>"Bar"</c>. The default value of <c>dnAttributes</c> is <c>false</c>.</p> </desc> </func> diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 09497482cf..b674b3ca93 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -1477,8 +1477,8 @@ f.txt: {person, "kalle", 25}. <tag><c>16#400</c></tag> <item><p>set group id on execution</p></item> </taglist> - <p>On Unix platforms, the following bits - can also be set:</p> + <p>On Unix platforms, other bits than those listed above + may be set.</p> </item> <tag><c>links = integer() >= 0</c></tag> <item> @@ -2042,8 +2042,8 @@ f.txt: {person, "kalle", 25}. <tag><c>16#400</c></tag> <item><p>Set group id on execution</p></item> </taglist> - <p>On Unix platforms, the following bits - can also be set.</p> + <p>On Unix platforms, other bits than those listed above + may be set.</p> </item> <tag><c>uid = integer() >= 0</c></tag> <item> diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl index 6e84a27ec9..0fabdc7929 100644 --- a/lib/mnesia/test/mnesia_test_lib.erl +++ b/lib/mnesia/test/mnesia_test_lib.erl @@ -263,6 +263,7 @@ slave_start_link(Host, Name, Retries) -> Path = code:get_path(), ok = rpc:call(NewNode, file, set_cwd, [Cwd]), true = rpc:call(NewNode, code, set_path, [Path]), + ok = rpc:call(NewNode, error_logger, tty, [false]), spawn_link(NewNode, ?MODULE, slave_sup, []), rpc:multicall([node() | nodes()], global, sync, []), {ok, NewNode}; diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl index 426e2f5125..85b31f3669 100644 --- a/lib/ssh/src/ssh_channel.erl +++ b/lib/ssh/src/ssh_channel.erl @@ -261,7 +261,7 @@ handle_info({ssh_cm, _, _} = Msg, #state{cm = ConnectionManager, adjust_window(Msg), {noreply, State#state{channel_state = ChannelState}, Timeout}; {stop, ChannelId, ChannelState} -> - ssh_connection:close(ConnectionManager, ChannelId), + catch ssh_connection:close(ConnectionManager, ChannelId), {stop, normal, State#state{close_sent = true, channel_state = ChannelState}} end; diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl index bd6bc0335b..ce5596e0f9 100644 --- a/lib/ssh/src/ssh_dbg.erl +++ b/lib/ssh/src/ssh_dbg.erl @@ -113,7 +113,12 @@ setup_tracer(Write, MangleArg) -> ok. %%%---------------------------------------------------------------- -shrink_bin(B) when is_binary(B), size(B)>100 -> {'*** SHRINKED BIN',size(B),element(1,split_binary(B,20)),'***'}; +shrink_bin(B) when is_binary(B), size(B)>100 -> {'*** SHRINKED BIN', + size(B), + element(1,split_binary(B,20)), + '...', + element(2,split_binary(B,size(B)-20)) + }; shrink_bin(L) when is_list(L) -> lists:map(fun shrink_bin/1, L); shrink_bin(T) when is_tuple(T) -> list_to_tuple(shrink_bin(tuple_to_list(T))); shrink_bin(X) -> X. diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 51e0d5196b..0a0ab5cdf7 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -315,9 +315,9 @@ init_per_testcase(TC, Config) when TC==shell_no_unicode ; {user_passwords, [{"foo", "bar"}]}]), ct:sleep(500), IO = ssh_test_lib:start_io_server(), - Shell = ssh_test_lib:start_shell(Port, IO, UserDir, - [{silently_accept_hosts, true}, - {user,"foo"},{password,"bar"}]), + Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}, + {silently_accept_hosts, true}, + {user,"foo"},{password,"bar"}]), ct:log("IO=~p, Shell=~p, self()=~p",[IO,Shell,self()]), ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p", [file:native_name_encoding(),io:getopts()]), @@ -343,14 +343,15 @@ end_per_testcase(TC, Config) when TC==shell_no_unicode ; TC==shell_unicode_string -> case proplists:get_value(sftpd, Config) of {Pid, _, _} -> - ssh:stop_daemon(Pid), - ssh:stop(); + catch ssh:stop_daemon(Pid); _ -> - ssh:stop() - end; + ok + end, + end_per_testcase(Config); end_per_testcase(_TestCase, Config) -> end_per_testcase(Config). -end_per_testcase(_Config) -> + +end_per_testcase(_Config) -> ssh:stop(), ok. @@ -524,7 +525,7 @@ shell(Config) when is_list(Config) -> ct:sleep(500), IO = ssh_test_lib:start_io_server(), - Shell = ssh_test_lib:start_shell(Port, IO, UserDir), + Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}]), receive {'EXIT', _, _} -> ct:fail(no_ssh_connection); @@ -562,10 +563,10 @@ exec_key_differs(Config, UserPKAlgs) -> ct:sleep(500), IO = ssh_test_lib:start_io_server(), - Shell = ssh_test_lib:start_shell(Port, IO, UserDir, - [{preferred_algorithms,[{public_key,['ssh-rsa']}]}, - {pref_public_key_algs,UserPKAlgs} - ]), + Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}, + {preferred_algorithms,[{public_key,['ssh-rsa']}]}, + {pref_public_key_algs,UserPKAlgs} + ]), receive @@ -596,9 +597,9 @@ exec_key_differs_fail(Config) when is_list(Config) -> ct:sleep(500), IO = ssh_test_lib:start_io_server(), - ssh_test_lib:start_shell(Port, IO, UserDir, - [{preferred_algorithms,[{public_key,['ssh-rsa']}]}, - {pref_public_key_algs,['ssh-dss']}]), + ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}, + {preferred_algorithms,[{public_key,['ssh-rsa']}]}, + {pref_public_key_algs,['ssh-dss']}]), receive {'EXIT', _, _} -> ok; diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl index 61883c0647..4cc12cbcbe 100644 --- a/lib/ssh/test/ssh_options_SUITE.erl +++ b/lib/ssh/test/ssh_options_SUITE.erl @@ -540,10 +540,18 @@ connectfun_disconnectfun_server(Config) -> {disconnect,Ref,R} -> ct:log("Disconnect result: ~p",[R]), ssh:stop_daemon(Pid) - after 2000 -> + after 5000 -> + receive + X -> ct:log("received ~p",[X]) + after 0 -> ok + end, {fail, "No disconnectfun action"} end - after 2000 -> + after 5000 -> + receive + X -> ct:log("received ~p",[X]) + after 0 -> ok + end, {fail, "No connectfun action"} end. @@ -649,7 +657,7 @@ disconnectfun_option_server(Config) -> ct:log("Server detected disconnect: ~p",[Reason]), ssh:stop_daemon(Pid), ok - after 3000 -> + after 5000 -> receive X -> ct:log("received ~p",[X]) after 0 -> ok @@ -974,7 +982,14 @@ ssh_connect_negtimeout(Config, Parallel) -> ct:sleep(round(Factor * NegTimeOut)), case inet:sockname(Socket) of - {ok,_} -> ct:fail("Socket not closed"); + {ok,_} -> + %% Give it another chance... + ct:log("Sleep more...",[]), + ct:sleep(round(Factor * NegTimeOut)), + case inet:sockname(Socket) of + {ok,_} -> ct:fail("Socket not closed"); + {error,_} -> ok + end; {error,_} -> ok end. @@ -1003,7 +1018,7 @@ ssh_connect_nonegtimeout_connected(Config, Parallel) -> ct:sleep(500), IO = ssh_test_lib:start_io_server(), - Shell = ssh_test_lib:start_shell(Port, IO, UserDir), + Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}]), receive Error = {'EXIT', _, _} -> ct:log("~p",[Error]), diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl index b10ec3707f..74bbc291b2 100644 --- a/lib/ssh/test/ssh_renegotiate_SUITE.erl +++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl @@ -92,11 +92,11 @@ rekey(Config) -> ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, [{rekey_limit, 0}]), - Kex1 = get_kex_init(ConnectionRef), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), receive after ?REKEY_DATA_TMO -> %%By this time rekeying would have been done - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), false = (Kex2 == Kex1), ssh:close(ConnectionRef), ssh:stop_daemon(Pid) @@ -120,31 +120,31 @@ rekey_limit(Config) -> {max_random_length_padding,0}]), {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef), - Kex1 = get_kex_init(ConnectionRef), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), timer:sleep(?REKEY_DATA_TMO), - Kex1 = get_kex_init(ConnectionRef), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), Data = lists:duplicate(159000,1), ok = ssh_sftp:write_file(SftpPid, DataFile, Data), timer:sleep(?REKEY_DATA_TMO), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), false = (Kex2 == Kex1), timer:sleep(?REKEY_DATA_TMO), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), ok = ssh_sftp:write_file(SftpPid, DataFile, "hi\n"), timer:sleep(?REKEY_DATA_TMO), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), false = (Kex2 == Kex1), timer:sleep(?REKEY_DATA_TMO), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), ssh_sftp:stop_channel(SftpPid), ssh:close(ConnectionRef), @@ -169,7 +169,7 @@ renegotiate1(Config) -> ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]), {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef), - Kex1 = get_kex_init(ConnectionRef), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]), @@ -181,7 +181,7 @@ renegotiate1(Config) -> timer:sleep(2000), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), false = (Kex2 == Kex1), @@ -208,7 +208,7 @@ renegotiate2(Config) -> ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]), {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef), - Kex1 = get_kex_init(ConnectionRef), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]), @@ -223,7 +223,7 @@ renegotiate2(Config) -> timer:sleep(2000), - Kex2 = get_kex_init(ConnectionRef), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), false = (Kex2 == Kex1), @@ -235,19 +235,3 @@ renegotiate2(Config) -> %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- -%% get_kex_init - helper function to get key_exchange_init_msg -get_kex_init(Conn) -> - %% First, validate the key exchange is complete (StateName == connected) - {{connected,_},S} = sys:get_state(Conn), - %% Next, walk through the elements of the #state record looking - %% for the #ssh_msg_kexinit record. This method is robust against - %% changes to either record. The KEXINIT message contains a cookie - %% unique to each invocation of the key exchange procedure (RFC4253) - SL = tuple_to_list(S), - case lists:keyfind(ssh_msg_kexinit, 1, SL) of - false -> - throw(not_found); - KexInit -> - KexInit - end. - diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl index 6fd401d182..f93237f3e7 100644 --- a/lib/ssh/test/ssh_test_lib.erl +++ b/lib/ssh/test/ssh_test_lib.erl @@ -127,24 +127,19 @@ std_simple_exec(Host, Port, Config, Opts) -> ssh:close(ConnectionRef). -start_shell(Port, IOServer, UserDir) -> - start_shell(Port, IOServer, UserDir, []). - -start_shell(Port, IOServer, UserDir, Options) -> - spawn_link(?MODULE, init_shell, [Port, IOServer, [{user_dir, UserDir}|Options]]). - start_shell(Port, IOServer) -> - spawn_link(?MODULE, init_shell, [Port, IOServer, []]). + start_shell(Port, IOServer, []). -init_shell(Port, IOServer, UserDir) -> - Host = hostname(), - Options = [{user_interaction, false}, {silently_accept_hosts, - true}] ++ UserDir, - group_leader(IOServer, self()), - loop_shell(Host, Port, Options). +start_shell(Port, IOServer, ExtraOptions) -> + spawn_link( + fun() -> + Host = hostname(), + Options = [{user_interaction, false}, + {silently_accept_hosts,true} | ExtraOptions], + group_leader(IOServer, self()), + ssh:shell(Host, Port, Options) + end). -loop_shell(Host, Port, Options) -> - ssh:shell(Host, Port, Options). start_io_server() -> spawn_link(?MODULE, init_io_server, [self()]). @@ -802,3 +797,40 @@ busy_wait(Nus, T0) -> end. %%%---------------------------------------------------------------- +%% get_kex_init - helper function to get key_exchange_init_msg + +get_kex_init(Conn) -> + Ref = make_ref(), + {ok,TRef} = timer:send_after(15000, {reneg_timeout,Ref}), + get_kex_init(Conn, Ref, TRef). + +get_kex_init(Conn, Ref, TRef) -> + %% First, validate the key exchange is complete (StateName == connected) + case sys:get_state(Conn) of + {{connected,_}, S} -> + timer:cancel(TRef), + %% Next, walk through the elements of the #state record looking + %% for the #ssh_msg_kexinit record. This method is robust against + %% changes to either record. The KEXINIT message contains a cookie + %% unique to each invocation of the key exchange procedure (RFC4253) + SL = tuple_to_list(S), + case lists:keyfind(ssh_msg_kexinit, 1, SL) of + false -> + throw(not_found); + KexInit -> + KexInit + end; + + {OtherState, S} -> + ct:log("Not in 'connected' state: ~p",[OtherState]), + receive + {reneg_timeout,Ref} -> + ct:log("S = ~p", [S]), + ct:fail(reneg_timeout) + after 0 -> + timer:sleep(100), % If renegotiation is complete we do not + % want to exit on the reneg_timeout + get_kex_init(Conn, Ref, TRef) + end + end. + diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index f481e9c1ce..2c7fe7898f 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -29,6 +29,7 @@ -define(TIMEOUT, 50000). -define(SSH_DEFAULT_PORT, 22). +-define(REKEY_DATA_TMO, 65000). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- @@ -55,7 +56,8 @@ groups() -> erlang_client_openssh_server_publickey_rsa, erlang_client_openssh_server_password, erlang_client_openssh_server_kexs, - erlang_client_openssh_server_nonexistent_subsystem + erlang_client_openssh_server_nonexistent_subsystem, + erlang_client_openssh_server_renegotiate ]}, {erlang_server, [], [erlang_server_openssh_client_public_key_dsa, erlang_server_openssh_client_public_key_rsa, @@ -105,6 +107,11 @@ init_per_testcase(erlang_server_openssh_client_public_key_rsa, Config) -> chk_key(sshc, 'ssh-rsa', ".ssh/id_rsa", Config); init_per_testcase(erlang_client_openssh_server_publickey_dsa, Config) -> chk_key(sshd, 'ssh-dss', ".ssh/id_dsa", Config); +init_per_testcase(erlang_server_openssh_client_renegotiate, Config) -> + case os:type() of + {unix,_} -> ssh:start(), Config; + Type -> {skip, io_lib:format("Unsupported test on ~p",[Type])} + end; init_per_testcase(_TestCase, Config) -> ssh:start(), Config. @@ -146,7 +153,7 @@ erlang_shell_client_openssh_server(Config) when is_list(Config) -> IO = ssh_test_lib:start_io_server(), Shell = ssh_test_lib:start_shell(?SSH_DEFAULT_PORT, IO), IO ! {input, self(), "echo Hej\n"}, - receive_hej(), + receive_data("Hej"), IO ! {input, self(), "exit\n"}, receive_logout(), receive_normal_exit(Shell). @@ -393,24 +400,34 @@ erlang_server_openssh_client_renegotiate(Config) -> SystemDir = proplists:get_value(data_dir, Config), PrivDir = proplists:get_value(priv_dir, Config), KnownHosts = filename:join(PrivDir, "known_hosts"), + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, {public_key_alg, PubKeyAlg}, {failfun, fun ssh_test_lib:failfun/2}]), +%% catch ssh_dbg:messages(fun(String,_D) -> ct:log(String) end), ct:sleep(500), + RenegLimitK = 3, DataFile = filename:join(PrivDir, "renegotiate_openssh_client.data"), - Data = lists:duplicate(32000, $a), + Data = lists:duplicate(trunc(1.1*RenegLimitK*1024), $a), ok = file:write_file(DataFile, Data), Cmd = "ssh -p " ++ integer_to_list(Port) ++ " -o UserKnownHostsFile=" ++ KnownHosts ++ - " -o RekeyLimit=20K" ++ + " -o RekeyLimit=" ++ integer_to_list(RenegLimitK) ++"K" ++ " " ++ Host ++ " < " ++ DataFile, OpenSsh = ssh_test_lib:open_port({spawn, Cmd}), Expect = fun({data,R}) -> - try lists:prefix(binary_to_list(R), Data) + try + NonAlphaChars = [C || C<-lists:seq(1,255), + not lists:member(C,lists:seq($a,$z)), + not lists:member(C,lists:seq($A,$Z)) + ], + Lines = string:tokens(binary_to_list(R), NonAlphaChars), + lists:any(fun(L) -> length(L)>1 andalso lists:prefix(L, Data) end, + Lines) catch _:_ -> false end; @@ -419,9 +436,61 @@ erlang_server_openssh_client_renegotiate(Config) -> end, ssh_test_lib:rcv_expected(Expect, OpenSsh, ?TIMEOUT), + %% Unfortunatly we can't check that there has been a renegotiation, just trust OpenSSH. ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +erlang_client_openssh_server_renegotiate(_Config) -> + process_flag(trap_exit, true), + + IO = ssh_test_lib:start_io_server(), + Ref = make_ref(), + Parent = self(), + + catch ssh_dbg:messages(fun(X,_) -> ct:log(X) end), + Shell = + spawn_link( + fun() -> + Host = ssh_test_lib:hostname(), + Options = [{user_interaction, false}, + {silently_accept_hosts,true}], + group_leader(IO, self()), + {ok, ConnRef} = ssh:connect(Host, ?SSH_DEFAULT_PORT, Options), + case ssh_connection:session_channel(ConnRef, infinity) of + {ok,ChannelId} -> + success = ssh_connection:ptty_alloc(ConnRef, ChannelId, []), + Args = [{channel_cb, ssh_shell}, + {init_args,[ConnRef, ChannelId]}, + {cm, ConnRef}, {channel_id, ChannelId}], + {ok, State} = ssh_channel:init([Args]), + Parent ! {ok, Ref, ConnRef}, + ssh_channel:enter_loop(State); + Error -> + Parent ! {error, Ref, Error} + end, + receive + nothing -> ok + end + end), + + receive + {error, Ref, Error} -> + ct:fail("Error=~p",[Error]); + {ok, Ref, ConnectionRef} -> + IO ! {input, self(), "echo Hej1\n"}, + receive_data("Hej1"), + Kex1 = ssh_test_lib:get_kex_init(ConnectionRef), + ssh_connection_handler:renegotiate(ConnectionRef), + IO ! {input, self(), "echo Hej2\n"}, + receive_data("Hej2"), + Kex2 = ssh_test_lib:get_kex_init(ConnectionRef), + IO ! {input, self(), "exit\n"}, + receive_logout(), + receive_normal_exit(Shell), + true = (Kex1 =/= Kex2) + end. + +%%-------------------------------------------------------------------- erlang_client_openssh_server_password() -> [{doc, "Test client password option"}]. erlang_client_openssh_server_password(Config) when is_list(Config) -> @@ -476,27 +545,24 @@ erlang_client_openssh_server_nonexistent_subsystem(Config) when is_list(Config) %%-------------------------------------------------------------------- %%% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- -receive_hej() -> +receive_data(Data) -> receive - <<"Hej", _binary>> = Hej -> - ct:log("Expected result: ~p~n", [Hej]); - <<"Hej\n", _binary>> = Hej -> - ct:log("Expected result: ~p~n", [Hej]); - <<"Hej\r\n", _/binary>> = Hej -> - ct:log("Expected result: ~p~n", [Hej]); - Info -> - Lines = binary:split(Info, [<<"\r\n">>], [global]), - case lists:member(<<"Hej">>, Lines) of + Info when is_binary(Info) -> + Lines = string:tokens(binary_to_list(Info), "\r\n "), + case lists:member(Data, Lines) of true -> ct:log("Expected result found in lines: ~p~n", [Lines]), ok; false -> ct:log("Extra info: ~p~n", [Info]), - receive_hej() - end + receive_data(Data) + end; + Other -> + ct:log("Unexpected: ~p",[Other]), + receive_data(Data) after 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) - end. + end. receive_logout() -> receive diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml index 1150043e76..61f88e3860 100644 --- a/lib/ssl/doc/src/ssl_distribution.xml +++ b/lib/ssl/doc/src/ssl_distribution.xml @@ -43,7 +43,7 @@ Erlang node distributed, <c>net_kernel</c> uses this module to set up listen ports and connections.</p> - <p>In the SSL application, an exra distribution + <p>In the SSL application, an extra distribution module, <c>inet_tls_dist</c>, can be used as an alternative. All distribution connections will use SSL and all participating Erlang nodes in a distributed system must use diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml index da03c39a26..e64b2ce18a 100644 --- a/lib/stdlib/doc/src/proc_lib.xml +++ b/lib/stdlib/doc/src/proc_lib.xml @@ -66,6 +66,12 @@ <seealso marker="sasl:error_logging">SASL Error Logging</seealso> in the SASL User's Guide.</p> + <p>Unlike in "plain Erlang", <c>proc_lib</c> processes will not generate + <em>error reports</em>, which are written to the terminal by the + emulator and do not require SASL to be started. All exceptions are + converted to <em>exits</em> which are ignored by the default + <c>error_logger</c> handler.</p> + <p>The crash report contains the previously stored information, such as ancestors and initial function, the termination reason, and information about other processes that terminate as a result diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl index 3dc1848550..363705b0f4 100644 --- a/lib/stdlib/src/proc_lib.erl +++ b/lib/stdlib/src/proc_lib.erl @@ -232,7 +232,7 @@ init_p(Parent, Ancestors, Fun) when is_function(Fun) -> Fun() catch Class:Reason -> - exit_p(Class, Reason) + exit_p(Class, Reason, erlang:get_stacktrace()) end. -spec init_p(pid(), [pid()], atom(), atom(), [term()]) -> term(). @@ -247,7 +247,7 @@ init_p_do_apply(M, F, A) -> apply(M, F, A) catch Class:Reason -> - exit_p(Class, Reason) + exit_p(Class, Reason, erlang:get_stacktrace()) end. -spec wake_up(atom(), atom(), [term()]) -> term(). @@ -257,22 +257,29 @@ wake_up(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> apply(M, F, A) catch Class:Reason -> - exit_p(Class, Reason) + exit_p(Class, Reason, erlang:get_stacktrace()) end. -exit_p(Class, Reason) -> +exit_p(Class, Reason, Stacktrace) -> case get('$initial_call') of {M,F,A} when is_atom(M), is_atom(F), is_integer(A) -> MFA = {M,F,make_dummy_args(A, [])}, crash_report(Class, Reason, MFA), - exit(Reason); + erlang:raise(exit, exit_reason(Class, Reason, Stacktrace), Stacktrace); _ -> %% The process dictionary has been cleared or %% possibly modified. crash_report(Class, Reason, []), - exit(Reason) + erlang:raise(exit, exit_reason(Class, Reason, Stacktrace), Stacktrace) end. +exit_reason(error, Reason, Stacktrace) -> + {Reason, Stacktrace}; +exit_reason(exit, Reason, _Stacktrace) -> + Reason; +exit_reason(throw, Reason, Stacktrace) -> + {{nocatch, Reason}, Stacktrace}. + -spec start(Module, Function, Args) -> Ret when Module :: module(), Function :: atom(), diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl index 28f9ab81fe..300baaf1c6 100644 --- a/lib/stdlib/test/gen_statem_SUITE.erl +++ b/lib/stdlib/test/gen_statem_SUITE.erl @@ -505,10 +505,10 @@ abnormal2(Config) -> {ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []), %% bad return value in the gen_statem loop - {{bad_return_from_state_function,badreturn},_} = + {{{bad_return_from_state_function,badreturn},_},_} = ?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason), receive - {'EXIT',Pid,{bad_return_from_state_function,badreturn}} -> ok + {'EXIT',Pid,{{bad_return_from_state_function,badreturn},_}} -> ok after 5000 -> ct:fail(gen_statem_did_not_die) end, @@ -887,7 +887,7 @@ error_format_status(Config) -> gen_statem:start( ?MODULE, start_arg(Config, {data,Data}), []), %% bad return value in the gen_statem loop - {{bad_return_from_state_function,badreturn},_} = + {{{bad_return_from_state_function,badreturn},_},_} = ?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason), receive {error,_, diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl index 416650e27e..a53e99afc9 100644 --- a/lib/stdlib/test/proc_lib_SUITE.erl +++ b/lib/stdlib/test/proc_lib_SUITE.erl @@ -26,7 +26,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - crash/1, sync_start_nolink/1, sync_start_link/1, + crash/1, stacktrace/1, sync_start_nolink/1, sync_start_link/1, spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1, hibernate/1, stop/1, t_format/1]). -export([ otp_6345/1, init_dont_hang/1]). @@ -50,7 +50,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [crash, {group, sync_start}, spawn_opt, hibernate, + [crash, stacktrace, {group, sync_start}, spawn_opt, hibernate, {group, tickets}, stop, t_format]. groups() -> @@ -198,6 +198,31 @@ match_info(Tuple1, Tuple2) when tuple_size(Tuple1) =:= tuple_size(Tuple2) -> match_info(_, _) -> throw(no_match). +stacktrace(Config) when is_list(Config) -> + process_flag(trap_exit, true), + %% Errors. + Pid1 = proc_lib:spawn_link(fun() -> 1 = 2 end), + receive + {'EXIT',Pid1,{{badmatch,2},_Stack1}} -> ok + after 500 -> + ct:fail(error) + end, + %% Exits. + Pid2 = proc_lib:spawn_link(fun() -> exit(bye) end), + receive + {'EXIT',Pid2,bye} -> ok + after 500 -> + ct:fail(exit) + end, + %% Throws. + Pid3 = proc_lib:spawn_link(fun() -> throw(ball) end), + receive + {'EXIT',Pid3,{{nocatch,ball},_Stack3}} -> ok + after 500 -> + ct:fail(throw) + end, + ok. + sync_start_nolink(Config) when is_list(Config) -> _Pid = spawn_link(?MODULE, sp5, [self()]), receive @@ -457,7 +482,7 @@ stop(_Config) -> %% System message is handled, but process dies with other reason %% than the given (in system_terminate/4 below) Pid5 = proc_lib:spawn(SysMsgProc), - {'EXIT',{badmatch,2}} = (catch proc_lib:stop(Pid5,crash,infinity)), + {'EXIT',{{badmatch,2},_Stacktrace}} = (catch proc_lib:stop(Pid5,crash,infinity)), false = erlang:is_process_alive(Pid5), %% Local registered name diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl index 1291a3e5ec..d1a4624419 100644 --- a/lib/tools/src/fprof.erl +++ b/lib/tools/src/fprof.erl @@ -1702,6 +1702,12 @@ trace_handler({trace_ts, Pid, send, _OtherPid, _Msg, TS} = Trace, dump_stack(Dump, get(Pid), Trace), TS; %% +%% send_to_non_existing_process +trace_handler({trace_ts, Pid, send_to_non_existing_process, _OtherPid, _Msg, TS} = Trace, + _Table, _, Dump) -> + dump_stack(Dump, get(Pid), Trace), + TS; +%% %% 'receive' trace_handler({trace_ts, Pid, 'receive', _Msg, TS} = Trace, _Table, _, Dump) -> |