diff options
Diffstat (limited to 'erts/emulator/test')
27 files changed, 928 insertions, 371 deletions
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 146c37b118..e1b42e5d85 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -781,14 +781,20 @@ is_builtin(_Config) -> {F,A} <- M:module_info(exports)], Exp = ordsets:from_list(Exp0), - %% erlang:apply/3 is considered to be built-in, but is not - %% implemented as other BIFs. + %% Built-ins implemented as special instructions. + Instructions = [{erlang,apply,2},{erlang,apply,3},{erlang,yield,0}], - Builtins0 = [{erlang,apply,3}|erlang:system_info(snifs)], + Builtins0 = Instructions ++ erlang:system_info(snifs), Builtins = ordsets:from_list(Builtins0), - NotBuiltin = ordsets:subtract(Exp, Builtins), - _ = [true = erlang:is_builtin(M, F, A) || {M,F,A} <- Builtins], - _ = [false = erlang:is_builtin(M, F, A) || {M,F,A} <- NotBuiltin], + + Fakes = [{M,F,42} || {M,F,_} <- Instructions], + All = ordsets:from_list(Fakes ++ Exp), + NotBuiltin = ordsets:subtract(All, Builtins), + + _ = [{true,_} = {erlang:is_builtin(M, F, A),MFA} || + {M,F,A}=MFA <- Builtins], + _ = [{false,_} = {erlang:is_builtin(M, F, A),MFA} || + {M,F,A}=MFA <- NotBuiltin], ok. diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 61536bacd7..4bc1838139 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -48,6 +48,7 @@ bad_list_to_binary/1, bad_binary_to_list/1, t_split_binary/1, bad_split/1, terms/1, terms_float/1, float_middle_endian/1, + b2t_used_big/1, external_size/1, t_iolist_size/1, t_hash/1, bad_size/1, @@ -57,7 +58,9 @@ otp_5484/1,otp_5933/1, ordering/1,unaligned_order/1,gc_test/1, bit_sized_binary_sizes/1, - otp_6817/1,deep/1,obsolete_funs/1,robustness/1,otp_8117/1, + otp_6817/1,deep/1, + term2bin_tuple_fallbacks/1, + robustness/1,otp_8117/1, otp_8180/1, trapping/1, large/1, error_after_yield/1, cmp_old_impl/1]). @@ -72,12 +75,14 @@ all() -> t_split_binary, bad_split, bad_list_to_binary, bad_binary_to_list, terms, terms_float, float_middle_endian, external_size, t_iolist_size, + b2t_used_big, bad_binary_to_term_2, safe_binary_to_term2, bad_binary_to_term, bad_terms, t_hash, bad_size, bad_term_to_binary, more_bad_terms, otp_5484, otp_5933, ordering, unaligned_order, gc_test, bit_sized_binary_sizes, otp_6817, otp_8117, deep, - obsolete_funs, robustness, otp_8180, trapping, large, + term2bin_tuple_fallbacks, + robustness, otp_8180, trapping, large, error_after_yield, cmp_old_impl]. groups() -> @@ -425,40 +430,77 @@ bad_term_to_binary(Config) when is_list(Config) -> terms(Config) when is_list(Config) -> TestFun = fun(Term) -> - try - S = io_lib:format("~p", [Term]), - io:put_chars(S) - catch - error:badarg -> - io:put_chars("bit sized binary") - end, + S = io_lib:format("~p", [Term]), + io:put_chars(S), Bin = term_to_binary(Term), case erlang:external_size(Bin) of Sz when is_integer(Sz), size(Bin) =< Sz -> ok end, - Bin1 = term_to_binary(Term, [{minor_version, 1}]), - case erlang:external_size(Bin1, [{minor_version, 1}]) of - Sz1 when is_integer(Sz1), size(Bin1) =< Sz1 -> - ok - end, + Bin1 = term_to_binary(Term, [{minor_version, 1}]), + case erlang:external_size(Bin1, [{minor_version, 1}]) of + Sz1 when is_integer(Sz1), size(Bin1) =< Sz1 -> + ok + end, Term = binary_to_term_stress(Bin), Term = binary_to_term_stress(Bin, [safe]), - Unaligned = make_unaligned_sub_binary(Bin), - Term = binary_to_term_stress(Unaligned), - Term = binary_to_term_stress(Unaligned, []), - Term = binary_to_term_stress(Bin, [safe]), + Bin_sz = byte_size(Bin), + {Term,Bin_sz} = binary_to_term_stress(Bin, [used]), + + BinE = <<Bin/binary, 1, 2, 3>>, + {Term,Bin_sz} = binary_to_term_stress(BinE, [used]), + + BinU = make_unaligned_sub_binary(Bin), + Term = binary_to_term_stress(BinU), + Term = binary_to_term_stress(BinU, []), + Term = binary_to_term_stress(BinU, [safe]), + {Term,Bin_sz} = binary_to_term_stress(BinU, [used]), + + BinUE = make_unaligned_sub_binary(BinE), + {Term,Bin_sz} = binary_to_term_stress(BinUE, [used]), + BinC = erlang:term_to_binary(Term, [compressed]), + BinC_sz = byte_size(BinC), + true = BinC_sz =< size(Bin), Term = binary_to_term_stress(BinC), - true = size(BinC) =< size(Bin), + {Term, BinC_sz} = binary_to_term_stress(BinC, [used]), + Bin = term_to_binary(Term, [{compressed,0}]), terms_compression_levels(Term, size(Bin), 1), - UnalignedC = make_unaligned_sub_binary(BinC), - Term = binary_to_term_stress(UnalignedC) + + BinUC = make_unaligned_sub_binary(BinC), + Term = binary_to_term_stress(BinUC), + {Term,BinC_sz} = binary_to_term_stress(BinUC, [used]), + + BinCE = <<BinC/binary, 1, 2, 3>>, + {Term,BinC_sz} = binary_to_term_stress(BinCE, [used]), + + BinUCE = make_unaligned_sub_binary(BinCE), + Term = binary_to_term_stress(BinUCE), + {Term,BinC_sz} = binary_to_term_stress(BinUCE, [used]) end, test_terms(TestFun), ok. +%% Test binary_to_term(_, [used]) returning a big Used integer. +b2t_used_big(_Config) -> + case erlang:system_info(wordsize) of + 8 -> + {skipped, "This is not a 32-bit machine"}; + 4 -> + %% Use a long utf8 atom for large external format but compact on heap. + BigAtom = binary_to_atom(<< <<16#F0908D88:32>> || _ <- lists:seq(1,255) >>, + utf8), + Atoms = (1 bsl 17) + (1 bsl 9), + BigAtomList = lists:duplicate(Atoms, BigAtom), + BigBin = term_to_binary(BigAtomList), + {BigAtomList, Used} = binary_to_term(BigBin, [used]), + 2 = erts_debug:size(Used), + Used = byte_size(BigBin), + Used = 1 + 1 + 4 + Atoms*(1+2+4*255) + 1, + ok + end. + terms_compression_levels(Term, UncompressedSz, Level) when Level < 10 -> BinC = erlang:term_to_binary(Term, [{compressed,Level}]), Term = binary_to_term_stress(BinC), @@ -1160,7 +1202,7 @@ very_big_num(0, Result) -> Result. make_port() -> - open_port({spawn, efile}, [eof]). + hd(erlang:ports()). make_pid() -> spawn_link(?MODULE, sleeper, []). @@ -1261,40 +1303,28 @@ deep_roundtrip(T) -> B = term_to_binary(T), T = binary_to_term(B). -obsolete_funs(Config) when is_list(Config) -> +term2bin_tuple_fallbacks(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), - X = id({1,2,3}), - Y = id([a,b,c,d]), - Z = id({x,y,z}), - obsolete_fun(fun() -> ok end), - obsolete_fun(fun() -> X end), - obsolete_fun(fun(A) -> {A,X} end), - obsolete_fun(fun() -> {X,Y} end), - obsolete_fun(fun() -> {X,Y,Z} end), - - obsolete_fun(fun ?MODULE:all/1), + term2bin_tf(fun ?MODULE:all/1), + term2bin_tf(<<1:1>>), + term2bin_tf(<<90,80:7>>), erts_debug:set_internal_state(available_internal_state, false), ok. -obsolete_fun(Fun) -> - Tuple = case erlang:fun_info(Fun, type) of - {type,external} -> - {module,M} = erlang:fun_info(Fun, module), - {name,F} = erlang:fun_info(Fun, name), - {M,F}; - {type,local} -> - {module,M} = erlang:fun_info(Fun, module), - {index,I} = erlang:fun_info(Fun, index), - {uniq,U} = erlang:fun_info(Fun, uniq), - {env,E} = erlang:fun_info(Fun, env), - {'fun',M,I,U,list_to_tuple(E)} - end, - Tuple = no_fun_roundtrip(Fun). - -no_fun_roundtrip(Term) -> - binary_to_term_stress(erts_debug:get_internal_state({term_to_binary_no_funs,Term})). +term2bin_tf(Term) -> + Tuple = case Term of + Fun when is_function(Fun) -> + {type, external} = erlang:fun_info(Fun, type), + {module,M} = erlang:fun_info(Fun, module), + {name,F} = erlang:fun_info(Fun, name), + {M,F}; + BS when bit_size(BS) rem 8 =/= 0 -> + Bits = bit_size(BS) rem 8, + {<<BS/bitstring, 0:(8-Bits)>>, Bits} + end, + Tuple = binary_to_term_stress(erts_debug:get_internal_state({term_to_binary_tuple_fallbacks,Term})). %% Test non-standard encodings never generated by term_to_binary/1 %% but recognized by binary_to_term/1. @@ -1442,13 +1472,13 @@ error_after_yield(Type, M, F, AN, AFun, TrapFunc) -> apply(M, F, A), exit({unexpected_success, {M, F, A}}) catch - error:Type -> + error:Type:Stk -> erlang:trace(self(),false,[running,{tracer,Tracer}]), %% We threw the exception from the native %% function we trapped to, but we want %% the BIF that originally was called %% to appear in the stack trace. - [{M, F, A, _} | _] = erlang:get_stacktrace() + [{M, F, A, _} | _] = Stk end end), receive diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl index 1251d644ae..d19f7f81ad 100644 --- a/erts/emulator/test/call_trace_SUITE.erl +++ b/erts/emulator/test/call_trace_SUITE.erl @@ -1116,8 +1116,8 @@ get_deep_4_loc(Arg) -> deep_4(Arg), ct:fail(should_not_return_to_here) catch - _:_ -> - [{?MODULE,deep_4,1,Loc0}|_] = erlang:get_stacktrace(), + _:_:Stk -> + [{?MODULE,deep_4,1,Loc0}|_] = Stk, Loc0 end. diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 77321aa50f..661a2ee6c9 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2017. All Rights Reserved. +%% Copyright Ericsson AB 1999-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -25,8 +25,10 @@ multi_proc_purge/1, t_check_old_code/1, external_fun/1,get_chunk/1,module_md5/1, constant_pools/1,constant_refc_binaries/1, + fake_literals/1, false_dependency/1,coverage/1,fun_confusion/1, - t_copy_literals/1, t_copy_literals_frags/1]). + t_copy_literals/1, t_copy_literals_frags/1, + erl_544/1]). -define(line_trace, 1). -include_lib("common_test/include/ct.hrl"). @@ -38,8 +40,10 @@ all() -> call_purged_fun_code_reload, call_purged_fun_code_there, multi_proc_purge, t_check_old_code, external_fun, get_chunk, module_md5, - constant_pools, constant_refc_binaries, false_dependency, - coverage, fun_confusion, t_copy_literals, t_copy_literals_frags]. + constant_pools, constant_refc_binaries, fake_literals, + false_dependency, + coverage, fun_confusion, t_copy_literals, t_copy_literals_frags, + erl_544]. init_per_suite(Config) -> erts_debug:set_internal_state(available_internal_state, true), @@ -554,6 +558,62 @@ wait_for_memory_deallocations() -> wait_for_memory_deallocations() end. +fake_literals(_Config) -> + Mod = fake__literals__module, + try + do_fake_literals(Mod) + after + _ = code:purge(Mod), + _ = code:delete(Mod), + _ = code:purge(Mod), + _ = code:delete(Mod) + end, + ok. + +do_fake_literals(Mod) -> + Tid = ets:new(test, []), + ExtTerms = get_external_terms(), + Term0 = {self(),make_ref(),Tid,fun() -> ok end,ExtTerms}, + Terms = [begin + make_literal_module(Mod, Term0), + Mod:term() + end || _ <- lists:seq(1, 10)], + verify_lit_terms(Terms, Term0), + true = ets:delete(Tid), + ok. + +make_literal_module(Mod, Term) -> + Exp = [{term,0}], + Attr = [], + Fs = [{function,term,0,2, + [{label,1}, + {line,[]}, + {func_info,{atom,Mod},{atom,term},0}, + {label,2}, + {move,{literal,Term},{x,0}}, + return]}], + Asm = {Mod,Exp,Attr,Fs,2}, + {ok,Mod,Beam} = compile:forms(Asm, [from_asm,binary,report]), + code:load_binary(Mod, atom_to_list(Mod), Beam). + +verify_lit_terms([H|T], Term) -> + case H =:= Term of + true -> + verify_lit_terms(T, Term); + false -> + error({bad_term,H}) + end; +verify_lit_terms([], _) -> + ok. + +get_external_terms() -> + {ok,Node} = test_server:start_node(?FUNCTION_NAME, slave, []), + Ref = rpc:call(Node, erlang, make_ref, []), + Ports = rpc:call(Node, erlang, ports, []), + Pid = rpc:call(Node, erlang, self, []), + _ = test_server:stop_node(Node), + {Ref,hd(Ports),Pid}. + %% OTP-7559: c_p->cp could contain garbage and create a false dependency %% to a module in a process. (Thanks to Richard Carlsson.) false_dependency(Config) when is_list(Config) -> @@ -860,6 +920,53 @@ reloader(Mod,Code,Time) -> reloader(Mod,Code,Time) end. +erl_544(Config) when is_list(Config) -> + case file:native_name_encoding() of + utf8 -> + {ok, CWD} = file:get_cwd(), + try + Mod = erl_544, + FileName = atom_to_list(Mod) ++ ".erl", + Priv = proplists:get_value(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + {ok, FileContent} = file:read_file(filename:join(Data, + FileName)), + Dir = filename:join(Priv, [16#2620,16#2620,16#2620]), + File = filename:join(Dir, FileName), + io:format("~ts~n", [File]), + ok = file:make_dir(Dir), + ok = file:set_cwd(Dir), + ok = file:write_file(File, [FileContent]), + {ok, Mod} = compile:file(File), + Res1 = (catch Mod:err()), + io:format("~p~n", [Res1]), + {'EXIT', {err, [{Mod, err, 0, Info1}|_]}} = Res1, + File = proplists:get_value(file, Info1), + Me = self(), + Go = make_ref(), + Tester = spawn_link(fun () -> + Mod:wait(Me, Go), + Mod:err() + end), + receive Go -> ok end, + Res2 = process_info(Tester, current_stacktrace), + io:format("~p~n", [Res2]), + {current_stacktrace, Stack} = Res2, + [{Mod, wait, 2, Info2}|_] = Stack, + File = proplists:get_value(file, Info2), + StackFun = fun(_, _, _) -> false end, + FormatFun = fun (Term, _) -> io_lib:format("~tp", [Term]) end, + Formated = + lib:format_stacktrace(1, Stack, StackFun, FormatFun), + true = is_list(Formated), + ok + after + ok = file:set_cwd(CWD) + end, + ok; + _Enc -> + {skipped, "Only run when native file name encoding is utf8"} + end. %% Utilities. diff --git a/erts/emulator/test/code_SUITE_data/erl_544.erl b/erts/emulator/test/code_SUITE_data/erl_544.erl new file mode 100644 index 0000000000..c93f3ef5bc --- /dev/null +++ b/erts/emulator/test/code_SUITE_data/erl_544.erl @@ -0,0 +1,35 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(erl_544). + +-export([err/0, wait/2]). + +err() -> + erlang:error(err). + +wait(Pid, Msg) -> + erlang:yield(), + Pid ! Msg, + receive + after infinity -> + ok + end, + err(). diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl index 031b05790d..4998fc08be 100644 --- a/erts/emulator/test/ddll_SUITE.erl +++ b/erts/emulator/test/ddll_SUITE.erl @@ -775,7 +775,7 @@ errors(Config) when is_list(Config) -> {error, bad_driver_name} = erl_ddll:load_driver(Path, wrongname_drv), %% We assume that there is a statically linked driver named "ddll": - {error, linked_in_driver} = erl_ddll:unload_driver(efile), + {error, linked_in_driver} = erl_ddll:unload_driver(ram_file_drv), {error, not_loaded} = erl_ddll:unload_driver("__pucko_driver__"), case os:type() of diff --git a/erts/emulator/test/dirty_bif_SUITE.erl b/erts/emulator/test/dirty_bif_SUITE.erl index 981ec4d48d..46eb0cba58 100644 --- a/erts/emulator/test/dirty_bif_SUITE.erl +++ b/erts/emulator/test/dirty_bif_SUITE.erl @@ -108,90 +108,80 @@ dirty_bif_exception(Config) when is_list(Config) -> erts_debug:dirty_cpu(error, Error), ct:fail(expected_exception) catch - error:ErrorType -> - [{erts_debug,dirty_cpu,[error, Error],_}|_] - = erlang:get_stacktrace(), + error:ErrorType:Stk1 -> + [{erts_debug,dirty_cpu,[error, Error],_}|_] = Stk1, ok end, try apply(erts_debug,dirty_cpu,[error, Error]), ct:fail(expected_exception) catch - error:ErrorType -> - [{erts_debug,dirty_cpu,[error, Error],_}|_] - = erlang:get_stacktrace(), + error:ErrorType:Stk2 -> + [{erts_debug,dirty_cpu,[error, Error],_}|_] = Stk2, ok end, try erts_debug:dirty_io(error, Error), ct:fail(expected_exception) catch - error:ErrorType -> - [{erts_debug,dirty_io,[error, Error],_}|_] - = erlang:get_stacktrace(), + error:ErrorType:Stk3 -> + [{erts_debug,dirty_io,[error, Error],_}|_] = Stk3, ok end, try apply(erts_debug,dirty_io,[error, Error]), ct:fail(expected_exception) catch - error:ErrorType -> - [{erts_debug,dirty_io,[error, Error],_}|_] - = erlang:get_stacktrace(), + error:ErrorType:Stk4 -> + [{erts_debug,dirty_io,[error, Error],_}|_] = Stk4, ok end, try erts_debug:dirty(normal, error, Error), ct:fail(expected_exception) catch - error:ErrorType -> - [{erts_debug,dirty,[normal, error, Error],_}|_] - = erlang:get_stacktrace(), + error:ErrorType:Stk5 -> + [{erts_debug,dirty,[normal, error, Error],_}|_] = Stk5, ok end, try apply(erts_debug,dirty,[normal, error, Error]), ct:fail(expected_exception) catch - error:ErrorType -> - [{erts_debug,dirty,[normal, error, Error],_}|_] - = erlang:get_stacktrace(), + error:ErrorType:Stk6 -> + [{erts_debug,dirty,[normal, error, Error],_}|_] = Stk6, ok end, try erts_debug:dirty(dirty_cpu, error, Error), ct:fail(expected_exception) catch - error:ErrorType -> - [{erts_debug,dirty,[dirty_cpu, error, Error],_}|_] - = erlang:get_stacktrace(), + error:ErrorType:Stk7 -> + [{erts_debug,dirty,[dirty_cpu, error, Error],_}|_] = Stk7, ok end, try apply(erts_debug,dirty,[dirty_cpu, error, Error]), ct:fail(expected_exception) catch - error:ErrorType -> - [{erts_debug,dirty,[dirty_cpu, error, Error],_}|_] - = erlang:get_stacktrace(), + error:ErrorType:Stk8 -> + [{erts_debug,dirty,[dirty_cpu, error, Error],_}|_] = Stk8, ok end, try erts_debug:dirty(dirty_io, error, Error), ct:fail(expected_exception) catch - error:ErrorType -> - [{erts_debug,dirty,[dirty_io, error, Error],_}|_] - = erlang:get_stacktrace(), + error:ErrorType:Stk9 -> + [{erts_debug,dirty,[dirty_io, error, Error],_}|_] = Stk9, ok end, try apply(erts_debug,dirty,[dirty_io, error, Error]), ct:fail(expected_exception) catch - error:ErrorType -> - [{erts_debug,dirty,[dirty_io, error, Error],_}|_] - = erlang:get_stacktrace(), + error:ErrorType:Stk10 -> + [{erts_debug,dirty,[dirty_io, error, Error],_}|_] = Stk10, ok end end, @@ -204,25 +194,22 @@ dirty_bif_multischedule_exception(Config) when is_list(Config) -> try erts_debug:dirty_cpu(reschedule,1001) catch - error:badarg -> - [{erts_debug,dirty_cpu,[reschedule, 1001],_}|_] - = erlang:get_stacktrace(), + error:badarg:Stk1 -> + [{erts_debug,dirty_cpu,[reschedule, 1001],_}|_] = Stk1, ok end, try erts_debug:dirty_io(reschedule,1001) catch - error:badarg -> - [{erts_debug,dirty_io,[reschedule, 1001],_}|_] - = erlang:get_stacktrace(), + error:badarg:Stk2 -> + [{erts_debug,dirty_io,[reschedule, 1001],_}|_] = Stk2, ok end, try erts_debug:dirty(normal,reschedule,1001) catch - error:badarg -> - [{erts_debug,dirty,[normal,reschedule,1001],_}|_] - = erlang:get_stacktrace(), + error:badarg:Stk3 -> + [{erts_debug,dirty,[normal,reschedule,1001],_}|_] = Stk3, ok end. @@ -230,6 +217,11 @@ dirty_scheduler_exit(Config) when is_list(Config) -> {ok, Node} = start_node(Config, "+SDio 1"), [ok] = mcall(Node, [fun() -> + %% Perform a dry run to ensure that all required code + %% is loaded. Otherwise the test will fail since code + %% loading is done through dirty IO and it won't make + %% any progress during this test. + _DryRun = test_dirty_scheduler_exit(), Start = erlang:monotonic_time(millisecond), ok = test_dirty_scheduler_exit(), End = erlang:monotonic_time(millisecond), @@ -246,23 +238,22 @@ test_dse(0,Pids) -> timer:sleep(100), kill_dse(Pids,[]); test_dse(N,Pids) -> - Pid = spawn_link(fun () -> erts_debug:dirty_io(wait, 5000) end), + Pid = spawn_link(fun () -> erts_debug:dirty_io(wait, 1000) end), test_dse(N-1,[Pid|Pids]). kill_dse([],Killed) -> - wait_dse(Killed); + wait_dse(Killed, ok); kill_dse([Pid|Pids],AlreadyKilled) -> exit(Pid,kill), kill_dse(Pids,[Pid|AlreadyKilled]). -wait_dse([]) -> - ok; -wait_dse([Pid|Pids]) -> +wait_dse([], Result) -> + Result; +wait_dse([Pid|Pids], Result) -> receive - {'EXIT',Pid,Reason} -> - killed = Reason - end, - wait_dse(Pids). + {'EXIT', Pid, killed} -> wait_dse(Pids, Result); + {'EXIT', Pid, _Other} -> wait_dse(Pids, failed) + end. dirty_call_while_terminated(Config) when is_list(Config) -> Me = self(), diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl index 13806fd5c4..93d0ac392c 100644 --- a/erts/emulator/test/dirty_nif_SUITE.erl +++ b/erts/emulator/test/dirty_nif_SUITE.erl @@ -109,9 +109,8 @@ dirty_nif_exception(Config) when is_list(Config) -> call_dirty_nif_exception(1), ct:fail(expected_badarg) catch - error:badarg -> - [{?MODULE,call_dirty_nif_exception,[1],_}|_] = - erlang:get_stacktrace(), + error:badarg:Stk1 -> + [{?MODULE,call_dirty_nif_exception,[1],_}|_] = Stk1, ok end, try @@ -121,9 +120,8 @@ dirty_nif_exception(Config) when is_list(Config) -> call_dirty_nif_exception(0), ct:fail(expected_badarg) catch - error:badarg -> - [{?MODULE,call_dirty_nif_exception,[0],_}|_] = - erlang:get_stacktrace(), + error:badarg:Stk2 -> + [{?MODULE,call_dirty_nif_exception,[0],_}|_] = Stk2, ok end, %% this checks that a dirty NIF can raise various terms as @@ -138,8 +136,8 @@ nif_raise_exceptions(NifFunc) -> erlang:apply(?MODULE,NifFunc,[Term]), ct:fail({expected,Term}) catch - error:Term -> - [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(), + error:Term:Stk -> + [{?MODULE,NifFunc,[Term],_}|_] = Stk, ok end end, ok, ExcTerms). @@ -151,6 +149,11 @@ dirty_scheduler_exit(Config) when is_list(Config) -> [ok] = mcall(Node, [fun() -> ok = erlang:load_nif(NifLib, []), + %% Perform a dry run to ensure that all required code + %% is loaded. Otherwise the test will fail since code + %% loading is done through dirty IO and it won't make + %% any progress during this test. + _DryRun = test_dirty_scheduler_exit(), Start = erlang:monotonic_time(millisecond), ok = test_dirty_scheduler_exit(), End = erlang:monotonic_time(millisecond), @@ -171,19 +174,18 @@ test_dse(N,Pids) -> test_dse(N-1,[Pid|Pids]). kill_dse([],Killed) -> - wait_dse(Killed); + wait_dse(Killed, ok); kill_dse([Pid|Pids],AlreadyKilled) -> exit(Pid,kill), kill_dse(Pids,[Pid|AlreadyKilled]). -wait_dse([]) -> - ok; -wait_dse([Pid|Pids]) -> +wait_dse([], Result) -> + Result; +wait_dse([Pid|Pids], Result) -> receive - {'EXIT',Pid,Reason} -> - killed = Reason - end, - wait_dse(Pids). + {'EXIT', Pid, killed} -> wait_dse(Pids, Result); + {'EXIT', Pid, _Other} -> wait_dse(Pids, failed) + end. dirty_call_while_terminated(Config) when is_list(Config) -> Me = self(), @@ -287,9 +289,9 @@ access_dirty_heap(Dirty, RGL, N, R) -> %% dirty NIF where the main lock is needed for that access do not get %% blocked. Each test passes its pid to dirty_sleeper, which sends a %% 'ready' message when it's running on a dirty scheduler and just before -%% it starts a 6 second sleep. When it receives the message, it verifies +%% it starts a 2 second sleep. When it receives the message, it verifies %% that access to the dirty process is as it expects. After the dirty -%% process finishes its 6 second sleep but before it returns from the dirty +%% process finishes its 2 second sleep but before it returns from the dirty %% scheduler, it sends a 'done' message. If the tester already received %% that message, the test fails because it means attempting to access the %% dirty process waited for that process to return to a regular scheduler, @@ -353,7 +355,7 @@ dirty_process_trace(Config) when is_list(Config) -> error(missing_trace_return_message) end after - 6500 -> + 2500 -> error(missing_done_message) end, ok @@ -380,7 +382,7 @@ code_purge(Config) when is_list(Config) -> Start = erlang:monotonic_time(), {Pid1, Mon1} = spawn_monitor(fun () -> dirty_code_test:func(fun () -> - %% Sleep for 6 seconds + %% Sleep for 2 seconds %% in dirty nif... dirty_sleeper() end) @@ -388,7 +390,7 @@ code_purge(Config) when is_list(Config) -> {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin), {Pid2, Mon2} = spawn_monitor(fun () -> dirty_code_test:func(fun () -> - %% Sleep for 6 seconds + %% Sleep for 2 seconds %% in dirty nif... dirty_sleeper() end) @@ -490,7 +492,7 @@ test_dirty_process_access(Start, Test, Finish) -> ok end after - 3000 -> + 1000 -> error(timeout) end, ok = Finish(NifPid). diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c index 0321b9898f..2a8b999307 100644 --- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -217,9 +217,9 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) } #ifdef __WIN32__ - Sleep(6000); + Sleep(2000); #else - sleep(6); + sleep(2); #endif if (argc == 1) { diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 2d0ae9c83e..e40d346e10 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -35,13 +35,18 @@ -include_lib("common_test/include/ct.hrl"). +%-define(Line, erlang:display({line,?LINE}),). +-define(Line,). + -export([all/0, suite/0, groups/0, ping/1, bulk_send_small/1, + group_leader/1, + optimistic_dflags/1, bulk_send_big/1, bulk_send_bigbig/1, local_send_small/1, local_send_big/1, local_send_legal/1, link_to_busy/1, exit_to_busy/1, lost_exit/1, link_to_dead/1, link_to_dead_new_node/1, - applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1, + ref_port_roundtrip/1, nil_roundtrip/1, trap_bif_1/1, trap_bif_2/1, trap_bif_3/1, stop_dist/1, dist_auto_connect_never/1, dist_auto_connect_once/1, @@ -61,6 +66,8 @@ %% Internal exports. -export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0, + group_leader_1/1, + optimistic_dflags_echo/0, optimistic_dflags_sender/1, roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1, dist_parallel_sender/3, dist_parallel_receiver/0, dist_evil_parallel_receiver/0]). @@ -74,8 +81,10 @@ suite() -> all() -> [ping, {group, bulk_send}, {group, local_send}, + group_leader, + optimistic_dflags, link_to_busy, exit_to_busy, lost_exit, link_to_dead, - link_to_dead_new_node, applied_monitor_node, + link_to_dead_new_node, ref_port_roundtrip, nil_roundtrip, stop_dist, {group, trap_bif}, {group, dist_auto_connect}, dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip, @@ -124,6 +133,96 @@ ping(Config) when is_list(Config) -> ok. +%% Test erlang:group_leader(_, ExternalPid), i.e. DOP_GROUP_LEADER +group_leader(Config) when is_list(Config) -> + ?Line Sock = start_relay_node(group_leader_1, []), + ?Line Sock2 = start_relay_node(group_leader_2, []), + try + ?Line Node2 = inet_rpc_nodename(Sock2), + ?Line {ok, ok} = do_inet_rpc(Sock, ?MODULE, group_leader_1, [Node2]) + after + ?Line stop_relay_node(Sock), + ?Line stop_relay_node(Sock2) + end, + ok. + +group_leader_1(Node2) -> + ?Line ExtPid = spawn(Node2, fun F() -> + receive {From, group_leader} -> + From ! {self(), group_leader, group_leader()} + end, + F() + end), + ?Line GL1 = self(), + ?Line group_leader(GL1, ExtPid), + ?Line ExtPid ! {self(), group_leader}, + ?Line {ExtPid, group_leader, GL1} = receive_one(), + + %% Kill connection and repeat test when group_leader/2 triggers auto-connect + ?Line net_kernel:monitor_nodes(true), + ?Line net_kernel:disconnect(Node2), + ?Line {nodedown, Node2} = receive_one(), + ?Line GL2 = spawn(fun() -> dummy end), + ?Line group_leader(GL2, ExtPid), + ?Line {nodeup, Node2} = receive_one(), + ?Line ExtPid ! {self(), group_leader}, + ?Line {ExtPid, group_leader, GL2} = receive_one(), + ok. + +%% Test optimistic distribution flags toward pending connections (DFLAG_DIST_HOPEFULLY) +optimistic_dflags(Config) when is_list(Config) -> + ?Line Sender = start_relay_node(optimistic_dflags_sender, []), + ?Line Echo = start_relay_node(optimistic_dflags_echo, []), + try + ?Line {ok, ok} = do_inet_rpc(Echo, ?MODULE, optimistic_dflags_echo, []), + + ?Line EchoNode = inet_rpc_nodename(Echo), + ?Line {ok, ok} = do_inet_rpc(Sender, ?MODULE, optimistic_dflags_sender, [EchoNode]) + after + ?Line stop_relay_node(Sender), + ?Line stop_relay_node(Echo) + end, + ok. + +optimistic_dflags_echo() -> + P = spawn(fun F() -> + receive {From, Term} -> + From ! {self(), Term} + end, + F() + end), + register(optimistic_dflags_echo, P), + optimistic_dflags_echo ! {self(), hello}, + {P, hello} = receive_one(), + ok. + +optimistic_dflags_sender(EchoNode) -> + ?Line net_kernel:monitor_nodes(true), + + optimistic_dflags_do(EchoNode, <<1:1>>), + optimistic_dflags_do(EchoNode, fun lists:map/2), + ok. + +optimistic_dflags_do(EchoNode, Term) -> + ?Line {optimistic_dflags_echo, EchoNode} ! {self(), Term}, + ?Line {nodeup, EchoNode} = receive_one(), + ?Line {EchoPid, Term} = receive_one(), + %% repeat with pid destination + ?Line net_kernel:disconnect(EchoNode), + ?Line {nodedown, EchoNode} = receive_one(), + ?Line EchoPid ! {self(), Term}, + ?Line {nodeup, EchoNode} = receive_one(), + ?Line {EchoPid, Term} = receive_one(), + + ?Line net_kernel:disconnect(EchoNode), + ?Line {nodedown, EchoNode} = receive_one(), + ok. + + +receive_one() -> + receive M -> M after 1000 -> timeout end. + + bulk_send_small(Config) when is_list(Config) -> bulk_send(64, 32). @@ -639,31 +738,11 @@ link_to_dead_new_node(Config) when is_list(Config) -> end, ok. -%% Test that monitor_node/2 works when applied. -applied_monitor_node(Config) when is_list(Config) -> - NonExisting = list_to_atom("__non_existing__@" ++ hostname()), - - %% Tail-recursive call to apply (since the node is non-existing, - %% there will be a trap). - - true = tail_apply(erlang, monitor_node, [NonExisting, true]), - [{nodedown, NonExisting}] = test_server:messages_get(), - - %% Ordinary call (with trap). - - true = apply(erlang, monitor_node, [NonExisting, true]), - [{nodedown, NonExisting}] = test_server:messages_get(), - - ok. - -tail_apply(M, F, A) -> - apply(M, F, A). - %% Test that sending a port or reference to another node and back again %% doesn't correct them in any way. ref_port_roundtrip(Config) when is_list(Config) -> process_flag(trap_exit, true), - Port = open_port({spawn, efile}, []), + Port = make_port(), Ref = make_ref(), {ok, Node} = start_node(ref_port_roundtrip), net_adm:ping(Node), @@ -684,6 +763,9 @@ ref_port_roundtrip(Config) when is_list(Config) -> end, ok. +make_port() -> + hd(erlang:ports()). + roundtrip(Term) -> exit(Term). @@ -1158,8 +1240,6 @@ contended_atom_cache_entry_test(Config, Type) -> spawn_link( SNode, fun () -> - erts_debug:set_internal_state(available_internal_state, - true), Master = self(), CIX = get_cix(), TestAtoms = case Type of @@ -1244,7 +1324,7 @@ get_cix(CIX) when is_integer(CIX), CIX < 0 -> get_cix(CIX) when is_integer(CIX) -> get_cix(CIX, unwanted_cixs(), - erts_debug:get_internal_state(max_atom_out_cache_index)). + get_internal_state(max_atom_out_cache_index)). get_cix(CIX, Unwanted, MaxCIX) when CIX > MaxCIX -> get_cix(0, Unwanted, MaxCIX); @@ -1256,8 +1336,8 @@ get_cix(CIX, Unwanted, MaxCIX) -> unwanted_cixs() -> lists:map(fun (Node) -> - erts_debug:get_internal_state({atom_out_cache_index, - Node}) + get_internal_state({atom_out_cache_index, + Node}) end, nodes()). @@ -1266,7 +1346,7 @@ get_conflicting_atoms(_CIX, 0) -> []; get_conflicting_atoms(CIX, N) -> Atom = list_to_atom("atom" ++ integer_to_list(erlang:unique_integer([positive]))), - case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of + case get_internal_state({atom_out_cache_index, Atom}) of CIX -> [Atom|get_conflicting_atoms(CIX, N-1)]; _ -> @@ -1277,7 +1357,7 @@ get_conflicting_unicode_atoms(_CIX, 0) -> []; get_conflicting_unicode_atoms(CIX, N) -> Atom = string_to_atom([16#1f608] ++ "atom" ++ integer_to_list(erlang:unique_integer([positive]))), - case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of + case get_internal_state({atom_out_cache_index, Atom}) of CIX -> [Atom|get_conflicting_unicode_atoms(CIX, N-1)]; _ -> @@ -1365,81 +1445,59 @@ bad_dist_structure(Config) when is_list(Config) -> start_monitor(Offender,P), P ! one, send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_monitor(Offender,P), send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal,normal},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_link(Offender,P), send_bad_structure(Offender, P,{?DOP_LINK},0), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_link(Offender,P), send_bad_structure(Offender, P,{?DOP_UNLINK,'replace'},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_link(Offender,P), send_bad_structure(Offender, P,{?DOP_UNLINK,'replace',make_ref()},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_link(Offender,P), send_bad_structure(Offender, P,{?DOP_UNLINK,make_ref(),P},0), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_link(Offender,P), send_bad_structure(Offender, P,{?DOP_UNLINK,normal,normal},0), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_monitor(Offender,P), send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_monitor(Offender,P), send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P,normal},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_monitor(Offender,P), send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + start_monitor(Offender,P), send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P,normal},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), + send_bad_structure(Offender, P,{?DOP_EXIT,'replace',P},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_EXIT,make_ref(),normal,normal},0), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_EXIT_TT,'replace',token,P},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_EXIT_TT,make_ref(),token,normal,normal},0), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_EXIT2,'replace',P},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_EXIT2,make_ref(),normal,normal},0), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_EXIT2_TT,'replace',token,P},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_EXIT2_TT,make_ref(),token,normal,normal},0), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace'},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace','atomic'},2), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace',P},0), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name},2,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name,token},0,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace',''},2,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',P},0,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name},0,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name,{token}},2,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_SEND_TT,'',P},0,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_SEND_TT,'',name,token},0,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_SEND,''},0,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_SEND,'',name},0,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), send_bad_structure(Offender, P,{?DOP_SEND,'',P,{token}},0,{message}), - pong = rpc:call(Victim, net_adm, ping, [Offender]), P ! two, P ! check_msgs, receive @@ -1685,13 +1743,16 @@ bad_dist_ext_size(Config) when is_list(Config) -> start_node_monitors([Offender,Victim]), Parent = self(), - P = spawn_link(Victim, + P = spawn_opt(Victim, fun () -> Parent ! {self(), started}, receive check_msgs -> ok end, %% DID CRASH HERE bad_dist_ext_check_msgs([one]), Parent ! {self(), messages_checked} - end), + end, + [link, + %% on_heap to force total_heap_size to inspect msg queue + {message_queue_data, on_heap}]), receive {P, started} -> ok end, P ! one, @@ -1714,6 +1775,7 @@ bad_dist_ext_size(Config) when is_list(Config) -> verify_still_up(Offender, Victim), + %% Let process_info(P, total_heap_size) find bad msg and disconnect rpc:call(Victim, erlang, process_info, [P, total_heap_size]), verify_down(Offender, connection_closed, Victim, killed), @@ -1795,10 +1857,11 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf) -> send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) -> Parent = self(), Done = make_ref(), - spawn(Offender, + spawn_link(Offender, fun () -> Node = node(Victim), pong = net_adm:ping(Node), + erlang:monitor_node(Node, true), DCtrl = dctrl(Node), Bad1 = case WhereToPutSelf of 0 -> @@ -1812,7 +1875,16 @@ send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) -> [] -> []; _Other -> [dmsg_ext(PayLoad)] end, + + receive {nodedown, Node} -> exit("premature nodedown") + after 10 -> ok + end, + dctrl_send(DCtrl, DData), + + receive {nodedown, Node} -> ok + after 5000 -> exit("missing nodedown") + end, Parent ! {DData,Done} end), receive @@ -1893,11 +1965,26 @@ send_bad_dhdr(BadNode, ToNode) when is_atom(BadNode), is_atom(ToNode) -> receive Done -> ok end. dctrl(Node) when is_atom(Node) -> - case catch erts_debug:get_internal_state(available_internal_state) of - true -> true; - _ -> erts_debug:set_internal_state(available_internal_state, true) - end, - erts_debug:get_internal_state({dist_ctrl, Node}). + get_internal_state({dist_ctrl, Node}). + +get_internal_state(Op) -> + try erts_debug:get_internal_state(Op) of + R -> R + catch + error:undef -> + erts_debug:set_internal_state(available_internal_state, true), + erts_debug:get_internal_state(Op) + end. + +set_internal_state(Op, Val) -> + try erts_debug:set_internal_state(Op, Val) of + R -> R + catch + error:undef -> + erts_debug:set_internal_state(available_internal_state, true), + erts_debug:set_internal_state(Op, Val) + end. + dmsg_hdr() -> [131, % Version Magic @@ -2040,11 +2127,9 @@ freeze_node(Node, MS) -> Freezer = self(), spawn_link(Node, fun () -> - erts_debug:set_internal_state(available_internal_state, - true), dctrl_dop_send(Freezer, DoingIt), receive after Own -> ok end, - erts_debug:set_internal_state(block, MS+Own) + set_internal_state(block, MS+Own) end), receive DoingIt -> ok end, receive after Own -> ok end. @@ -2248,8 +2333,7 @@ forever(Fun) -> forever(Fun). abort(Why) -> - erts_debug:set_internal_state(available_internal_state, true), - erts_debug:set_internal_state(abort, Why). + set_internal_state(abort, Why). start_busy_dist_port_tracer() -> diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 475e03087a..294c42780d 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -80,6 +80,7 @@ async_blast/1, thr_msg_blast/1, consume_timeslice/1, + env/1, z_test/1]). -export([bin_prefix/2]). @@ -166,6 +167,7 @@ all() -> %% Keep a_test first and z_test last... async_blast, thr_msg_blast, consume_timeslice, + env, z_test]. groups() -> @@ -1768,7 +1770,10 @@ smp_select0(Config) -> smp_select_loop(_, 0) -> ok; smp_select_loop(Port, N) -> - "ok" = erlang:port_control(Port, ?CHKIO_SMP_SELECT, []), + case erlang:port_control(Port, ?CHKIO_SMP_SELECT, []) of + "yield" -> erlang:yield(); + "ok" -> ok + end, receive stop -> io:format("Worker ~p stopped with ~p laps left\n",[self(), N]), @@ -2360,6 +2365,51 @@ count_proc_sched(Ps, PNs) -> PNs end. +%% +%% Tests whether erl_drv_putenv reflects in os:getenv and vice versa. +%% +env(Config) when is_list(Config) -> + ok = load_driver(proplists:get_value(data_dir, Config), env_drv), + Port = open_port({spawn_driver, env_drv}, []), + true = is_port(Port), + + Keys = ["env_drv_a_key", "env_drv_b_key", "env_drv_c_key"], + Values = ["a_value", "b_value", "c_value"], + + [env_put_test(Port, Key, Value) || Key <- Keys, Value <- Values], + [env_get_test(Port, Key, Value) || Key <- Keys, Value <- Values], + [env_oversize_test(Port, Key) || Key <- Keys], + [env_notfound_test(Port, Key) || Key <- Keys], + + true = port_close(Port), + erl_ddll:unload_driver(env_drv), + ok. + +env_control(Port, Command, Key, Value) -> + KeyBin = list_to_binary(Key), + ValueBin = list_to_binary(Value), + Header = <<(byte_size(KeyBin)), (byte_size(ValueBin))>>, + Payload = <<KeyBin/binary, ValueBin/binary>>, + port_control(Port, Command, <<Header/binary, Payload/binary>>). + +env_put_test(Port, Key, Value) -> + os:unsetenv(Key), + [0] = env_control(Port, 0, Key, Value), + Value = os:getenv(Key). + +env_get_test(Port, Key, ExpectedValue) -> + true = os:putenv(Key, ExpectedValue), + [0] = env_control(Port, 1, Key, ExpectedValue). + +env_oversize_test(Port, Key) -> + os:putenv(Key, [$A || _ <- lists:seq(1, 1024)]), + [127] = env_control(Port, 1, Key, ""). + +env_notfound_test(Port, Key) -> + true = os:unsetenv(Key), + [255] = env_control(Port, 1, Key, ""). + + a_test(Config) when is_list(Config) -> rpc(Config, fun check_io_debug/0). @@ -2614,8 +2664,8 @@ rpc(Config, Fun) -> Result = try Fun() of Res -> Res - catch E:R -> - {'EXIT',E,R,erlang:get_stacktrace()} + catch E:R:Stk -> + {'EXIT',E,R,Stk} end, Self ! {Ref, Result} end), diff --git a/erts/emulator/test/driver_SUITE_data/Makefile.src b/erts/emulator/test/driver_SUITE_data/Makefile.src index 1fedd72200..bcabaa689d 100644 --- a/erts/emulator/test/driver_SUITE_data/Makefile.src +++ b/erts/emulator/test/driver_SUITE_data/Makefile.src @@ -16,7 +16,8 @@ MISC_DRVS = outputv_drv@dll@ \ thr_free_drv@dll@ \ async_blast_drv@dll@ \ thr_msg_blast_drv@dll@ \ - consume_timeslice_drv@dll@ + consume_timeslice_drv@dll@ \ + env_drv@dll@ SYS_INFO_DRVS = sys_info_base_drv@dll@ \ sys_info_prev_drv@dll@ \ diff --git a/erts/emulator/test/driver_SUITE_data/chkio_drv.c b/erts/emulator/test/driver_SUITE_data/chkio_drv.c index d548c4b1dc..ee8f28e8b1 100644 --- a/erts/emulator/test/driver_SUITE_data/chkio_drv.c +++ b/erts/emulator/test/driver_SUITE_data/chkio_drv.c @@ -961,7 +961,6 @@ chkio_drv_control(ErlDrvData drv_data, break; } case CHKIO_SMP_SELECT: { - int rounds = 1; /*rand(); */ ChkioSmpSelect* pip = (ChkioSmpSelect*) cddp->test_data; if (pip == NULL) { erl_drv_mutex_lock(smp_pipes_mtx); @@ -978,7 +977,8 @@ chkio_drv_control(ErlDrvData drv_data, } erl_drv_mutex_unlock(smp_pipes_mtx); } - while (rounds--) { + res_str = NULL; + { int op = rand_r(&pip->rand_state); switch (pip->state) { case Closed: { @@ -988,7 +988,6 @@ chkio_drv_control(ErlDrvData drv_data, fcntl(fds[0], F_SETFL, flags|O_NONBLOCK) < 0) { driver_failure_posix(cddp->port, errno); - rounds = 0; break; } TRACEF(("%T: Created pipe [%d->%d]\n", cddp->id, fds[1], fds[0])); @@ -1075,7 +1074,9 @@ chkio_drv_control(ErlDrvData drv_data, pip->next_write++; } break; - case Waiting: + case Waiting: + res_str = "yield"; + res_len = -1; break; default: fprintf(stderr, "Strange state %d\n", pip->state); @@ -1091,9 +1092,11 @@ chkio_drv_control(ErlDrvData drv_data, else { cddp->test_data = pip; } - } - res_str = "ok"; - res_len = -1; + } + if (!res_str) { + res_str = "ok"; + res_len = -1; + } break; } case CHKIO_DRV_USE: diff --git a/erts/emulator/test/driver_SUITE_data/env_drv.c b/erts/emulator/test/driver_SUITE_data/env_drv.c new file mode 100644 index 0000000000..0e910eeb84 --- /dev/null +++ b/erts/emulator/test/driver_SUITE_data/env_drv.c @@ -0,0 +1,108 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2017. 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% + */ + +/* Tests whether erl_drv_putenv/erl_drv_getenv work correctly and reflect + * changes to os:putenv/getenv. */ + +#include <string.h> +#include <stdio.h> + +#include "erl_driver.h" + +static ErlDrvSSizeT env_drv_ctl(ErlDrvData drv_data, unsigned int cmd, + char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rsize); + +static ErlDrvEntry env_drv_entry = { + NULL /* init */, + NULL /* start */, + NULL /* stop */, + NULL /* output */, + NULL /* ready_input */, + NULL /* ready_output */, + "env_drv", + NULL /* finish */, + NULL /* handle */, + env_drv_ctl, + NULL /* timeout */, + NULL /* outputv*/, + NULL /* ready_async */, + NULL /* flush */, + NULL /* call*/, + NULL /* event */, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL /* handle2 */, + NULL /* handle_monitor */ +}; + +DRIVER_INIT(env_drv) { + return &env_drv_entry; +} + +static int test_putenv(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) { + char key[256], value[256]; + int key_len, value_len; + + key_len = buf[0]; + value_len = buf[1]; + + sprintf(key, "%.*s", key_len, &buf[2]); + sprintf(value, "%.*s", value_len, &buf[2 + key_len]); + + return erl_drv_putenv(key, value); +} + +static int test_getenv(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) { + char expected_value[256], stored_value[256], key[256]; + int expected_value_len, key_len; + size_t stored_value_len; + int res; + + key_len = buf[0]; + sprintf(key, "%.*s", key_len, &buf[2]); + + expected_value_len = buf[1]; + sprintf(expected_value, "%.*s", expected_value_len, &buf[2 + key_len]); + + stored_value_len = sizeof(stored_value); + res = erl_drv_getenv(key, stored_value, &stored_value_len); + + if(res == 0) { + return strcmp(stored_value, expected_value) != 0; + } else if(res == 1) { + return 127; + } + + return 255; +} + +static ErlDrvSSizeT env_drv_ctl(ErlDrvData drv_data, unsigned int cmd, + char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rsize) { + + if(cmd == 0) { + (**rbuf) = (char)test_putenv(drv_data, buf, len); + } else { + (**rbuf) = (char)test_getenv(drv_data, buf, len); + } + + return 1; +} diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index 08d5597d78..16d581a567 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -19,12 +19,9 @@ -module(efile_SUITE). -export([all/0, suite/0]). --export([async_dist/1, - iter_max_files/1, - proc_zero_sized_files/1 - ]). +-export([iter_max_files/1, proc_zero_sized_files/1]). --export([do_iter_max_files/2, do_async_dist/1]). +-export([do_iter_max_files/2]). -include_lib("common_test/include/ct.hrl"). -include_lib("stdlib/include/assert.hrl"). @@ -32,84 +29,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [iter_max_files, async_dist, proc_zero_sized_files]. - -do_async_dist(Dir) -> - X = 100, - AT = erlang:system_info(thread_pool_size), - Keys = file_keys(Dir,AT*X,[],[]), - Tab = ets:new(x,[ordered_set]), - [ ets:insert(Tab,{N,0}) || N <- lists:seq(0,AT-1) ], - [ ets:update_counter(Tab,(N rem AT),1) || N <- Keys ], - Res = [ V || {_,V} <- ets:tab2list(Tab) ], - ets:delete(Tab), - {Res, sdev(Res)/X}. - -sdev(List) -> - Len = length(List), - Mean = lists:sum(List)/Len, - math:sqrt(lists:sum([ (X - Mean) * (X - Mean) || X <- List ]) / Len). - -file_keys(_,0,FdList,FnList) -> - [ file:close(FD) || FD <- FdList ], - [ file:delete(FN) || FN <- FnList ], - []; -file_keys(Dir,Num,FdList,FnList) -> - Name = "dummy"++integer_to_list(Num), - FN = filename:join([Dir,Name]), - case file:open(FN,[write,raw]) of - {ok,FD} -> - {file_descriptor,prim_file,{Port,_}} = FD, - <<X:32/integer-big>> = - iolist_to_binary(erlang:port_control(Port,$K,[])), - [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])]; - {error,_} -> - % Try freeing up FD's if there are any - case FdList of - [] -> - exit({cannot_open_file,FN}); - _ -> - [ file:close(FD) || FD <- FdList ], - [ file:delete(F) || F <- FnList ], - file_keys(Dir,Num,[],[]) - end - end. - -%% Check that the distribution of files over async threads is fair -async_dist(Config) when is_list(Config) -> - DataDir = proplists:get_value(data_dir,Config), - Dir = filename:dirname(code:which(?MODULE)), - AsyncSizes = [7,10,100,255,256,64,63,65], - Max = 0.5, - - lists:foreach(fun(Size) -> - {ok,Node} = - test_server:start_node - (test_iter_max_files,slave, - [{args, - "+A "++integer_to_list(Size)++ - " -pa " ++ Dir}]), - {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist, - [DataDir]), - test_server:stop_node(Node), - if - SD > Max -> - io:format("Bad async queue distribution for " - "~p async threads:~n" - " Standard deviation is ~p~n" - " Key distribution:~n ~lp~n", - [Size,SD,Distr]), - exit({bad_async_dist,Size,SD,Distr}); - true -> - io:format("OK async queue distribution for " - "~p async threads:~n" - " Standard deviation is ~p~n" - " Key distribution:~n ~lp~n", - [Size,SD,Distr]), - ok - end - end, AsyncSizes), - ok. + [iter_max_files, proc_zero_sized_files]. %% %% Open as many files as possible. Do this several times and check @@ -117,6 +37,12 @@ async_dist(Config) when is_list(Config) -> %% iter_max_files(Config) when is_list(Config) -> + case os:type() of + {win32, _} -> {skip, "Windows lacks a hard limit on file handles"}; + _ -> iter_max_files_1(Config) + end. + +iter_max_files_1(Config) -> DataDir = proplists:get_value(data_dir,Config), TestFile = filename:join(DataDir, "existing_file"), N = 10, diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index d8c5b663e3..a66ca7a57d 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -200,13 +200,16 @@ monitor_nodes(Config) when is_list(Config) -> monitor_node(A, true), check_monitor_node(self(), A, 1), check_monitor_node(self(), B, 3), + ok = receive {nodedown, C} -> ok after 1000 -> timeout end, + %%OTP-21: monitor_node(_,false) does not trigger auto-connect anymore + %% and therefore no nodedown if it fails. + %%ok = receive {nodedown, C} -> ok after 1000 -> timeout end, + ok = receive {nodedown, C} -> ok after 1000 -> timeout end, + ok = receive {nodedown, D} -> ok after 1000 -> timeout end, + ok = receive {nodedown, D} -> ok after 1000 -> timeout end, check_monitor_node(self(), C, 0), check_monitor_node(self(), D, 0), - receive {nodedown, C} -> ok end, - receive {nodedown, C} -> ok end, - receive {nodedown, C} -> ok end, - receive {nodedown, D} -> ok end, - receive {nodedown, D} -> ok end, + stop_node(A), receive {nodedown, A} -> ok end, check_monitor_node(self(), A, 0), diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index be9b63d534..da0292f385 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -395,12 +395,12 @@ raise(Conf) when is_list(Conf) -> try try foo({'div',{1,0}}) catch - error:badarith -> + error:badarith:A0 -> put(raise, A0 = erlang:get_stacktrace()), erlang:raise(error, badarith, A0) end catch - error:badarith -> + error:badarith:A1 -> A1 = erlang:get_stacktrace(), A1 = get(raise) end, diff --git a/erts/emulator/test/guard_SUITE.erl b/erts/emulator/test/guard_SUITE.erl index 1a93a9f5c2..f2c1595392 100644 --- a/erts/emulator/test/guard_SUITE.erl +++ b/erts/emulator/test/guard_SUITE.erl @@ -500,7 +500,7 @@ all_types() -> {atom, xxxx}, {ref, make_ref()}, {pid, self()}, - {port, open_port({spawn, efile}, [])}, + {port, make_port()}, {function, fun(_) -> "" end}, {function, fun erlang:abs/1}, {binary, list_to_binary([])}, @@ -551,4 +551,7 @@ type_test(bitstring, X) when is_bitstring(X) -> type_test(function, X) when is_function(X) -> function. +make_port() -> + hd(erlang:ports()). + id(I) -> I. diff --git a/erts/emulator/test/lcnt_SUITE.erl b/erts/emulator/test/lcnt_SUITE.erl index 504b9b54cf..4e52c2813c 100644 --- a/erts/emulator/test/lcnt_SUITE.erl +++ b/erts/emulator/test/lcnt_SUITE.erl @@ -28,14 +28,16 @@ init_per_testcase/2, end_per_testcase/2]). -export( - [toggle_lock_counting/1, error_on_invalid_category/1, preserve_locks/1]). + [toggle_lock_counting/1, error_on_invalid_category/1, preserve_locks/1, + registered_processes/1, registered_db_tables/1]). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {seconds, 10}}]. all() -> - [toggle_lock_counting, error_on_invalid_category, preserve_locks]. + [toggle_lock_counting, error_on_invalid_category, preserve_locks, + registered_processes, registered_db_tables]. init_per_suite(Config) -> case erlang:system_info(lock_counting) of @@ -154,3 +156,25 @@ preserve_locks(Config) when is_list(Config) -> error_on_invalid_category(Config) when is_list(Config) -> {error, badarg, q_invalid} = erts_debug:lcnt_control(mask, [q_invalid]), ok. + +registered_processes(Config) when is_list(Config) -> + %% There ought to be at least one registered process (init/code_server) + erts_debug:lcnt_control(mask, [process]), + [_, {locks, ProcLocks}] = erts_debug:lcnt_collect(), + true = lists:any( + fun + ({proc_main, RegName, _, _}) when is_atom(RegName) -> true; + (_Lock) -> false + end, ProcLocks), + ok. + +registered_db_tables(Config) when is_list(Config) -> + %% There ought to be at least one registered table (code) + erts_debug:lcnt_control(mask, [db]), + [_, {locks, DbLocks}] = erts_debug:lcnt_collect(), + true = lists:any( + fun + ({db_tab, RegName, _, _}) when is_atom(RegName) -> true; + (_Lock) -> false + end, DbLocks), + ok. diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 02f3c89318..c9e971af8a 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -52,7 +52,7 @@ t_bif_map_values/1, t_bif_map_to_list/1, t_bif_map_from_list/1, - t_bif_erts_internal_maps_to_list/1, + t_bif_map_next/1, %% erlang t_erlang_hash/1, @@ -119,7 +119,7 @@ all() -> [t_build_and_match_literals, t_build_and_match_literals_large, t_bif_map_update, t_bif_map_values, t_bif_map_to_list, t_bif_map_from_list, - t_bif_erts_internal_maps_to_list, + t_bif_map_next, %% erlang t_erlang_hash, t_map_encode_decode, @@ -2364,41 +2364,71 @@ t_bif_map_from_list(Config) when is_list(Config) -> {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))), ok. -t_bif_erts_internal_maps_to_list(Config) when is_list(Config) -> - %% small maps - [] = erts_internal:maps_to_list(#{},-1), - [] = erts_internal:maps_to_list(#{},-2), - [] = erts_internal:maps_to_list(#{},10), - [{a,1},{b,2}] = lists:sort(erts_internal:maps_to_list(#{a=>1,b=>2}, 2)), - [{a,1},{b,2}] = lists:sort(erts_internal:maps_to_list(#{a=>1,b=>2}, -1)), - [{_,_}] = erts_internal:maps_to_list(#{a=>1,b=>2}, 1), - [{a,1},{b,2},{c,3}] = lists:sort(erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},-2)), - [{a,1},{b,2},{c,3}] = lists:sort(erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},3)), - [{a,1},{b,2},{c,3}] = lists:sort(erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},5)), - [{_,_},{_,_}] = erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},2), - [{_,_}] = erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},1), - [] = erts_internal:maps_to_list(#{c=>3,a=>1,b=>2},0), - - %% big maps - M = maps:from_list([{I,ok}||I <- lists:seq(1,500)]), - [] = erts_internal:maps_to_list(M,0), - [{_,_}] = erts_internal:maps_to_list(M,1), - [{_,_},{_,_}] = erts_internal:maps_to_list(M,2), - Ls1 = erts_internal:maps_to_list(M,10), - 10 = length(Ls1), - Ls2 = erts_internal:maps_to_list(M,20), - 20 = length(Ls2), - Ls3 = erts_internal:maps_to_list(M,120), - 120 = length(Ls3), - Ls4 = erts_internal:maps_to_list(M,-1), - 500 = length(Ls4), +t_bif_map_next(Config) when is_list(Config) -> - %% error cases - {'EXIT', {{badmap,[{a,b},b]},_}} = (catch erts_internal:maps_to_list(id([{a,b},b]),id(1))), - {'EXIT', {badarg,_}} = (catch erts_internal:maps_to_list(id(#{}),id(a))), - {'EXIT', {badarg,_}} = (catch erts_internal:maps_to_list(id(#{1=>2}),id(<<>>))), + erts_debug:set_internal_state(available_internal_state, true), + + try + + none = maps:next(maps:iterator(id(#{}))), + + verify_iterator(#{}), + verify_iterator(#{a => 1, b => 2, c => 3}), + + %% Use fatmap in order to test iterating in very deep maps + FM = fatmap(43), + verify_iterator(FM), + + {'EXIT', {{badmap,[{a,b},b]},_}} = (catch maps:iterator(id([{a,b},b]))), + {'EXIT', {badarg,_}} = (catch maps:next(id(a))), + {'EXIT', {badarg,_}} = (catch maps:next(id([a|FM]))), + {'EXIT', {badarg,_}} = (catch maps:next(id([1|#{}]))), + {'EXIT', {badarg,_}} = (catch maps:next(id([-1|#{}]))), + {'EXIT', {badarg,_}} = (catch maps:next(id([-1|FM]))), + {'EXIT', {badarg,_}} = (catch maps:next(id([16#FFFFFFFFFFFFFFFF|FM]))), + {'EXIT', {badarg,_}} = (catch maps:next(id([-16#FFFFFFFFFFFFFFFF|FM]))), + + %% This us a whitebox test that the error code works correctly. + %% It uses a path for a tree of depth 4 and tries to do next on + %% each of those paths. + (fun F(0) -> ok; + F(N) -> + try maps:next([N|FM]) of + none -> + F(N-1); + {_K,_V,_I} -> + F(N-1) + catch error:badarg -> + F(N-1) + end + end)(16#FFFF), + + ok + after + erts_debug:set_internal_state(available_internal_state, false) + end. + +verify_iterator(Map) -> + KVs = t_fold(fun(K, V, A) -> [{K, V} | A] end, [], Map), + + %% Verify that KVs created by iterating Map is of + %% correct size and contains all elements + true = length(KVs) == maps:size(Map), + [maps:get(K, Map) || {K, _} <- KVs], ok. + +t_fold(Fun, Init, Map) -> + t_fold_1(Fun, Init, maps:iterator(Map)). + +t_fold_1(Fun, Acc, Iter) -> + case maps:next(Iter) of + {K, V, NextIter} -> + t_fold_1(Fun, Fun(K,V,Acc), NextIter); + none -> + Acc + end. + t_bif_build_and_check(Config) when is_list(Config) -> ok = check_build_and_remove(750,[fun(K) -> [K,K] end, fun(K) -> [float(K),K] end, diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index bec2291867..85c302e310 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -2257,9 +2257,8 @@ nif_schedule(Config) when is_list(Config) -> {B,A} = call_nif_schedule(A, B), ok = try call_nif_schedule(1, 2) catch - error:badarg -> - [{?MODULE,call_nif_schedule,[1,2],_}|_] = - erlang:get_stacktrace(), + error:badarg:Stk -> + [{?MODULE,call_nif_schedule,[1,2],_}|_] = Stk, ok end, ok. @@ -2429,8 +2428,8 @@ nif_raise_exceptions(NifFunc) -> erlang:apply(?MODULE,NifFunc,[Term]), ct:fail({expected,Term}) catch - error:Term -> - [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(), + error:Term:Stk -> + [{?MODULE,NifFunc,[Term],_}|_] = Stk, ok end end, ok, ExcTerms). @@ -3037,14 +3036,17 @@ nif_ioq(Config) -> {enqbraw,a}, {enqbraw,a, 5}, {peek, a}, + {peek_head, a}, {deq, a, 42}, %% Test enqv {enqv, a, 2, 100}, + {peek_head, a}, {deq, a, all}, %% This skips all elements but one in the iolist {enqv, a, 5, iolist_size(nif_ioq_payload(5)) - 1}, + {peek_head, a}, {peek, a}, %% Test to enqueue a bunch of refc binaries @@ -3074,9 +3076,13 @@ nif_ioq(Config) -> Q = ioq_nif(create), + false = ioq_nif(peek_head, Q), + {'EXIT', {badarg, _}} = (catch ioq_nif(deq, Q, 1)), {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, 1, 1234)), + false = ioq_nif(peek_head, Q), + {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [atom_in_list], 0)), {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [make_ref()], 0)), {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [256], 0)), @@ -3085,6 +3091,8 @@ nif_ioq(Config) -> {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [1 bsl 64], 0)), {'EXIT', {badarg, _}} = (catch ioq_nif(enqv, Q, [{tuple}], 0)), + false = ioq_nif(peek_head, Q), + {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [atom_in_list], use_stack)), {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [make_ref()], no_stack)), {'EXIT', {badarg, _}} = (catch ioq_nif(inspect, [256], use_stack)), @@ -3145,6 +3153,20 @@ nif_ioq_run([{peek, Name} = H|T], State) -> true = iolist_to_binary(B) == iolist_to_binary(Data), nif_ioq_run(T, State); +nif_ioq_run([{peek_head, Name} = H|T], State) -> + #{ q := IOQ, b := B } = maps:get(Name, State), + RefData = iolist_to_binary(B), + + ct:log("~p", [H]), + + {true, QueueHead} = ioq_nif(peek_head, IOQ), + true = byte_size(QueueHead) > 0, + + {RefHead, _Tail} = split_binary(RefData, byte_size(QueueHead)), + + true = QueueHead =:= RefHead, + + nif_ioq_run(T, State); nif_ioq_run([{deq, Name, all}|T], State) -> #{ q := IOQ, b := B } = maps:get(Name, State), Size = ioq_nif(size, IOQ), diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 79560a38aa..fa9ae1015c 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -3403,6 +3403,15 @@ static ERL_NIF_TERM ioq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return enif_make_badarg(env); else return enif_make_atom(env, "true"); + } else if (enif_is_identical(argv[0], enif_make_atom(env, "peek_head"))) { + ERL_NIF_TERM head_term; + + if(enif_ioq_peek_head(env, ioq->q, NULL, &head_term)) { + return enif_make_tuple2(env, + enif_make_atom(env, "true"), head_term); + } + + return enif_make_atom(env, "false"); } else if (enif_is_identical(argv[0], enif_make_atom(env, "peek"))) { int iovlen, num, i, off = 0; SysIOVec *iov = enif_ioq_peek(ioq->q, &iovlen); diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index be90f929df..7df001fec5 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -966,6 +966,8 @@ check_refc(ThisNodeName,ThisCreation,Table,EntryList) when is_list(EntryList) -> {case Referrer of {system,delayed_delete_timer} -> true; + {system,thread_progress_delete_timer} -> + true; _ -> DDT end, diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index 17555d63c6..592542405f 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -118,6 +118,7 @@ t_float(Config) when is_list(Config) -> %% Tests float_to_list/1, float_to_list/2, float_to_binary/1, float_to_binary/2 t_float_to_string(Config) when is_list(Config) -> + rand_seed(), test_fts("0.00000000000000000000e+00", 0.0), test_fts("2.50000000000000000000e+01", 25.0), test_fts("2.50000000000000000000e+00", 2.5), @@ -167,8 +168,8 @@ t_float_to_string(Config) when is_list(Config) -> test_fts("1.12300",1.123, [{decimals, 5}]), test_fts("1.123",1.123, [{decimals, 5}, compact]), test_fts("1.1234",1.1234,[{decimals, 6}, compact]), - test_fts("1.01",1.005, [{decimals, 2}]), - test_fts("-1.01",-1.005,[{decimals, 2}]), + test_fts("1.00",1.005, [{decimals, 2}]), %% 1.005 is really 1.0049999999... + test_fts("-1.00",-1.005,[{decimals, 2}]), test_fts("0.999",0.999, [{decimals, 3}]), test_fts("-0.999",-0.999,[{decimals, 3}]), test_fts("1.0",0.999, [{decimals, 2}, compact]), @@ -184,6 +185,9 @@ t_float_to_string(Config) when is_list(Config) -> test_fts("123000000000000000000.0",1.23e20, [{decimals, 10}, compact]), test_fts("1.2300000000e+20",1.23e20, [{scientific, 10}, compact]), test_fts("1.23000000000000000000e+20",1.23e20, []), + + fts_rand_float_decimals(1000), + ok. test_fts(Expect, Float) -> @@ -197,6 +201,49 @@ test_fts(Expect, Float, Args) -> BinExpect = float_to_binary(Float,Args). +rand_float_reasonable() -> + F = rand_float(), + case abs(F) > 1.0e238 of + true -> rand_float_reasonable(); + false -> F + end. + +fts_rand_float_decimals(0) -> ok; +fts_rand_float_decimals(N) -> + [begin + F0 = rand_float_reasonable(), + L0 = float_to_list(F0, [{decimals, D}]), + L1 = case D of + 0 -> L0 ++ ".0"; + _ -> L0 + end, + F1 = list_to_float(L1), + Diff = abs(F0-F1), + MaxDiff = max_diff_decimals(F0, D), + ok = case Diff =< MaxDiff of + true -> ok; + false -> + io:format("F0 = ~w ~w\n", [F0, <<F0/float>>]), + io:format("L1 = ~s\n", [L1]), + io:format("F1 = ~w ~w\n", [F1, <<F1/float>>]), + io:format("Diff = ~w, MaxDiff = ~w\n", [Diff, MaxDiff]), + error + end + end + || D <- lists:seq(0,15)], + + fts_rand_float_decimals(N-1). + +max_diff_decimals(F, D) -> + IntBits = floor(math:log2(abs(F))) + 1, + FracBits = (52 - IntBits), + Log10_2 = 0.3010299956639812, % math:log10(2) + MaxDec = floor(FracBits * Log10_2), + + Resolution = math:pow(2, IntBits - 53), + + (math:pow(10, -min(D,MaxDec)) / 2) + Resolution. + %% Tests list_to_float/1. t_string_to_float_safe(Config) when is_list(Config) -> @@ -331,18 +378,26 @@ t_trunc_and_friends(_Config) -> -18446744073709551616 = trunc_and_friends(-float(1 bsl 64)), %% Random. + rand_seed(), t_trunc_and_friends_rand(100), ok. +rand_seed() -> + rand:seed(exrop), + io:format("\n*** rand:export_seed() = ~w\n\n", [rand:export_seed()]), + ok. + +rand_float() -> + F0 = rand:uniform() * math:pow(10, 50*rand:normal()), + case rand:uniform() of + U when U < 0.5 -> -F0; + _ -> F0 + end. + t_trunc_and_friends_rand(0) -> ok; t_trunc_and_friends_rand(N) -> - F0 = rand:uniform() * math:pow(10, 50*rand:normal()), - F = case rand:uniform() of - U when U < 0.5 -> -F0; - _ -> F0 - end, - _ = trunc_and_friends(F), + _ = trunc_and_friends(rand_float()), t_trunc_and_friends_rand(N-1). trunc_and_friends(F) -> @@ -491,7 +546,7 @@ t_string_to_integer(Config) when is_list(Config) -> list_to_binary(Value),Base)), {'EXIT', {badarg, _}} = (catch erlang:list_to_integer(Value,Base)) - end,[{" 1",1},{" 1",37},{"2",2},{"C",11}, + end,[{" 1",1},{" 1",37},{"2",2},{"B",11},{"b",11},{":", 16}, {"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111z",16}, {"1z111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",16}, {"111z11111111",16}]), diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index f29528a22f..e46665a881 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1035,6 +1035,9 @@ huge_env(Config) when is_list(Config) -> try erlang:open_port({spawn,Cmd},[exit_status, {env, Env}]) of P -> receive + {P, {exit_status,N}} = M when N > 127-> + %% If exit status is > 127 something went very wrong + ct:fail("Open port failed got ~p",[M]); {P, {exit_status,N}} = M -> %% We test that the exit status is an integer, this means %% that the child program has started. If we get an atom diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index a9f20f9928..a8bcfac84d 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -2532,8 +2532,13 @@ system_task_on_suspended(Config) when is_list(Config) -> end. gc_request_when_gc_disabled(Config) when is_list(Config) -> - Master = self(), AIS = erts_debug:set_internal_state(available_internal_state, true), + gc_request_when_gc_disabled_do(ref), + gc_request_when_gc_disabled_do(immed), + erts_debug:set_internal_state(available_internal_state, AIS). + +gc_request_when_gc_disabled_do(ReqIdType) -> + Master = self(), {P, M} = spawn_opt(fun () -> true = erts_debug:set_internal_state(gc_state, false), @@ -2545,7 +2550,10 @@ gc_request_when_gc_disabled(Config) when is_list(Config) -> receive after 100 -> ok end end, [monitor, link]), receive {P, gc_state, false} -> ok end, - ReqId = make_ref(), + ReqId = case ReqIdType of + ref -> make_ref(); + immed -> immed + end, async = garbage_collect(P, [{async, ReqId}]), receive {garbage_collect, ReqId, Result} -> @@ -2554,7 +2562,6 @@ gc_request_when_gc_disabled(Config) when is_list(Config) -> ok end, receive {garbage_collect, ReqId, true} -> ok end, - erts_debug:set_internal_state(available_internal_state, AIS), receive {'DOWN', M, process, P, _Reason} -> ok end, ok. diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl index a12019ec83..1fe11428b4 100644 --- a/erts/emulator/test/receive_SUITE.erl +++ b/erts/emulator/test/receive_SUITE.erl @@ -25,14 +25,16 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, - call_with_huge_message_queue/1,receive_in_between/1]). + call_with_huge_message_queue/1,receive_in_between/1, + receive_opt_exception/1,receive_opt_recursion/1]). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 3}}]. -all() -> - [call_with_huge_message_queue, receive_in_between]. +all() -> + [call_with_huge_message_queue, receive_in_between, + receive_opt_exception, receive_opt_recursion]. call_with_huge_message_queue(Config) when is_list(Config) -> Pid = spawn_link(fun echo_loop/0), @@ -113,6 +115,60 @@ receive_one() -> dummy -> ok end. +receive_opt_exception(_Config) -> + Recurse = fun() -> + %% Overwrite with the same mark, + %% and never consume it. + ThrowFun = fun() -> throw(aborted) end, + aborted = (catch do_receive_opt_exception(ThrowFun)), + ok + end, + do_receive_opt_exception(Recurse), + + %% Eat the second message. + receive + Ref when is_reference(Ref) -> ok + end. + +do_receive_opt_exception(Disturber) -> + %% Create a receive mark. + Ref = make_ref(), + self() ! Ref, + Disturber(), + receive + Ref -> + ok + after 0 -> + error(the_expected_message_was_not_there) + end. + +receive_opt_recursion(_Config) -> + Recurse = fun() -> + %% Overwrite with the same mark, + %% and never consume it. + NoOp = fun() -> ok end, + BlackHole = spawn(NoOp), + expected = do_receive_opt_recursion(BlackHole, NoOp, true), + ok + end, + do_receive_opt_recursion(self(), Recurse, false), + ok. + +do_receive_opt_recursion(Recipient, Disturber, IsInner) -> + Ref = make_ref(), + Recipient ! Ref, + Disturber(), + receive + Ref -> ok + after 0 -> + case IsInner of + true -> + expected; + false -> + error(the_expected_message_was_not_there) + end + end. + %%% %%% Common helpers. %%% |