diff options
Diffstat (limited to 'lib')
97 files changed, 3919 insertions, 4635 deletions
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index 5badcce696..c699672db1 100644 --- a/lib/compiler/src/beam_disasm.erl +++ b/lib/compiler/src/beam_disasm.erl @@ -314,10 +314,7 @@ get_funs({LsR0,[{func_info,[{atom,M}=AtomM,{atom,F}=AtomF,ArityArg]}|Code0]}) when is_atom(M), is_atom(F) -> Arity = resolve_arg_unsigned(ArityArg), {LsR,Code,RestCode} = get_fun(Code0, []), - Entry = case Code of - [{label,[{u,E}]}|_] -> E; - _ -> undefined - end, + [{label,[{u,Entry}]}|_] = Code, [#function{name=F, arity=Arity, entry=Entry, diff --git a/lib/compiler/src/beam_disasm.hrl b/lib/compiler/src/beam_disasm.hrl index e18214644f..d968cd9587 100644 --- a/lib/compiler/src/beam_disasm.hrl +++ b/lib/compiler/src/beam_disasm.hrl @@ -22,7 +22,9 @@ %% the system (e.g. in the translation from Beam to Icode). %% -%% XXX: THE FOLLOWING TYPE DECLARATION DOES NOT BELONG HERE... +%% XXX: THE FOLLOWING TYPE DECLARATION DOES NOT BELONG HERE. +%% IT SHOULD BE MOVED TO A FILE THAT DEFINES (AND EXPORTS) +%% PROPER TYPES FOR THE SET OF BEAM INSTRUCTIONS. %% -type beam_instr() :: 'bs_init_writable' | 'fclearerror' | 'if_end' | 'remove_message' | 'return' | 'send' | 'timeout' @@ -34,7 +36,7 @@ -record(function, {name :: atom(), arity :: byte(), - entry, %% unused ?? + entry :: beam_lib:label(), %% unnecessary ? code = [] :: [beam_instr()]}). -record(beam_file, {module :: module(), diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 402e3c4912..b4bbc5e739 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -242,7 +242,7 @@ gexpr_test_add(Ke, St0) -> expr(#c_var{anno=A,name={_Name,Arity}}=Fname, Sub, St) -> %% A local in an expression. %% For now, these are wrapped into a fun by reverse - %% etha-conversion, but really, there should be exactly one + %% eta-conversion, but really, there should be exactly one %% such "lambda function" for each escaping local name, %% instead of one for each occurrence as done now. Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} || diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl index 536e891a1e..d52a7c83f7 100644 --- a/lib/eldap/test/eldap_basic_SUITE.erl +++ b/lib/eldap/test/eldap_basic_SUITE.erl @@ -28,12 +28,11 @@ -include_lib("eldap/ebin/ELDAPv3.hrl"). --define(TIMEOUT, 120000). % 2 min - - %% Control to delete a referral object: -define(manageDsaIT, {control,"2.16.840.1.113730.3.4.2",false,asn1_NOVALUE}). +suite() -> + [{timetrap,{seconds,40}}]. all() -> [app, @@ -124,7 +123,7 @@ end_per_suite(_Config) -> init_per_group(return_values, Config) -> - case ?config(ldap_server,Config) of + case proplists:get_value(ldap_server,Config) of undefined -> {skip, "LDAP server not availble"}; {Host,Port} -> @@ -132,7 +131,7 @@ init_per_group(return_values, Config) -> Config end; init_per_group(plain_api, Config0) -> - case ?config(ldap_server,Config0) of + case proplists:get_value(ldap_server,Config0) of undefined -> {skip, "LDAP server not availble"}; Server = {Host,Port} -> @@ -140,7 +139,7 @@ init_per_group(plain_api, Config0) -> initialize_db([{server,Server}, {ssl_flag,false}, {start_tls,false} | Config0]) end; init_per_group(ssl_api, Config0) -> - case ?config(ldaps_server,Config0) of + case proplists:get_value(ldaps_server,Config0) of undefined -> {skip, "LDAPS server not availble"}; Server = {Host,Port} -> @@ -148,7 +147,7 @@ init_per_group(ssl_api, Config0) -> initialize_db([{server,Server}, {ssl_flag,true}, {start_tls,false} | Config0]) end; init_per_group(start_tls_api, Config0) -> - case {?config(ldap_server,Config0), ?config(ssl_available,Config0)} of + case {proplists:get_value(ldap_server,Config0), proplists:get_value(ssl_available,Config0)} of {undefined,true} -> {skip, "LDAP server not availble"}; {_,false} -> @@ -187,18 +186,18 @@ end_per_group(_Group, Config) -> Config. init_per_testcase(ssl_connection, Config) -> - case ?config(ssl_available,Config) of + case proplists:get_value(ssl_available,Config) of true -> SSL_Port = 9999, - CertFile = filename:join(?config(data_dir,Config), "certs/server/cert.pem"), - KeyFile = filename:join(?config(data_dir,Config), "certs/server/key.pem"), + CertFile = filename:join(proplists:get_value(data_dir,Config), "certs/server/cert.pem"), + KeyFile = filename:join(proplists:get_value(data_dir,Config), "certs/server/key.pem"), Parent = self(), Listener = spawn_link( fun() -> case ssl:listen(SSL_Port, [{certfile, CertFile}, {keyfile, KeyFile} - | ?config(tcp_listen_opts,Config) + | proplists:get_value(tcp_listen_opts,Config) ]) of {ok,SSL_LSock} -> Parent ! {ok,self()}, @@ -245,7 +244,7 @@ init_per_testcase(TC, Config) -> end; false -> - case proplists:get_value(name,?config(tc_group_properties, Config)) of + case proplists:get_value(name,proplists:get_value(tc_group_properties, Config)) of api_not_bound -> {ok,H} = open(Config), [{handle,H} | Config]; @@ -282,7 +281,7 @@ appup(Config) when is_list(Config) -> %%%---------------------------------------------------------------- open_ret_val_success(Config) -> - {Host,Port} = ?config(ldap_server,Config), + {Host,Port} = proplists:get_value(ldap_server,Config), {ok,H} = eldap:open([Host], [{port,Port}]), catch eldap:close(H). @@ -292,7 +291,7 @@ open_ret_val_error(_Config) -> %%%---------------------------------------------------------------- close_ret_val(Config) -> - {Host,Port} = ?config(ldap_server,Config), + {Host,Port} = proplists:get_value(ldap_server,Config), {ok,H} = eldap:open([Host], [{port,Port}]), ok = eldap:close(H). @@ -313,7 +312,6 @@ tcp_connection(Config) -> end. %%%---------------------------------------------------------------- - close_after_tcp_error(Config) -> Host = proplists:get_value(listen_host, Config), Port = proplists:get_value(listen_port, Config), @@ -436,37 +434,37 @@ tcp_connection_option(Config) -> %%%---------------------------------------------------------------- elementary_search(Config) -> {ok, #eldap_search_result{entries=[_]}} = - eldap:search(?config(handle,Config), - #eldap_search{base = ?config(eldap_path, Config), + eldap:search(proplists:get_value(handle,Config), + #eldap_search{base = proplists:get_value(eldap_path, Config), filter= eldap:present("objectclass"), scope = eldap:wholeSubtree()}). %%%---------------------------------------------------------------- search_non_existant(Config) -> {error, noSuchObject} = - eldap:search(?config(handle,Config), - #eldap_search{base = "cn=Bar," ++ ?config(eldap_path, Config), + eldap:search(proplists:get_value(handle,Config), + #eldap_search{base = "cn=Bar," ++ proplists:get_value(eldap_path, Config), filter= eldap:present("objectclass"), scope = eldap:wholeSubtree()}). %%%---------------------------------------------------------------- add_when_not_bound(Config) -> - {error, _} = eldap:add(?config(handle,Config), - "cn=Jonas Jonsson," ++ ?config(eldap_path, Config), + {error, _} = eldap:add(proplists:get_value(handle,Config), + "cn=Jonas Jonsson," ++ proplists:get_value(eldap_path, Config), [{"objectclass", ["person"]}, {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]). %%%---------------------------------------------------------------- bind(Config) -> - ok = eldap:simple_bind(?config(handle,Config), + ok = eldap:simple_bind(proplists:get_value(handle,Config), "cn=Manager,dc=ericsson,dc=se", "hejsan"). %%%---------------------------------------------------------------- add_when_bound(Config) -> - ok = eldap:add(?config(handle, Config), - "cn=Jonas Jonsson," ++ ?config(eldap_path, Config), + ok = eldap:add(proplists:get_value(handle, Config), + "cn=Jonas Jonsson," ++ proplists:get_value(eldap_path, Config), [{"objectclass", ["person"]}, {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]). @@ -474,16 +472,16 @@ add_when_bound(Config) -> %%%---------------------------------------------------------------- add_already_exists(Config) -> {error, entryAlreadyExists} = - eldap:add(?config(handle, Config), - "cn=Jonas Jonsson," ++ ?config(eldap_path, Config), + eldap:add(proplists:get_value(handle, Config), + "cn=Jonas Jonsson," ++ proplists:get_value(eldap_path, Config), [{"objectclass", ["person"]}, {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]). %%%---------------------------------------------------------------- more_add(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), ok = eldap:add(H, "cn=Foo Bar," ++ BasePath, [{"objectclass", ["person"]}, {"cn", ["Foo Bar"]}, @@ -495,8 +493,8 @@ more_add(Config) -> %%%---------------------------------------------------------------- add_referral(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), {ok,{referral,["ldap://nowhere.example.com"++_]}} = eldap:add(H, "cn=Foo Bar,dc=notHere," ++ BasePath, [{"objectclass", ["person"]}, @@ -506,28 +504,28 @@ add_referral(Config) -> %%%---------------------------------------------------------------- search_filter_equalityMatch(Config) -> - BasePath = ?config(eldap_path, Config), + BasePath = proplists:get_value(eldap_path, Config), ExpectedDN = "cn=Jonas Jonsson," ++ BasePath, {ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} = - eldap:search(?config(handle, Config), + eldap:search(proplists:get_value(handle, Config), #eldap_search{base = BasePath, filter = eldap:equalityMatch("sn", "Jonsson"), scope=eldap:singleLevel()}). %%%---------------------------------------------------------------- search_filter_substring_any(Config) -> - BasePath = ?config(eldap_path, Config), + BasePath = proplists:get_value(eldap_path, Config), ExpectedDN = "cn=Jonas Jonsson," ++ BasePath, {ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} = - eldap:search(?config(handle, Config), + eldap:search(proplists:get_value(handle, Config), #eldap_search{base = BasePath, filter = eldap:substrings("sn", [{any, "ss"}]), scope=eldap:singleLevel()}). %%%---------------------------------------------------------------- search_filter_initial(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), ExpectedDN = "cn=Foo Bar," ++ BasePath, {ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} = eldap:search(H, @@ -537,8 +535,8 @@ search_filter_initial(Config) -> %%%---------------------------------------------------------------- search_filter_final(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), ExpectedDN = "cn=Foo Bar," ++ BasePath, {ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} = eldap:search(H, @@ -548,8 +546,8 @@ search_filter_final(Config) -> %%%---------------------------------------------------------------- search_filter_and(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), ExpectedDN = "cn=Foo Bar," ++ BasePath, {ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} = eldap:search(H, @@ -560,8 +558,8 @@ search_filter_and(Config) -> %%%---------------------------------------------------------------- search_filter_or(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), ExpectedDNs = lists:sort(["cn=Foo Bar," ++ BasePath, "ou=Team," ++ BasePath]), {ok, #eldap_search_result{entries=Es}} = @@ -574,8 +572,8 @@ search_filter_or(Config) -> %%%---------------------------------------------------------------- search_filter_and_not(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), {ok, #eldap_search_result{entries=[]}} = eldap:search(H, #eldap_search{base = BasePath, @@ -587,8 +585,8 @@ search_filter_and_not(Config) -> %%%---------------------------------------------------------------- search_two_hits(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), DN1 = "cn=Santa Claus," ++ BasePath, DN2 = "cn=Jultomten," ++ BasePath, %% Add two objects: @@ -619,8 +617,8 @@ search_two_hits(Config) -> %%%---------------------------------------------------------------- search_referral(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), DN = "cn=Santa Claus,dc=notHere," ++ BasePath, {ok,{referral,["ldap://nowhere.example.com"++_]}} = eldap:search(H, #eldap_search{base = DN, @@ -629,8 +627,8 @@ search_referral(Config) -> %%%---------------------------------------------------------------- modify(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), %% The object to modify DN = "cn=Foo Bar," ++ BasePath, @@ -662,8 +660,8 @@ modify(Config) -> %%%---------------------------------------------------------------- modify_referral(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), %% The object to modify DN = "cn=Foo Bar,dc=notHere," ++ BasePath, @@ -675,8 +673,8 @@ modify_referral(Config) -> %%%---------------------------------------------------------------- delete(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), %% The element to play with: DN = "cn=Jonas Jonsson," ++ BasePath, @@ -693,16 +691,16 @@ delete(Config) -> %%%---------------------------------------------------------------- delete_referral(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), %% The element to play with: DN = "cn=Jonas Jonsson,dc=notHere," ++ BasePath, {ok,{referral,["ldap://nowhere.example.com"++_]}} = eldap:delete(H, DN). %%%---------------------------------------------------------------- modify_dn_delete_old(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), OrigCN = "Foo Bar", OriginalRDN = "cn="++OrigCN, DN = OriginalRDN ++ "," ++ BasePath, @@ -747,8 +745,8 @@ modify_dn_delete_old(Config) -> %%%---------------------------------------------------------------- modify_dn_keep_old(Config) -> - H = ?config(handle, Config), - BasePath = ?config(eldap_path, Config), + H = proplists:get_value(handle, Config), + BasePath = proplists:get_value(eldap_path, Config), OriginalRDN = "cn=Foo Bar", DN = OriginalRDN ++ "," ++ BasePath, NewCN = "Niclas Andre", @@ -887,7 +885,7 @@ initialize_db(Config) -> clear_db(Config) -> {ok,H} = open_bind(Config), - Path = ?config(eldap_path, Config), + Path = proplists:get_value(eldap_path, Config), delete_old_contents(H, Path), eldap:close(H), Config. @@ -939,20 +937,20 @@ ok(MODULE, LINE, X) -> cond_start_tls(H, Config) -> - case ?config(start_tls,Config) of + case proplists:get_value(start_tls,Config) of true -> start_tls(H,Config); _ -> Config end. start_tls(H, Config) -> - KeyFile = filename:join([?config(data_dir,Config), + KeyFile = filename:join([proplists:get_value(data_dir,Config), "certs/client/key.pem" ]), case eldap:start_tls(H, [{keyfile, KeyFile}]) of ok -> [{start_tls_success,true} | Config]; Error -> - ct:log("Start_tls on ~p failed: ~p",[?config(url,Config) ,Error]), + ct:log("Start_tls on ~p failed: ~p",[proplists:get_value(url,Config) ,Error]), ct:fail("start_tls failed") end. @@ -964,8 +962,8 @@ open_bind(Config) -> {ok,H}. open(Config) -> - {Host,Port} = ?config(server,Config), - SSLflag = ?config(ssl_flag,Config), + {Host,Port} = proplists:get_value(server,Config), + SSLflag = proplists:get_value(ssl_flag,Config), {ok,H} = eldap:open([Host], [{port,Port},{ssl,SSLflag}]), cond_start_tls(H, Config), {ok,H}. @@ -1023,7 +1021,7 @@ init_ssl_certs_et_al(Config) -> of R when R==ok ; R=={error,{already_started,ssl}} -> try make_certs:all("/dev/null", - filename:join(?config(data_dir,Config), "certs")) + filename:join(proplists:get_value(data_dir,Config), "certs")) of {ok,_} -> true; Other -> diff --git a/lib/et/src/et_collector.erl b/lib/et/src/et_collector.erl index b8916c0eb9..aba90b0be1 100644 --- a/lib/et/src/et_collector.erl +++ b/lib/et/src/et_collector.erl @@ -27,8 +27,7 @@ -behaviour(gen_server). %% External exports --export([ - start_link/1, +-export([start_link/1, stop/1, report/2, @@ -55,8 +54,7 @@ dict_delete/2, dict_lookup/2, dict_match/2, - multicast/2 - ]). + multicast/2]). %% Internal export -export([monitor_trace_port/2]). @@ -258,7 +256,7 @@ parse_opt(BadList, _S, _Dict, _Clients) -> {error, {bad_option_list, BadList}}. start_clients(CollectorPid, [{Type, Parameters} | T]) -> - start_trace_client(CollectorPid, Type, Parameters), + _ = start_trace_client(CollectorPid, Type, Parameters), start_clients(CollectorPid, T); start_clients(CollectorPid, []) -> {ok, CollectorPid}. @@ -892,7 +890,7 @@ init([InitialS, Dict]) -> process_flag(trap_exit, true), case InitialS#state.parent_pid of undefined -> - ignore; + ok; Pid when is_pid(Pid) -> link(Pid) end, @@ -914,7 +912,7 @@ init_global(S) -> Spec = trace_spec_wrapper(EventFun, EndFun, {ok, self()}), dbg:tracer(process, Spec), et_selector:change_pattern(S#state.trace_pattern), - net_kernel:monitor_nodes(true), + ok = net_kernel:monitor_nodes(true), lists:foreach(fun(N) -> self() ! {nodeup, N} end, nodes()), S#state{trace_nodes = [node()]}; false -> @@ -1001,7 +999,7 @@ handle_call({save_event_file, FileName, Options}, _From, S) -> %% insert() -> %% case S2#state.file of %% undefined -> - %% ignore; + %% ok; %% F -> %% Fd = F#file.desc, %% ok = disk_log:log(Fd, Event) @@ -1010,7 +1008,7 @@ handle_call({save_event_file, FileName, Options}, _From, S) -> Fun = fun({_, E}, A) -> ok = disk_log:log(Fd, E), A end, Tab = S#state.event_tab, Reply = tab_iterate(Fun, Tab, ets:first(Tab), ok), - disk_log:close(Fd), + ok = disk_log:close(Fd), {Reply, S} %% all -> %% Reply = tab_iterate(WriteFun, Tab, ok), @@ -1033,7 +1031,7 @@ handle_call({save_event_file, FileName, Options}, _From, S) -> handle_call({change_pattern, Pattern}, _From, S) -> Ns = S#state.trace_nodes, - rpc:multicall(Ns, et_selector, change_pattern, [Pattern]), + {_,[]} = rpc:multicall(Ns, et_selector, change_pattern, [Pattern]), Reply = {old_pattern, S#state.trace_pattern}, S2 = S#state{trace_pattern = Pattern}, reply(Reply, S2); @@ -1045,8 +1043,9 @@ handle_call(clear_table, _From, S) -> handle_call(stop, _From, S) -> do_multicast(S#state.subscribers, close), case S#state.trace_global of - true -> rpc:multicall(S#state.trace_nodes, dbg, stop_clear, []); - false -> ignore + true -> {_,[]} = rpc:multicall(S#state.trace_nodes, dbg, stop_clear, []), + ok; + false -> ok end, {stop, shutdown, ok, S}; handle_call(Request, From, S) -> @@ -1239,8 +1238,8 @@ tab_iterate(Fun, Tab, Key, Acc) -> file_open(F) -> Fd = make_ref(), case F#file.file_opt of - write -> file:rename(F#file.name, F#file.name ++ ".OLD"); - append -> ignore + write -> ok = file:rename(F#file.name, F#file.name ++ ".OLD"); + append -> ok end, Args = [{file, F#file.name}, {name, Fd}, {repair, true}, {mode, read_write}], @@ -1278,7 +1277,7 @@ do_multicast([], _Msg) -> opt_unlink(Pid) -> if Pid =:= undefined -> - ignore; + ok; true -> unlink(Pid) end. diff --git a/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl index d5347a43db..247dd4c7ba 100644 --- a/lib/et/src/et_wx_contents_viewer.erl +++ b/lib/et/src/et_wx_contents_viewer.erl @@ -89,7 +89,7 @@ start_link(Options) -> S#state.parent_pid =/= self() -> unlink(Pid); true -> - ignore + ok end, {ok, Pid} catch @@ -242,11 +242,12 @@ handle_event(#wx{id = Id, S) -> case proplists:get_value(Id, S#state.menu_data) of undefined -> - ignore; + ok; Data when is_record(Data, filter) -> F = Data, - ChildState= S#state{active_filter = F#filter.name}, - wx_object:start_link(?MODULE, [ChildState], []); + ChildState = S#state{active_filter = F#filter.name}, + _ = wx_object:start_link(?MODULE, [ChildState], []), + ok; {hide, Actors} -> send_viewer_event(S, {delete_actors, Actors}); {show, Actors} -> @@ -267,7 +268,7 @@ handle_event(#wx{id = Id, TimeStamp = case S#state.event_order of trace_ts -> Event#event.trace_ts; - event_ts -> Event#event.event_ts + event_ts -> Event#event.event_ts end, FileName = lists:flatten(["et_contents_viewer_", now_to_string(TimeStamp), ".txt"]), Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT, @@ -275,7 +276,7 @@ handle_event(#wx{id = Id, case select_file(S#state.frame, Msg, filename:absname(FileName), Style) of {ok, FileName2} -> Bin = list_to_binary(event_to_string(Event, S#state.event_order)), - file:write_file(FileName2, Bin); + ok = file:write_file(FileName2, Bin); cancel -> ok end, @@ -351,19 +352,21 @@ handle_event(#wx{event = #wxKey{rawCode = KeyCode}}, S) -> $0 -> case lists:keysearch(?DEFAULT_FILTER_NAME, #filter.name, S#state.filters) of {value, F} when is_record(F, filter) -> - ChildState= S#state{active_filter = F#filter.name}, - wx_object:start_link(?MODULE, [ChildState], []); + ChildState = S#state{active_filter = F#filter.name}, + _ = wx_object:start_link(?MODULE, [ChildState], []), + ok; false -> - ignore + ok end, {noreply, S}; Int when is_integer(Int), Int > $0, Int =< $9 -> case catch lists:nth(Int-$0, S#state.filters) of F when is_record(F, filter) -> - ChildState= S#state{active_filter = F#filter.name}, - wx_object:start_link(?MODULE, [ChildState], []); + ChildState = S#state{active_filter = F#filter.name}, + _ = wx_object:start_link(?MODULE, [ChildState], []), + ok; {'EXIT', _} -> - ignore + ok end, {noreply, S}; @@ -443,11 +446,11 @@ create_window(S) -> Title = lists:concat([?MODULE, " (filter: ", Name, ")"]), WinOpt = [{size, {W,H}}], Frame = wxFrame:new(wx:null(), ?wxID_ANY, Title, WinOpt), - wxFrame:createStatusBar(Frame), + _ = wxFrame:createStatusBar(Frame), Panel = wxPanel:new(Frame, []), Bar = wxMenuBar:new(), - wxFrame:setMenuBar(Frame,Bar), + _ = wxFrame:setMenuBar(Frame,Bar), create_file_menu(Bar), Editor = wxTextCtrl:new(Panel, ?wxID_ANY, [{style, 0 bor ?wxDEFAULT @@ -457,21 +460,21 @@ create_window(S) -> bor ?wxTE_DONTWRAP}]), Font = wxFont:new(10, ?wxFONTFAMILY_TELETYPE, ?wxNORMAL, ?wxNORMAL,[]), TextAttr = wxTextAttr:new(?wxBLACK, [{font, Font}]), - wxTextCtrl:setDefaultStyle(Editor, TextAttr), + _ = wxTextCtrl:setDefaultStyle(Editor, TextAttr), Sizer = wxBoxSizer:new(?wxHORIZONTAL), - wxSizer:add(Sizer, Editor, [{flag, ?wxEXPAND}, {proportion, 1}]), + _ = wxSizer:add(Sizer, Editor, [{flag, ?wxEXPAND}, {proportion, 1}]), FilteredEvent = config_editor(Editor, S), S2 = S#state{win = Frame, panel = Panel, filtered_event = FilteredEvent}, HideData = create_hide_menu(Bar, S2), SearchData = create_search_menu(Bar, S2), FilterData = create_filter_menu(Bar, S#state.filters), - wxFrame:connect(Frame, command_menu_selected, []), - wxFrame:connect(Frame, key_up), - wxFrame:connect(Frame, close_window, [{skip,true}]), - wxFrame:setFocus(Frame), - wxPanel:setSizer(Panel, Sizer), - wxSizer:fit(Sizer, Panel), - wxFrame:show(Frame), + _ = wxFrame:connect(Frame, command_menu_selected, []), + _ = wxFrame:connect(Frame, key_up), + _ = wxFrame:connect(Frame, close_window, [{skip,true}]), + _ = wxFrame:setFocus(Frame), + _ = wxPanel:setSizer(Panel, Sizer), + _ = wxSizer:fit(Sizer, Panel), + _ = wxFrame:show(Frame), S2#state{menu_data = HideData++SearchData++FilterData, editor = Editor, frame = Frame}. menuitem(Menu, Id, Text, UserData) -> @@ -480,16 +483,17 @@ menuitem(Menu, Id, Text, UserData) -> create_file_menu(Bar) -> Menu = wxMenu:new([]), - wxMenu:append(Menu, ?wxID_SAVE, "Save"), - wxMenu:append(Menu, ?wxID_PRINT,"Print"), - wxMenu:appendSeparator(Menu), - wxMenu:append(Menu, ?wxID_EXIT, "Close"), - wxMenuBar:append(Bar, Menu, "File"). + _ = wxMenu:append(Menu, ?wxID_SAVE, "Save"), + _ = wxMenu:append(Menu, ?wxID_PRINT,"Print"), + _ = wxMenu:appendSeparator(Menu), + _ = wxMenu:append(Menu, ?wxID_EXIT, "Close"), + _ = wxMenuBar:append(Bar, Menu, "File"), + ok. create_filter_menu(Bar, Filters) -> Menu = wxMenu:new([]), - wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Select Filter"), [{enable, false}]), - wxMenu:appendSeparator(Menu), + _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Select Filter"), [{enable, false}]), + _ = wxMenu:appendSeparator(Menu), Item = fun(F, {N,Acc}) when F#filter.name =:= ?DEFAULT_FILTER_NAME-> Label = lists:concat([pad_string(F#filter.name, 20, $\ , right), "(0)"]), MenuItem = menuitem(Menu, ?wxID_ANY, Label, F), @@ -502,7 +506,7 @@ create_filter_menu(Bar, Filters) -> end, Filters2 = lists:keysort(#filter.name, Filters), {_,MenuData} = lists:foldl(Item, {1, []}, Filters2), - wxMenuBar:append(Bar, Menu, "Filters"), + _ = wxMenuBar:append(Bar, Menu, "Filters"), MenuData. create_hide_menu(Bar, S) -> @@ -515,33 +519,33 @@ create_hide_menu(Bar, S) -> S#state.viewer_pid =:= undefined -> ignore; From =:= To -> - wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Hide actor in Viewer "), - [{enable, false}]), - wxMenu:appendSeparator(Menu), + _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Hide actor in Viewer "), + [{enable, false}]), + _ = wxMenu:appendSeparator(Menu), Hide = menuitem(Menu, ?wxID_ANY, "From=To (f|t|b)", {hide, [From]}), - wxMenu:appendSeparator(Menu), - wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Show actor in Viewer "), - [{enable, false}]), - wxMenu:appendSeparator(Menu), + _ = wxMenu:appendSeparator(Menu), + _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Show actor in Viewer "), + [{enable, false}]), + _ = wxMenu:appendSeparator(Menu), Show = menuitem(Menu, ?wxID_ANY, "From=To (F|T|B)", {show, [From]}), [Show,Hide]; true -> - wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Hide actor in Viewer "), - [{enable, false}]), - wxMenu:appendSeparator(Menu), + _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Hide actor in Viewer "), + [{enable, false}]), + _ = wxMenu:appendSeparator(Menu), Hide = [menuitem(Menu, ?wxID_ANY, "From (f)", {hide, [From]}), menuitem(Menu, ?wxID_ANY, "To (t)", {hide, [To]}), menuitem(Menu, ?wxID_ANY, "Both (b)", {hide, [From, To]})], - wxMenu:appendSeparator(Menu), - wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Show actor in Viewer "), - [{enable, false}]), - wxMenu:appendSeparator(Menu), + _ = wxMenu:appendSeparator(Menu), + _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Show actor in Viewer "), + [{enable, false}]), + _ = wxMenu:appendSeparator(Menu), Show = [menuitem(Menu, ?wxID_ANY, "From (F)", {show, [From]}), menuitem(Menu, ?wxID_ANY, "To (T)", {show, [To]}), menuitem(Menu, ?wxID_ANY, "Both (B)", {show, [From, To]})], Show++Hide end, - wxMenuBar:append(Bar, Menu, "Hide"), + _ = wxMenuBar:append(Bar, Menu, "Hide"), MenuData. create_search_menu(Bar, S) -> @@ -549,9 +553,9 @@ create_search_menu(Bar, S) -> E = S#state.filtered_event, From = E#event.from, To = E#event.to, - wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Search in Viewer "), - [{enable, false}]), - wxMenu:appendSeparator(Menu), + _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Search in Viewer "), + [{enable, false}]), + _ = wxMenu:appendSeparator(Menu), MenuData = if S#state.viewer_pid =:= undefined -> @@ -571,7 +575,7 @@ create_search_menu(Bar, S) -> menuitem(Menu, ?wxID_ANY, "Reverse from this event (r)", {mode, ModeR}), menuitem(Menu, ?wxID_ANY, "Abort search. Display all (a)", {mode, all})] end, - wxMenuBar:append(Bar, Menu, "Search"), + _ = wxMenuBar:append(Bar, Menu, "Search"), MenuData. config_editor(Editor, S) -> @@ -668,9 +672,10 @@ pad_string(String, MinLen, Char, Dir) when is_integer(MinLen), MinLen >= 0 -> send_viewer_event(S, Event) -> case S#state.viewer_pid of ViewerPid when is_pid(ViewerPid) -> - ViewerPid ! {et, Event}; + ViewerPid ! {et, Event}, + ok; undefined -> - ignore + ok end. select_file(Frame, Message, DefaultFile, Style) -> diff --git a/lib/et/src/et_wx_viewer.erl b/lib/et/src/et_wx_viewer.erl index 0fc10cb37b..9613299e6b 100644 --- a/lib/et/src/et_wx_viewer.erl +++ b/lib/et/src/et_wx_viewer.erl @@ -320,8 +320,8 @@ init([S]) when is_record(S, state) -> undefined -> ok; ParentPid -> link(ParentPid) end, - wx:new(), - wx:debug(S#state.wx_debug), + _ = wx:new(), + _ = wx:debug(S#state.wx_debug), et_collector:dict_insert(S#state.collector_pid, {subscriber, self()}, ?MODULE), @@ -532,26 +532,25 @@ handle_info(#wx{id=Id, event = #wxCommand{type = command_menu_selected}}, S=#sta load_all -> Style = ?wxFD_OPEN bor ?wxFD_OVERWRITE_PROMPT, Msg = "Select a file to load events from", - S2 = - case select_file(S#state.frame, Msg, S#state.event_file, Style) of - {ok, NewFile} -> - et_collector:start_trace_client(CollectorPid, event_file, NewFile), - S#state{event_file = NewFile}; - cancel -> - S - end, + S2 = case select_file(S#state.frame, Msg, S#state.event_file, Style) of + {ok, NewFile} -> + _ = et_collector:start_trace_client(CollectorPid, event_file, NewFile), + S#state{event_file = NewFile}; + cancel -> + S + end, noreply(S2); save_all -> Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT, Msg = "Select a file to save events to", - S2 = - case select_file(S#state.frame, Msg, S#state.event_file, Style) of - {ok, NewFile} -> - et_collector:save_event_file(CollectorPid, NewFile, [existing, write, keep]), - S#state{event_file = NewFile}; - cancel -> - S - end, + S2 = case select_file(S#state.frame, Msg, S#state.event_file, Style) of + {ok, NewFile} -> + ok = et_collector:save_event_file(CollectorPid, NewFile, + [existing, write, keep]), + S#state{event_file = NewFile}; + cancel -> + S + end, noreply(S2); print_setup -> S2 = print_setup(S), @@ -787,7 +786,7 @@ handle_info(#wx{event = #wxKey{keyCode = KeyCode, shiftDown = SD}}, S) -> noreply(S) end; handle_info(#wx{event = #wxScroll{type = scroll_changed}} = Wx, S) -> - get_latest_scroll(Wx), + _ = get_latest_scroll(Wx), Pos = wxScrollBar:getThumbPosition(S#state.scroll_bar), {_, LineTopY, LineBotY} = calc_y(S), Range = LineBotY - LineTopY, @@ -1107,7 +1106,7 @@ pick_n([Head | Tail], N, Acc) when N > 0 -> pick_n(Tail, N - 1, [Head | Acc]). close_all(S) -> - close_all_others(S), + _ = close_all_others(S), wxFrame:destroy(S#state.frame), opt_unlink(S#state.parent_pid), {stop, shutdown, S}. @@ -1115,12 +1114,12 @@ close_all(S) -> close_all_others(S) -> Fun = fun({{subscriber, Pid}, _}) -> - if - Pid =:= self() -> - ignore; + if Pid =:= self() -> + ok; true -> unlink(Pid), - Pid ! {et, close} + Pid ! {et, close}, + ok end end, All = et_collector:dict_match(S#state.collector_pid, @@ -1208,9 +1207,9 @@ create_main_window(S) -> CheckBoxData = [{wxCheckBox:getId(HideActions), hide_actions}, {wxCheckBox:getId(HideActors), hide_actors}], wxPanel:connect(Panel, command_checkbox_clicked), - wxSizer:add(CheckSizer, HideActions), - wxSizer:add(CheckSizer,HideActors), - wxSizer:add(OptSizer, CheckSizer, [{border, 10}, {flag, ?wxALL}]), + _ = wxSizer:add(CheckSizer, HideActions), + _ = wxSizer:add(CheckSizer,HideActors), + _ = wxSizer:add(OptSizer, CheckSizer, [{border, 10}, {flag, ?wxALL}]), DetailLevelBox = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, [{label, "Detail level"}]), @@ -1222,22 +1221,21 @@ create_main_window(S) -> {size, {200,-1}}]), wxStatusBar:setStatusText(StatusBar, where_text(S)), wxFrame:connect(Frame, command_slider_updated), - wxSizer:add(DetailLevelBox, DetailLevel), - wxSizer:add(OptSizer, DetailLevelBox, [{border, 10}, {flag, ?wxALL}]), - wxSizer:addStretchSpacer(OptSizer), - wxSizer:add(MainSizer, OptSizer), - wxSizer:add(MainSizer, - wxStaticLine:new(Panel, [{style, ?wxLI_HORIZONTAL}]), - [{flag, ?wxEXPAND}]), + _ = wxSizer:add(DetailLevelBox, DetailLevel), + _ = wxSizer:add(OptSizer, DetailLevelBox, [{border, 10}, {flag, ?wxALL}]), + _ = wxSizer:addStretchSpacer(OptSizer), + _ = wxSizer:add(MainSizer, OptSizer), + _ = wxSizer:add(MainSizer, wxStaticLine:new(Panel, [{style, ?wxLI_HORIZONTAL}]), + [{flag, ?wxEXPAND}]), CanvasSizer = wxBoxSizer:new(?wxHORIZONTAL), Canvas = wxPanel:new(Panel, [{style, ?wxFULL_REPAINT_ON_RESIZE}]), {CanvasW,CanvasH} = wxPanel:getSize(Canvas), ScrollBar = wxScrollBar:new(Panel, ?wxID_ANY, [{style, ?wxSB_VERTICAL}]), - wxSizer:add(CanvasSizer, Canvas, [{flag, ?wxEXPAND}, {proportion, 1}]), - wxSizer:add(CanvasSizer, ScrollBar, [{flag, ?wxEXPAND}]), - wxSizer:add(MainSizer, CanvasSizer, [{flag, ?wxEXPAND}, {proportion, 1}]), + _ = wxSizer:add(CanvasSizer, Canvas, [{flag, ?wxEXPAND}, {proportion, 1}]), + _ = wxSizer:add(CanvasSizer, ScrollBar, [{flag, ?wxEXPAND}]), + _ = wxSizer:add(MainSizer, CanvasSizer, [{flag, ?wxEXPAND}, {proportion, 1}]), wxPanel:connect(Canvas, left_down), wxPanel:connect(Canvas, left_up), wxPanel:connect(Canvas, right_up), @@ -1337,8 +1335,7 @@ menuitem(Menu, Id, Text, UserData) -> create_file_menu(Bar) -> Menu = wxMenu:new([]), - Data = [ - menuitem(Menu, ?wxID_ANY, "Clear all events in the Collector", clear_all), + Data = [menuitem(Menu, ?wxID_ANY, "Clear all events in the Collector", clear_all), menuitem(Menu, ?wxID_ANY, "Load events to the Collector from file", load_all), menuitem(Menu, ?wxID_ANY, "Save all events in the Collector to file", save_all), @@ -1348,51 +1345,44 @@ create_file_menu(Bar) -> menuitem(Menu, ?wxID_ANY, "Close this Viewer", close), menuitem(Menu, ?wxID_ANY, "Close all other Viewers, but this (c)", close_all_others), - menuitem(Menu, ?wxID_ANY, "Close all Viewers and the Collector) (C) ", close_all) - ], - wxMenu:insertSeparator(Menu, 3), - wxMenu:insertSeparator(Menu, 7), + menuitem(Menu, ?wxID_ANY, "Close all Viewers and the Collector) (C) ", close_all)], + _ = wxMenu:insertSeparator(Menu, 3), + _ = wxMenu:insertSeparator(Menu, 7), wxMenuBar:append(Bar, Menu, "File"), Data. create_viewer_menu(Bar) -> Menu = wxMenu:new([]), - wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Scroll this Viewer"), - [{enable, false}]), - wxMenu:appendSeparator(Menu), - D1 = [ - menuitem(Menu, ?wxID_ANY, "First (f)", first), + _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Scroll this Viewer"), + [{enable, false}]), + _ = wxMenu:appendSeparator(Menu), + D1 = [menuitem(Menu, ?wxID_ANY, "First (f)", first), menuitem(Menu, ?wxID_ANY, "Last (l)", last), menuitem(Menu, ?wxID_ANY, "Prev (p)", prev), menuitem(Menu, ?wxID_ANY, "Next (n)", next), - menuitem(Menu, ?wxID_ANY, "Refresh (r)", refresh) - ], - wxMenu:appendSeparator(Menu), - D2 = [ - menuitem(Menu, ?wxID_ANY, "Up 5 (Up)", up), - menuitem(Menu, ?wxID_ANY, "Down 5 (Down)", down) - ], - wxMenu:appendSeparator(Menu), - wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Actor visibility in this Viewer"), - [{enable, false}]), - wxMenu:appendSeparator(Menu), + menuitem(Menu, ?wxID_ANY, "Refresh (r)", refresh)], + _ = wxMenu:appendSeparator(Menu), + D2 = [menuitem(Menu, ?wxID_ANY, "Up 5 (Up)", up), + menuitem(Menu, ?wxID_ANY, "Down 5 (Down)", down)], + _ = wxMenu:appendSeparator(Menu), + _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Actor visibility in this Viewer"), + [{enable, false}]), + _ = wxMenu:appendSeparator(Menu), D3 = [menuitem(Menu, ?wxID_ANY, "Display all actors (a)", display_all)], - wxMenuBar:append(Bar, Menu, "Viewer"), + _ = wxMenuBar:append(Bar, Menu, "Viewer"), [D1,D2,D3]. create_collector_menu(Bar) -> Menu = wxMenu:new([]), - wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Scroll all Viewers"), - [{enable, false}]), - wxMenu:appendSeparator(Menu), - Data = [ - menuitem(Menu, ?wxID_ANY, "First (F)", first_all), + _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Scroll all Viewers"), + [{enable, false}]), + _ = wxMenu:appendSeparator(Menu), + Data = [menuitem(Menu, ?wxID_ANY, "First (F)", first_all), menuitem(Menu, ?wxID_ANY, "Last (L)", last_all), menuitem(Menu, ?wxID_ANY, "Prev (P)", prev_all), menuitem(Menu, ?wxID_ANY, "Next (N)", next_all), - menuitem(Menu, ?wxID_ANY, "Refresh (R)", refresh_all) - ], - wxMenuBar:append(Bar, Menu, "Collector"), + menuitem(Menu, ?wxID_ANY, "Refresh (R)", refresh_all)], + _ = wxMenuBar:append(Bar, Menu, "Collector"), Data. create_filter_menu(S=#state{filter_menu = {Menu,Data}}, ActiveFilterName, Filters) -> @@ -1421,21 +1411,19 @@ create_filter_menu(S=#state{filter_menu = {Menu,Data}}, ActiveFilterName, Filter Same = lists:concat([pad_string(ActiveFilterName, 20), "(=) same scale"]), Larger = lists:concat([pad_string(ActiveFilterName, 20), "(+) bigger scale"]), Smaller = lists:concat([pad_string(ActiveFilterName, 20), "(-) smaller scale"]), - D2 = [ - menuitem(Menu, ?wxID_ANY, Same, {data, Filter, 0}), + D2 = [menuitem(Menu, ?wxID_ANY, Same, {data, Filter, 0}), menuitem(Menu, ?wxID_ANY, Smaller, {data, Filter, -1}), menuitem(Menu, ?wxID_ANY, Larger, {data, Filter, 1}), wxMenu:appendSeparator(Menu), I2 = wxMenu:append(Menu, ?wxID_ANY, "New Filter Same Scale"), - wxMenu:appendSeparator(Menu) - ], - wxMenuItem:enable(I2, [{enable,false}]), + wxMenu:appendSeparator(Menu)], + _ = wxMenuItem:enable(I2, [{enable,false}]), {_,D3} = lists:foldl(Item, {1,[]}, Filters), S#state{filter_menu = {Menu, lists:flatten([D1,D2,D3])}}. create_help_menu(Bar) -> Menu = wxMenu:new([]), - menuitem(Menu, ?wxID_HELP, "Info", help), + _ = menuitem(Menu, ?wxID_HELP, "Info", help), wxMenuBar:append(Bar, Menu, "Help"). clear_canvas(S) -> diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 9453ca6c6f..f649c6e599 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -154,8 +154,6 @@ type(M, F, A, Xs) -> erl_types:erl_type(). %%-- erlang ------------------------------------------------------------------- -type(erlang, halt, 0, _, _) -> t_none(); -type(erlang, halt, 1, _, _) -> t_none(); type(erlang, halt, 2, _, _) -> t_none(); type(erlang, exit, 1, _, _) -> t_none(); type(erlang, error, 1, _, _) -> t_none(); @@ -2341,10 +2339,6 @@ arg_types(erlang, bit_size, 1) -> %% Guard bif, needs to be here. arg_types(erlang, byte_size, 1) -> [t_bitstr()]; -arg_types(erlang, halt, 0) -> - []; -arg_types(erlang, halt, 1) -> - [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()])]; arg_types(erlang, halt, 2) -> [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()]), t_list(t_tuple([t_atom('flush'), t_boolean()]))]; diff --git a/lib/hipe/icode/Makefile b/lib/hipe/icode/Makefile index a5edb10d90..c86562a981 100644 --- a/lib/hipe/icode/Makefile +++ b/lib/hipe/icode/Makefile @@ -59,7 +59,7 @@ DOC_MODULES = hipe_beam_to_icode \ hipe_icode_pp hipe_icode_primops \ hipe_icode_range \ hipe_icode_split_arith \ - hipe_icode_ssa hipe_icode_ssa_const_prop \ + hipe_icode_ssa hipe_icode_ssa_const_prop hipe_icode_call_elim \ hipe_icode_ssa_copy_prop hipe_icode_ssa_struct_reuse \ hipe_icode_type $(HIPE_MODULES) diff --git a/lib/hipe/icode/hipe_icode_call_elim.erl b/lib/hipe/icode/hipe_icode_call_elim.erl new file mode 100644 index 0000000000..6a22133962 --- /dev/null +++ b/lib/hipe/icode/hipe_icode_call_elim.erl @@ -0,0 +1,78 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +%%---------------------------------------------------------------------- +%% File : hipe_icode_call_elim.erl +%% Authors : Daniel S. McCain <[email protected]>, +%% Magnus Lång <[email protected]> +%% Created : 14 Apr 2014 by Magnus Lång <[email protected]> +%% Purpose : Eliminate calls to BIFs that are side-effect free only when +%% executed on some argument types. +%%---------------------------------------------------------------------- +-module(hipe_icode_call_elim). +-export([cfg/1]). + +-include("hipe_icode.hrl"). +-include("../flow/cfg.hrl"). + +-spec cfg(cfg()) -> cfg(). + +cfg(IcodeSSA) -> + lists:foldl(fun (Lbl, CFG1) -> + BB1 = hipe_icode_cfg:bb(CFG1, Lbl), + Code1 = hipe_bb:code(BB1), + Code2 = lists:map(fun elim_insn/1, Code1), + BB2 = hipe_bb:code_update(BB1, Code2), + hipe_icode_cfg:bb_add(CFG1, Lbl, BB2) + end, IcodeSSA, hipe_icode_cfg:labels(IcodeSSA)). + +-spec elim_insn(icode_instr()) -> icode_instr(). +elim_insn(Insn=#icode_call{'fun'={_,_,_}=MFA, args=Args, type=remote, + dstlist=[Dst=#icode_variable{ + annotation={type_anno, RetType, _}}]}) -> + Opaques = 'universe', + case erl_types:t_is_singleton(RetType, Opaques) of + true -> + ArgTypes = [case Arg of + #icode_variable{annotation={type_anno, Type, _}} -> Type; + #icode_const{} -> + erl_types:t_from_term(hipe_icode:const_value(Arg)) + end || Arg <- Args], + case can_be_eliminated(MFA, ArgTypes) of + true -> + Const = hipe_icode:mk_const( + erl_types:t_singleton_to_term(RetType, Opaques)), + #icode_move{dst=Dst, src=Const}; + false -> Insn + end; + false -> Insn + end; +elim_insn(Insn) -> Insn. + + +%% A function can be eliminated for some argument types if it has no side +%% effects when run on arguments of those types. + +-spec can_be_eliminated(mfa(), [erl_types:erl_type()]) -> boolean(). + +can_be_eliminated({maps, is_key, 2}, [_K, M]) -> + erl_types:t_is_map(M); +can_be_eliminated(_, _) -> + false. diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl index e3ba00c5e9..5eae8d440a 100644 --- a/lib/hipe/icode/hipe_icode_type.erl +++ b/lib/hipe/icode/hipe_icode_type.erl @@ -363,6 +363,7 @@ call_always_fails(#icode_call{} = I, Info) -> %% These can actually be calls too. {erlang, halt, 0} -> false; {erlang, halt, 1} -> false; + {erlang, halt, 2} -> false; {erlang, exit, 1} -> false; {erlang, error, 1} -> false; {erlang, error, 2} -> false; diff --git a/lib/hipe/llvm/Makefile b/lib/hipe/llvm/Makefile index d2d39fb9e3..25b47a580f 100644 --- a/lib/hipe/llvm/Makefile +++ b/lib/hipe/llvm/Makefile @@ -40,12 +40,12 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN) # Target Specs # ---------------------------------------------------- ifdef HIPE_ENABLED -HIPE_MODULES = hipe_rtl_to_llvm \ +HIPE_MODULES = elf_format \ hipe_llvm \ - elf_format \ + hipe_llvm_liveness \ hipe_llvm_main \ hipe_llvm_merge \ - hipe_llvm_liveness + hipe_rtl_to_llvm else HIPE_MODULES = endif @@ -71,7 +71,7 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) include ../native.mk -ERL_COMPILE_FLAGS += +inline #+warn_missing_spec +ERL_COMPILE_FLAGS += +inline +warn_export_vars #+warn_missing_spec # if in 32 bit backend define BIT32 symbol ARCH = $(shell echo $(TARGET) | sed 's/^\(x86_64\)-.*/64bit/') @@ -108,3 +108,11 @@ release_spec: opt $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin release_docs_spec: + +$(EBIN)/elf_format.beam: elf_format.hrl elf32_format.hrl elf64_format.hrl +$(EBIN)/hipe_llvm_main.beam: ../../kernel/src/hipe_ext_format.hrl \ + hipe_llvm_arch.hrl elf_format.hrl elf32_format.hrl elf64_format.hrl +$(EBIN)/hipe_llvm_merge.beam: ../../kernel/src/hipe_ext_format.hrl \ + hipe_llvm_arch.hrl ../rtl/hipe_literals.hrl ../main/hipe.hrl +$(EBIN)/hipe_rtl_to_llvm.beam: ../rtl/hipe_rtl.hrl ../rtl/hipe_literals.hrl \ + hipe_llvm_arch.hrl diff --git a/lib/hipe/llvm/elf_format.erl b/lib/hipe/llvm/elf_format.erl index 260da9b5e6..8cf6ea6250 100644 --- a/lib/hipe/llvm/elf_format.erl +++ b/lib/hipe/llvm/elf_format.erl @@ -13,21 +13,20 @@ -module(elf_format). --export([get_tab_entries/1, - %% Relocations - get_rodata_relocs/1, - get_text_relocs/1, +-export([%% Relocations extract_rela/2, - get_rela_addends/1, %% Note extract_note/2, %% Executable code extract_text/1, %% GCC Exception Table - get_exn_handlers/1, - %% Misc. - set_architecture_flag/1, - is64bit/0 + get_exn_handlers/1, + %% Symbols + elf_symbols/1, + %% Sections + section_contents/2, + %% Main interface + read/1 ]). -include("elf_format.hrl"). @@ -36,27 +35,57 @@ %% Types %%------------------------------------------------------------------------------ --type elf() :: binary(). - --type lp() :: non_neg_integer(). % landing pad --type num() :: non_neg_integer(). --type index() :: non_neg_integer(). --type offset() :: non_neg_integer(). --type size() :: non_neg_integer(). --type start() :: non_neg_integer(). - --type info() :: index(). --type nameoff() :: offset(). --type valueoff() :: offset(). - --type name() :: string(). --type name_size() :: {name(), size()}. --type name_sizes() :: [name_size()]. +-export_type([elf/0 + ,addend/0 + ,bitflags/0 + ,name/0 + ,offset/0 + ,reloc_type/0 + ,shdr_type/0 + ,size/0 + ,sym_bind/0 + ,sym_type/0 + ,valueoff/0 + ]). + +-type bitflags() :: non_neg_integer(). +-type index() :: non_neg_integer(). +-type lp() :: non_neg_integer(). % landing pad +-type num() :: non_neg_integer(). +-type offset() :: non_neg_integer(). +-type size() :: non_neg_integer(). +-type start() :: non_neg_integer(). + +-type addend() :: integer() | undefined. +-type name() :: string(). +-type shdr_type() :: 'null' | 'progbits' | 'symtab' | 'strtab' | 'rela' + | 'hash' | 'dynamic' | 'note' | 'nobits' | 'rel' | 'shlib' + | 'dynsym' | {os, ?SHT_LOOS..?SHT_HIOS} + | {proc, ?SHT_LOPROC..?SHT_HIPROC}. +-type sym_bind() :: 'local' | 'global' | 'weak' | {os, ?STB_LOOS..?STB_HIOS} + | {proc, ?STB_LOPROC..?STB_HIPROC}. +-type sym_type() :: 'notype' | 'object' | 'func' | 'section' | 'file' + | {os, ?STT_LOOS..?STT_HIOS} + | {proc, ?STT_LOPROC..?STT_HIPROC}. +-type valueoff() :: offset(). + +-ifdef(BIT32). % 386 +-type reloc_type() :: '32' | 'pc32'. +-else. % X86_64 +-type reloc_type() :: '64' | 'pc32' | '32'. +-endif. %%------------------------------------------------------------------------------ %% Abstract Data Types and Accessors for ELF Structures. %%------------------------------------------------------------------------------ +-record(elf, {file :: binary() + ,sections :: [elf_shdr()] + ,sec_nam :: #{string() => elf_shdr()} + ,symbols :: undefined | [elf_sym()] + }). +-opaque elf() :: #elf{}. + %% File header -record(elf_ehdr, {ident, % ELF identification type, % Object file type @@ -85,42 +114,6 @@ }). %% -type elf_ehdr_ident() :: #elf_ehdr_ident{}. -%% Section header entries --record(elf_shdr, {name, % Section name - type, % Section type - flags, % Section attributes - addr, % Virtual address in memory - offset :: offset(), % Offset in file - size :: size(), % Size of section - link, % Link to other section - info, % Miscellaneous information - addralign, % Address align boundary - entsize % Size of entries, if section has table - }). -%% -type elf_shdr() :: #elf_shdr{}. - -%% Symbol table entries --record(elf_sym, {name :: nameoff(), % Symbol name - info, % Type and Binding attributes - other, % Reserved - shndx, % Section table index - value :: valueoff(), % Symbol value - size :: size() % Size of object - }). --type elf_sym() :: #elf_sym{}. - -%% Relocations --record(elf_rel, {r_offset :: offset(), % Address of reference - r_info :: info() % Symbol index and type of relocation - }). --type elf_rel() :: #elf_rel{}. - --record(elf_rela, {r_offset :: offset(), % Address of reference - r_info :: info(), % Symbol index and type of relocation - r_addend :: offset() % Constant part of expression - }). --type elf_rela() :: #elf_rela{}. - %% %% Program header table %% -record(elf_phdr, {type, % Type of segment %% flags, % Segment attributes @@ -199,44 +192,19 @@ mk_shdr(Name, Type, Flags, Addr, Offset, Size, Link, Info, AddrAlign, EntSize) - %%%------------------------- %%% Symbol Table Entries %%%------------------------- -mk_sym(Name, Info, Other, Shndx, Value, Size) -> - #elf_sym{name = Name, info = Info, other = Other, - shndx = Shndx, value = Value, size = Size}. - --spec sym_name(elf_sym()) -> nameoff(). -sym_name(#elf_sym{name = Name}) -> Name. +mk_sym(Name, Bind, Type, Section, Value, Size) -> + #elf_sym{name = Name, bind = Bind, type = Type, + section = Section, value = Value, size = Size}. +%% -spec sym_name(elf_sym()) -> string(). +%% sym_name(#elf_sym{name = Name}) -> Name. +%% %% -spec sym_value(elf_sym()) -> valueoff(). %% sym_value(#elf_sym{value = Value}) -> Value. %% %% -spec sym_size(elf_sym()) -> size(). %% sym_size(#elf_sym{size = Size}) -> Size. -%%%------------------------- -%%% Relocations -%%%------------------------- --spec mk_rel(offset(), info()) -> elf_rel(). -mk_rel(Offset, Info) -> - #elf_rel{r_offset = Offset, r_info = Info}. - -%% The following two functions capitalize on the fact that the two kinds of -%% relocation records (for 32- and 64-bit architectures have similar structure. - --spec r_offset(elf_rel() | elf_rela()) -> offset(). -r_offset(#elf_rel{r_offset = Offset}) -> Offset; -r_offset(#elf_rela{r_offset = Offset}) -> Offset. - --spec r_info(elf_rel() | elf_rela()) -> info(). -r_info(#elf_rel{r_info = Info}) -> Info; -r_info(#elf_rela{r_info = Info}) -> Info. - --spec mk_rela(offset(), info(), offset()) -> elf_rela(). -mk_rela(Offset, Info, Addend) -> - #elf_rela{r_offset = Offset, r_info = Info, r_addend = Addend}. - --spec rela_addend(elf_rela()) -> offset(). -rela_addend(#elf_rela{r_addend = Addend}) -> Addend. - %% %%%------------------------- %% %%% GCC exception table %% %%%------------------------- @@ -263,15 +231,30 @@ mk_gccexntab_callsite(Start, Size, LP, Action) -> %% gccexntab_callsite_lp(#elf_gccexntab_callsite{lp = LP}) -> LP. %%------------------------------------------------------------------------------ +%% Main interface function +%%------------------------------------------------------------------------------ + +%% @doc Parses an ELF file. +-spec read(binary()) -> elf(). +read(ElfBin) -> + Header = extract_header(ElfBin), + [_UndefinedSec|Sections] = extract_shdrtab(ElfBin, Header), + SecNam = maps:from_list( + [{Name, Sec} || Sec = #elf_shdr{name=Name} <- Sections]), + Elf0 = #elf{file=ElfBin, sections=Sections, sec_nam=SecNam}, + [_UndefinedSym|Symbols] = extract_symtab(Elf0, extract_strtab(Elf0)), + Elf0#elf{symbols=Symbols}. + +%%------------------------------------------------------------------------------ %% Functions to manipulate the ELF File Header %%------------------------------------------------------------------------------ %% @doc Extracts the File Header from an ELF formatted object file. Also sets %% the ELF class variable in the process dictionary (used by many functions %% in this and hipe_llvm_main modules). --spec extract_header(elf()) -> elf_ehdr(). -extract_header(Elf) -> - Ehdr_bin = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE), +-spec extract_header(binary()) -> elf_ehdr(). +extract_header(ElfBin) -> + Ehdr_bin = get_binary_segment(ElfBin, 0, ?ELF_EHDR_SIZE), << %% Structural pattern matching on fields. Ident_bin:?E_IDENT_SIZE/binary, Type:?bits(?E_TYPE_SIZE)/integer-little, @@ -300,19 +283,28 @@ extract_header(Elf) -> %% Functions to manipulate Section Header Entries %%------------------------------------------------------------------------------ +-type shdrtab() :: [elf_shdr()]. + %% @doc Extracts the Section Header Table from an ELF formated Object File. -extract_shdrtab(Elf) -> - %% Extract File Header to get info about Section Header Offset (in bytes), - %% Entry Size (in bytes) and Number of entries - #elf_ehdr{shoff = ShOff, shentsize = ShEntsize, shnum = ShNum} = - extract_header(Elf), +-spec extract_shdrtab(binary(), elf_ehdr()) -> shdrtab(). +extract_shdrtab(ElfBin, #elf_ehdr{shoff=ShOff, shentsize=?ELF_SHDRENTRY_SIZE, + shnum=ShNum, shstrndx=ShStrNdx}) -> %% Get actual Section header table (binary) - ShdrBin = get_binary_segment(Elf, ShOff, ShNum * ShEntsize), - get_shdrtab_entries(ShdrBin, []). - -get_shdrtab_entries(<<>>, Acc) -> - lists:reverse(Acc); -get_shdrtab_entries(ShdrBin, Acc) -> + ShdrBin = get_binary_segment(ElfBin, ShOff, ShNum * ?ELF_SHDRENTRY_SIZE), + %% We need to lookup the offset and size of the section header string table + %% before we can fully parse the section table. We compute its offset and + %% extract the fields we need here. + ShStrEntryOffset = ShStrNdx * ?ELF_SHDRENTRY_SIZE, + <<_:ShStrEntryOffset/binary, _:?SH_NAME_SIZE/binary, + _:?SH_TYPE_SIZE/binary, _:?SH_FLAGS_SIZE/binary, _:?SH_ADDR_SIZE/binary, + ShStrOffset:?bits(?SH_OFFSET_SIZE)/little, + ShStrSize:?bits(?SH_SIZE_SIZE)/little, + _/binary>> = ShdrBin, + ShStrTab = parse_strtab(get_binary_segment(ElfBin, ShStrOffset, ShStrSize)), + get_shdrtab_entries(ShdrBin, ShStrTab). + +get_shdrtab_entries(<<>>, _ShStrTab) -> []; +get_shdrtab_entries(ShdrTab, ShStrTab) -> <<%% Structural pattern matching on fields. Name:?bits(?SH_NAME_SIZE)/integer-little, Type:?bits(?SH_TYPE_SIZE)/integer-little, @@ -324,205 +316,166 @@ get_shdrtab_entries(ShdrBin, Acc) -> Info:?bits(?SH_INFO_SIZE)/integer-little, Addralign:?bits(?SH_ADDRALIGN_SIZE)/integer-little, Entsize:?bits(?SH_ENTSIZE_SIZE)/integer-little, - MoreShdrE/binary - >> = ShdrBin, - ShdrE = mk_shdr(Name, Type, Flags, Addr, Offset, - Size, Link, Info, Addralign, Entsize), - get_shdrtab_entries(MoreShdrE, [ShdrE | Acc]). - -%% @doc Extracts a specific Entry of a Section Header Table. This function -%% takes as argument the Section Header Table (`SHdrTab') and the entry's -%% serial number (`EntryNum') and returns the entry (`shdr'). -get_shdrtab_entry(SHdrTab, EntryNum) -> - lists:nth(EntryNum + 1, SHdrTab). - -%%------------------------------------------------------------------------------ -%% Functions to manipulate Section Header String Table -%%------------------------------------------------------------------------------ - -%% @doc Extracts the Section Header String Table. This section is not a known -%% ELF Object File section. It is just a "hidden" table storing the -%% names of all sections that exist in current object file. --spec extract_shstrtab(elf()) -> [name()]. -extract_shstrtab(Elf) -> - %% Extract Section Name String Table Index - #elf_ehdr{shstrndx = ShStrNdx} = extract_header(Elf), - ShHdrTab = extract_shdrtab(Elf), - %% Extract Section header entry and get actual Section-header String Table - #elf_shdr{offset = ShStrOffset, size = ShStrSize} = - get_shdrtab_entry(ShHdrTab, ShStrNdx), - case get_binary_segment(Elf, ShStrOffset, ShStrSize) of - <<>> -> %% Segment empty - []; - ShStrTab -> %% Convert to string table - [Name || {Name, _Size} <- get_names(ShStrTab)] - end. - -%%------------------------------------------------------------------------------ - --spec get_tab_entries(elf()) -> [{name(), valueoff(), size()}]. -get_tab_entries(Elf) -> - SymTab = extract_symtab(Elf), - Ts = [{Name, Value, Size div ?ELF_XWORD_SIZE} - || #elf_sym{name = Name, value = Value, size = Size} <- SymTab, - Name =/= 0], - {NameIndices, ValueOffs, Sizes} = lists:unzip3(Ts), - %% Find the names of the symbols. - %% Get string table entries ([{Name, Offset in strtab section}]). Keep only - %% relevant entries: - StrTab = extract_strtab(Elf), - Relevant = [get_strtab_entry(StrTab, Off) || Off <- NameIndices], - %% Zip back to {Name, ValueOff, Size} - lists:zip3(Relevant, ValueOffs, Sizes). + Rest/binary + >> = ShdrTab, + Entry = mk_shdr(get_strtab_entry(Name, ShStrTab), decode_shdr_type(Type), + Flags, Addr, Offset, Size, Link, Info, Addralign, Entsize), + [Entry | get_shdrtab_entries(Rest, ShStrTab)]. + +decode_shdr_type(?SHT_NULL) -> 'null'; +decode_shdr_type(?SHT_PROGBITS) -> 'progbits'; +decode_shdr_type(?SHT_SYMTAB) -> 'symtab'; +decode_shdr_type(?SHT_STRTAB) -> 'strtab'; +decode_shdr_type(?SHT_RELA) -> 'rela'; +decode_shdr_type(?SHT_HASH) -> 'hash'; %unused +decode_shdr_type(?SHT_DYNAMIC) -> 'dynamic'; %unused +decode_shdr_type(?SHT_NOTE) -> 'note'; %unused +decode_shdr_type(?SHT_NOBITS) -> 'nobits'; +decode_shdr_type(?SHT_REL) -> 'rel'; +decode_shdr_type(?SHT_SHLIB) -> 'shlib'; %unused +decode_shdr_type(?SHT_DYNSYM) -> 'dynsym'; %unused +decode_shdr_type(OS) when ?SHT_LOOS =< OS, OS =< ?SHT_HIOS -> {os, OS}; +decode_shdr_type(Proc) when ?SHT_LOPROC =< Proc, Proc =< ?SHT_HIPROC -> + {proc, Proc}. + +-spec elf_section(non_neg_integer(), elf()) -> undefined | abs | elf_shdr(). +elf_section(0, #elf{}) -> undefined; +elf_section(?SHN_ABS, #elf{}) -> abs; +elf_section(Index, #elf{sections=SecIdx}) -> + lists:nth(Index, SecIdx). + +%% Reads the contents of a section from an object +-spec section_contents(elf_shdr(), elf()) -> binary(). +section_contents(#elf_shdr{offset=Offset, size=Size}, #elf{file=ElfBin}) -> + get_binary_segment(ElfBin, Offset, Size). %%------------------------------------------------------------------------------ %% Functions to manipulate Symbol Table %%------------------------------------------------------------------------------ %% @doc Function that extracts Symbol Table from an ELF Object file. -extract_symtab(Elf) -> - Symtab_bin = extract_segment_by_name(Elf, ?SYMTAB), - get_symtab_entries(Symtab_bin, []). - -get_symtab_entries(<<>>, Acc) -> - lists:reverse(Acc); -get_symtab_entries(Symtab_bin, Acc) -> - <<SymE_bin:?ELF_SYM_SIZE/binary, MoreSymE/binary>> = Symtab_bin, - case is64bit() of - true -> - <<%% Structural pattern matching on fields. - Name:?bits(?ST_NAME_SIZE)/integer-little, - Info:?bits(?ST_INFO_SIZE)/integer-little, - Other:?bits(?ST_OTHER_SIZE)/integer-little, - Shndx:?bits(?ST_SHNDX_SIZE)/integer-little, - Value:?bits(?ST_VALUE_SIZE)/integer-little, - Size:?bits(?ST_SIZE_SIZE)/integer-little - >> = SymE_bin; - false -> - << %% Same fields in different order: - Name:?bits(?ST_NAME_SIZE)/integer-little, - Value:?bits(?ST_VALUE_SIZE)/integer-little, - Size:?bits(?ST_SIZE_SIZE)/integer-little, - Info:?bits(?ST_INFO_SIZE)/integer-little, - Other:?bits(?ST_OTHER_SIZE)/integer-little, - Shndx:?bits(?ST_SHNDX_SIZE)/integer-little - >> = SymE_bin - end, - SymE = mk_sym(Name, Info, Other, Shndx, Value, Size), - get_symtab_entries(MoreSymE, [SymE | Acc]). - -%% @doc Extracts a specific entry from the Symbol Table (as binary). -%% This function takes as arguments the Symbol Table (`SymTab') -%% and the entry's serial number and returns that entry (`sym'). -get_symtab_entry(SymTab, EntryNum) -> - lists:nth(EntryNum + 1, SymTab). +extract_symtab(Elf, StrTab) -> + Symtab = extract_segment_by_name(Elf, ?SYMTAB), + [parse_sym(Sym, Elf, StrTab) || <<Sym:?ELF_SYM_SIZE/binary>> <= Symtab]. + +-ifdef(BIT32). +parse_sym(<<%% Structural pattern matching on fields. + Name:?bits(?ST_NAME_SIZE)/integer-little, + Value:?bits(?ST_VALUE_SIZE)/integer-little, + Size:?bits(?ST_SIZE_SIZE)/integer-little, + Info:?bits(?ST_INFO_SIZE)/integer-little, + _Other:?bits(?ST_OTHER_SIZE)/integer-little, + Shndx:?bits(?ST_SHNDX_SIZE)/integer-little>>, + Elf, StrTab) -> + mk_sym(get_strtab_entry(Name, StrTab), decode_symbol_bind(?ELF_ST_BIND(Info)), + decode_symbol_type(?ELF_ST_TYPE(Info)), elf_section(Shndx, Elf), Value, + Size). +-else. +parse_sym(<<%% Same fields in different order: + Name:?bits(?ST_NAME_SIZE)/integer-little, + Info:?bits(?ST_INFO_SIZE)/integer-little, + _Other:?bits(?ST_OTHER_SIZE)/integer-little, + Shndx:?bits(?ST_SHNDX_SIZE)/integer-little, + Value:?bits(?ST_VALUE_SIZE)/integer-little, + Size:?bits(?ST_SIZE_SIZE)/integer-little>>, + Elf, StrTab) -> + mk_sym(get_strtab_entry(Name, StrTab), decode_symbol_bind(?ELF_ST_BIND(Info)), + decode_symbol_type(?ELF_ST_TYPE(Info)), elf_section(Shndx, Elf), Value, + Size). +-endif. + +decode_symbol_bind(?STB_LOCAL) -> 'local'; +decode_symbol_bind(?STB_GLOBAL) -> 'global'; +decode_symbol_bind(?STB_WEAK) -> 'weak'; %unused +decode_symbol_bind(OS) when ?STB_LOOS =< OS, OS =< ?STB_HIOS -> {os, OS}; +decode_symbol_bind(Proc) when ?STB_LOPROC =< Proc, Proc =< ?STB_HIPROC -> + {proc, Proc}. + +decode_symbol_type(?STT_NOTYPE) -> 'notype'; +decode_symbol_type(?STT_OBJECT) -> 'object'; +decode_symbol_type(?STT_FUNC) -> 'func'; +decode_symbol_type(?STT_SECTION) -> 'section'; +decode_symbol_type(?STT_FILE) -> 'file'; +decode_symbol_type(OS) when ?STT_LOOS =< OS, OS =< ?STT_HIOS -> {os, OS}; +decode_symbol_type(Proc) when ?STT_LOPROC =< Proc, Proc =< ?STT_HIPROC -> + {proc, Proc}. + +%% @doc Extracts a specific entry from the Symbol Table. +-spec elf_symbol(0, elf()) -> undefined; + (pos_integer(), elf()) -> elf_sym(). +elf_symbol(0, #elf{}) -> undefined; +elf_symbol(Index, #elf{symbols=Symbols}) -> lists:nth(Index, Symbols). + +-spec elf_symbols(elf()) -> [elf_sym()]. +elf_symbols(#elf{symbols=Symbols}) -> Symbols. %%------------------------------------------------------------------------------ %% Functions to manipulate String Table %%------------------------------------------------------------------------------ +%% ADT: get_strtab_entry/1 must be used to consume this type. +-type strtab() :: binary(). + %% @doc Extracts String Table from an ELF formated Object File. --spec extract_strtab(elf()) -> [{string(), offset()}]. +-spec extract_strtab(elf()) -> strtab(). extract_strtab(Elf) -> - Strtab_bin = extract_segment_by_name(Elf, ?STRTAB), - NamesSizes = get_names(Strtab_bin), - make_offsets(NamesSizes). - -%% @doc Returns the name of the symbol at the given offset. The string table -%% contains entries of the form {Name, Offset}. If no such offset exists -%% returns the empty string (`""'). -%% XXX: There might be a bug here because of the "compact" saving the ELF -%% format uses: e.g. only stores ".rela.text" for ".rela.text" and ".text". -get_strtab_entry(Strtab, Offset) -> - case lists:keyfind(Offset, 2, Strtab) of - {Name, Offset} -> Name; - false -> "" - end. + parse_strtab(extract_segment_by_name(Elf, ?STRTAB)). + +-spec parse_strtab(binary()) -> strtab(). +parse_strtab(StrTabSectionBin) -> StrTabSectionBin. + +%% @doc Returns the name of the symbol at the given offset. +-spec get_strtab_entry(non_neg_integer(), strtab()) -> string(). +get_strtab_entry(Offset, StrTab) -> + <<_:Offset/binary, StrBin/binary>> = StrTab, + bin_get_string(StrBin). + +%% @doc Extracts a null-terminated string from a binary. +-spec bin_get_string(binary()) -> string(). +%% FIXME: No regard for encoding (just happens to work for ASCII and Latin-1) +bin_get_string(<<0, _/binary>>) -> []; +bin_get_string(<<Char, Rest/binary>>) -> [Char|bin_get_string(Rest)]. %%------------------------------------------------------------------------------ %% Functions to manipulate Relocations %%------------------------------------------------------------------------------ -%% @doc This function gets as argument an ELF binary file and returns a list -%% with all .rela.rodata labels (i.e. constants and literals in code) -%% or an empty list if no ".rela.rodata" section exists in code. --spec get_rodata_relocs(elf()) -> [offset()]. -get_rodata_relocs(Elf) -> - case is64bit() of - true -> - %% Only care about the addends (== offsets): - get_rela_addends(extract_rela(Elf, ?RODATA)); - false -> - %% Find offsets hardcoded in ".rodata" entry - %%XXX: Treat all 0s as padding and skip them! - [SkipPadding || SkipPadding <- extract_rodata(Elf), SkipPadding =/= 0] - end. - --spec get_rela_addends([elf_rela()]) -> [offset()]. -get_rela_addends(RelaEntries) -> - [rela_addend(E) || E <- RelaEntries]. - -%% @doc Extract a list of the form `[{SymbolName, Offset}]' with all relocatable -%% symbols and their offsets in the code from the ".text" section. --spec get_text_relocs(elf()) -> [{name(), offset()}]. -get_text_relocs(Elf) -> - %% Only care about the symbol table index and the offset: - NameOffsetTemp = [{?ELF_R_SYM(r_info(E)), r_offset(E)} - || E <- extract_rela(Elf, ?TEXT)], - {NameIndices, ActualOffsets} = lists:unzip(NameOffsetTemp), - %% Find the names of the symbols: - %% - %% Get those symbol table entries that are related to Text relocs: - Symtab = extract_symtab(Elf), - SymtabEs = [get_symtab_entry(Symtab, Index) || Index <- NameIndices], - %XXX: not zero-indexed! - %% Symbol table entries contain the offset of the name of the symbol in - %% String Table: - SymtabEs2 = [sym_name(E) || E <- SymtabEs], %XXX: Do we need to sort SymtabE? - %% Get string table entries ([{Name, Offset in strtab section}]). Keep only - %% relevant entries: - Strtab = extract_strtab(Elf), - Relevant = [get_strtab_entry(Strtab, Off) || Off <- SymtabEs2], - %% Zip back with actual offsets: - lists:zip(Relevant, ActualOffsets). - %% @doc Extract the Relocations segment for section `Name' (that is passed %% as second argument) from an ELF formated Object file binary. --spec extract_rela(elf(), name()) -> [elf_rel() | elf_rela()]. +-spec extract_rela(elf(), name()) -> [elf_rel()]. + +-ifdef(BIT32). extract_rela(Elf, Name) -> - SegName = - case is64bit() of - true -> ?RELA(Name); % ELF-64 uses ".rela" - false -> ?REL(Name) % ...while ELF-32 uses ".rel" - end, - Rela_bin = extract_segment_by_name(Elf, SegName), - get_rela_entries(Rela_bin, []). - -get_rela_entries(<<>>, Acc) -> - lists:reverse(Acc); -get_rela_entries(Bin, Acc) -> - E = case is64bit() of - true -> - <<%% Structural pattern matching on fields of a Rela Entry. - Offset:?bits(?R_OFFSET_SIZE)/integer-little, - Info:?bits(?R_INFO_SIZE)/integer-little, - Addend:?bits(?R_ADDEND_SIZE)/integer-little, - Rest/binary - >> = Bin, - mk_rela(Offset, Info, Addend); - false -> - <<%% Structural pattern matching on fields of a Rel Entry. - Offset:?bits(?R_OFFSET_SIZE)/integer-little, - Info:?bits(?R_INFO_SIZE)/integer-little, - Rest/binary - >> = Bin, - mk_rel(Offset, Info) - end, - get_rela_entries(Rest, [E | Acc]). - -%% %% @doc Extract the `EntryNum' (serial number) Relocation Entry. -%% get_rela_entry(Rela, EntryNum) -> -%% lists:nth(EntryNum + 1, Rela). + SecData = extract_segment_by_name(Elf, Name), + [#elf_rel{offset=Offset, symbol=elf_symbol(?ELF_R_SYM(Info), Elf), + type=decode_reloc_type(?ELF_R_TYPE(Info)), + addend=read_implicit_addend(Offset, SecData)} + || <<Offset:?bits(?R_OFFSET_SIZE)/little, + Info:?bits(?R_INFO_SIZE)/little % 386 uses ".rel" + >> <= extract_segment_by_name(Elf, ?REL(Name))]. + +%% The only types HiPE knows how to patch +decode_reloc_type(1) -> '32'; +decode_reloc_type(2) -> 'pc32'. + +read_implicit_addend(Offset, Section) -> + %% All x86 relocation types uses 'word32' relocation fields; i.e. 32-bit LE. + <<_:Offset/binary, Addend:32/signed-little, _/binary>> = Section, + Addend. + +-else. %% BIT32 +extract_rela(Elf, Name) -> + [#elf_rel{offset=Offset, symbol=elf_symbol(?ELF_R_SYM(Info), Elf), + type=decode_reloc_type(?ELF_R_TYPE(Info)), addend=Addend} + || <<Offset:?bits(?R_OFFSET_SIZE)/little, + Info:?bits(?R_INFO_SIZE)/little, + Addend:?bits(?R_ADDEND_SIZE)/signed-little % X86_64 uses ".rela" + >> <= extract_segment_by_name(Elf, ?RELA(Name))]. + +decode_reloc_type(1) -> '64'; +decode_reloc_type(2) -> 'pc32'; +decode_reloc_type(10) -> '32'. +-endif. %% BIT32 %%------------------------------------------------------------------------------ %% Functions to manipulate Executable Code segment @@ -615,19 +568,6 @@ get_gccexntab_callsites(CSTab, Acc) -> get_gccexntab_callsites(More, [GccCS | Acc]). %%------------------------------------------------------------------------------ -%% Functions to manipulate Read-only Data (.rodata) -%%------------------------------------------------------------------------------ -extract_rodata(Elf) -> - Rodata_bin = extract_segment_by_name(Elf, ?RODATA), - get_rodata_entries(Rodata_bin, []). - -get_rodata_entries(<<>>, Acc) -> - lists:reverse(Acc); -get_rodata_entries(Rodata_bin, Acc) -> - <<Num:?bits(?ELF_ADDR_SIZE)/integer-little, More/binary>> = Rodata_bin, - get_rodata_entries(More, [Num | Acc]). - -%%------------------------------------------------------------------------------ %% Helper functions %%------------------------------------------------------------------------------ @@ -647,107 +587,15 @@ get_binary_segment(Bin, Offset, Size) -> %% There are handy macros defined in elf_format.hrl for all Standard %% Section Names. -spec extract_segment_by_name(elf(), string()) -> binary(). -extract_segment_by_name(Elf, SectionName) -> - %% Extract Section Header Table and Section Header String Table from binary - SHdrTable = extract_shdrtab(Elf), - Names = extract_shstrtab(Elf), - %% Zip to a list of (Name,ShdrE) - [_Zero | ShdrEs] = lists:keysort(2, SHdrTable), % Skip first entry (zeros). - L = lists:zip(Names, ShdrEs), +extract_segment_by_name(#elf{file=ElfBin, sec_nam=SecNam}, SectionName) -> %% Find Section Header Table entry by name - case lists:keyfind(SectionName, 1, L) of - {SectionName, ShdrE} -> %% Note: Same name. - #elf_shdr{offset = Offset, size = Size} = ShdrE, - get_binary_segment(Elf, Offset, Size); - false -> %% Not found. + case SecNam of + #{SectionName := #elf_shdr{offset=Offset, size=Size}} -> + get_binary_segment(ElfBin, Offset, Size); + #{} -> %% Not found. <<>> end. -%% @doc Extracts a list of strings with (zero-separated) names from a binary. -%% Returns tuples of `{Name, Size}'. -%% XXX: Skip trailing 0. --spec get_names(<<_:8,_:_*8>>) -> name_sizes(). -get_names(<<0, Bin/binary>>) -> - NamesSizes = get_names(Bin, []), - fix_names(NamesSizes, []). - -get_names(<<>>, Acc) -> - lists:reverse(Acc); -get_names(Bin, Acc) -> - {Name, MoreNames} = bin_get_string(Bin), - get_names(MoreNames, [{Name, length(Name)} | Acc]). - -%% @doc Fix names: -%% e.g. If ".rela.text" exists, ".text" does not. Same goes for -%% ".rel.text". In that way, the Section Header String Table is more -%% compact. Add ".text" just *before* the corresponding rela-field, -%% etc. --spec fix_names(name_sizes(), name_sizes()) -> name_sizes(). -fix_names([], Acc) -> - lists:reverse(Acc); -fix_names([{Name, Size}=T | Names], Acc) -> - case is64bit() of - true -> - case string:str(Name, ".rela") =:= 1 of - true -> %% Name starts with ".rela": - Section = string:substr(Name, 6), - fix_names(Names, [{Section, Size - 5} - | [T | Acc]]); % XXX: Is order ok? (".text" - % always before ".rela.text") - false -> %% Name does not start with ".rela": - fix_names(Names, [T | Acc]) - end; - false -> - case string:str(Name, ".rel") =:= 1 of - true -> %% Name starts with ".rel": - Section = string:substr(Name, 5), - fix_names(Names, [{Section, Size - 4} - | [T | Acc]]); % XXX: Is order ok? (".text" - % always before ".rela.text") - false -> %% Name does not start with ".rel": - fix_names(Names, [T | Acc]) - end - end. - - -%% @doc A function that byte-reverses a binary. This might be needed because of -%% little (fucking!) endianess. --spec bin_reverse(binary()) -> binary(). -bin_reverse(Bin) when is_binary(Bin) -> - bin_reverse(Bin, <<>>). - --spec bin_reverse(binary(), binary()) -> binary(). -bin_reverse(<<>>, Acc) -> - Acc; -bin_reverse(<<Head, More/binary>>, Acc) -> - bin_reverse(More, <<Head, Acc/binary>>). - -%% @doc A function that extracts a null-terminated string from a binary. It -%% returns the found string along with the rest of the binary. --spec bin_get_string(binary()) -> {string(), binary()}. -bin_get_string(Bin) -> - bin_get_string(Bin, <<>>). - -bin_get_string(<<>>, BinAcc) -> - Bin = bin_reverse(BinAcc), % little endian! - {binary_to_list(Bin), <<>>}; -bin_get_string(<<0, MoreBin/binary>>, BinAcc) -> - Bin = bin_reverse(BinAcc), % little endian! - {binary_to_list(Bin), MoreBin}; -bin_get_string(<<Letter, Tail/binary>>, BinAcc) -> - bin_get_string(Tail, <<Letter, BinAcc/binary>>). - -%% @doc -make_offsets(NamesSizes) -> - {Names, Sizes} = lists:unzip(NamesSizes), - Offsets = make_offsets_from_sizes(Sizes, 1, []), - lists:zip(Names, Offsets). - -make_offsets_from_sizes([], _, Acc) -> - lists:reverse(Acc); -make_offsets_from_sizes([Size | Sizes], Cur, Acc) -> - make_offsets_from_sizes(Sizes, Size+Cur+1, [Cur | Acc]). % For the "."! - %% @doc Little-Endian Base 128 (LEB128) Decoder %% This function extracts the <b>first</b> LEB128-encoded integer in a %% binary and returns that integer along with the remaining binary. This is @@ -770,21 +618,3 @@ leb128_decode(LebNum, NoOfBits, Acc) -> <<Num:Size/integer>> = <<NextBundle:7/bits, Acc/bits>>, {Num, MoreLebNums} end. - -%% @doc Extract ELF Class from ELF header and export symbol to process -%% dictionary. --spec set_architecture_flag(elf()) -> 'ok'. -set_architecture_flag(Elf) -> - %% Extract information about ELF Class from ELF Header - <<16#7f, $E, $L, $F, EI_Class, _MoreHeader/binary>> - = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE), - put(elf_class, EI_Class), - ok. - -%% @doc Read from object file header if the file class is ELF32 or ELF64. --spec is64bit() -> boolean(). -is64bit() -> - case get(elf_class) of - ?ELFCLASS64 -> true; - ?ELFCLASS32 -> false - end. diff --git a/lib/hipe/llvm/elf_format.hrl b/lib/hipe/llvm/elf_format.hrl index 7a3cdfead6..57a36f0c3e 100644 --- a/lib/hipe/llvm/elf_format.hrl +++ b/lib/hipe/llvm/elf_format.hrl @@ -486,3 +486,43 @@ %% Misc. %%------------------------------------------------------------------------------ -define(bits(Bytes), ((Bytes) bsl 3)). + +%%------------------------------------------------------------------------------ +%% Exported record and type declarations for 'elf_format' module +%%------------------------------------------------------------------------------ + +%% Section header entries +-record(elf_shdr, + {name :: elf_format:name() % Section name + ,type :: elf_format:shdr_type() % Section type + ,flags :: elf_format:bitflags() % Section attributes + ,addr :: elf_format:offset() % Virtual address in memory + ,offset :: elf_format:offset() % Offset in file + ,size :: elf_format:size() % Size of section + ,link :: non_neg_integer() % Link to other section + ,info :: non_neg_integer() % Miscellaneous information + ,addralign :: elf_format:size() % Address align boundary + ,entsize :: elf_format:size() % Size of entries, if section has + % table + }). +-type elf_shdr() :: #elf_shdr{}. + +%% Symbol table entries +-record(elf_sym, + {name :: elf_format:name() % Symbol name + ,bind :: elf_format:sym_bind() % Symbol binding + ,type :: elf_format:sym_type() % Symbol type + ,value :: elf_format:valueoff() % Symbol value + ,size :: elf_format:size() % Size of object + ,section :: undefined | abs | elf_shdr() + }). +-type elf_sym() :: #elf_sym{}. + +%% Relocations +-record(elf_rel, + {offset :: elf_format:offset() + ,type :: elf_format:reloc_type() + ,addend :: elf_format:addend() + ,symbol :: elf_sym() + }). +-type elf_rel() :: #elf_rel{}. diff --git a/lib/hipe/llvm/hipe_llvm.erl b/lib/hipe/llvm/hipe_llvm.erl index 5e33731a2b..c2547dd89e 100644 --- a/lib/hipe/llvm/hipe_llvm.erl +++ b/lib/hipe/llvm/hipe_llvm.erl @@ -234,7 +234,7 @@ function_arg_type_list/1 ]). --export([pp_ins_list/2, pp_ins/2]). +-export([pp_ins_list/3, pp_ins/3]). %%----------------------------------------------------------------------------- @@ -765,13 +765,17 @@ function_arg_type_list(#llvm_fun{arg_type_list=Arg_type_list}) -> %% Pretty-printer Functions %%---------------------------------------------------------------------------- -%% @doc Pretty-print a list of LLVM instructions to a Device. -pp_ins_list(_Dev, []) -> ok; -pp_ins_list(Dev, [I|Is]) -> - pp_ins(Dev, I), - pp_ins_list(Dev, Is). +-type llvm_version() :: {Major :: integer(), Minor :: integer()}. -pp_ins(Dev, I) -> +%% @doc Pretty-print a list of LLVM instructions to a Device, using syntax +%% compatible with LLVM v. Major.Minor +-spec pp_ins_list(file:io_device(), llvm_version(), [llvm_instr()]) -> ok. +pp_ins_list(_Dev, _Ver, []) -> ok; +pp_ins_list(Dev, Ver={_,_}, [I|Is]) -> + pp_ins(Dev, Ver, I), + pp_ins_list(Dev, Ver, Is). + +pp_ins(Dev, Ver, I) -> case indent(I) of true -> write(Dev, " "); false -> ok @@ -861,7 +865,7 @@ pp_ins(Dev, I) -> true -> write(Dev, "volatile "); false -> ok end, - pp_type(Dev, load_p_type(I)), + pp_dereference_type(Dev, Ver, load_p_type(I)), write(Dev, [" ", load_pointer(I), " "]), case load_alignment(I) of [] -> ok; @@ -897,7 +901,7 @@ pp_ins(Dev, I) -> true -> write(Dev, "inbounds "); false -> ok end, - pp_type(Dev, getelementptr_p_type(I)), + pp_dereference_type(Dev, Ver, getelementptr_p_type(I)), write(Dev, [" ", getelementptr_value(I)]), pp_typed_idxs(Dev, getelementptr_typed_idxs(I)), write(Dev, "\n"); @@ -958,12 +962,16 @@ pp_ins(Dev, I) -> pp_args(Dev, fun_def_arglist(I)), write(Dev, ") "), pp_options(Dev, fun_def_fn_attrs(I)), + case Ver >= {3,7} of false -> ok; true -> + write(Dev, "personality i32 (i32, i64, i8*,i8*)* " + "@__gcc_personality_v0 ") + end, case fun_def_align(I) of [] -> ok; N -> write(Dev, ["align ", N]) end, write(Dev, "{\n"), - pp_ins_list(Dev, fun_def_body(I)), + pp_ins_list(Dev, Ver, fun_def_body(I)), write(Dev, "}\n"); #llvm_fun_decl{} -> write(Dev, "declare "), @@ -992,8 +1000,12 @@ pp_ins(Dev, I) -> pp_type(Dev, const_decl_type(I)), write(Dev, [" ", const_decl_value(I), "\n"]); #llvm_landingpad{} -> - write(Dev, "landingpad { i8*, i32 } personality i32 (i32, i64, i8*,i8*)* - @__gcc_personality_v0 cleanup\n"); + write(Dev, "landingpad { i8*, i32 } "), + case Ver < {3,7} of false -> ok; true -> + write(Dev, "personality i32 (i32, i64, i8*,i8*)* " + "@__gcc_personality_v0 ") + end, + write(Dev, "cleanup\n"); #llvm_asm{} -> write(Dev, [asm_instruction(I), "\n"]); #llvm_adj_stack{} -> @@ -1002,13 +1014,27 @@ pp_ins(Dev, I) -> pp_type(Dev, adj_stack_type(I)), write(Dev, [" ", adj_stack_offset(I),")\n"]); #llvm_branch_meta{} -> - write(Dev, ["!", branch_meta_id(I), " = metadata !{metadata !\"branch_weights\", - i32 ", branch_meta_true_weight(I), ", i32 ", - branch_meta_false_weight(I), "}\n"]); + write(Dev, ["!", branch_meta_id(I), " = "]), + if Ver < {3,6} -> write(Dev, "metadata !{metadata "); + Ver >= {3,6} -> write(Dev, "!{ ") + end, + write(Dev, ["!\"branch_weights\", i32 ", branch_meta_true_weight(I), + ", i32 ", branch_meta_false_weight(I), "}\n"]); Other -> exit({?MODULE, pp_ins, {"Unknown LLVM instruction", Other}}) end. +%% @doc Print the type of a dereference in an LLVM instruction using syntax +%% parsable by the specified LLVM version. +pp_dereference_type(Dev, Ver, Type) -> + case Ver >= {3,7} of + false -> ok; + true -> + pp_type(Dev, pointer_type(Type)), + write(Dev, ", ") + end, + pp_type(Dev, Type). + %% @doc Pretty-print a list of types pp_type_list(_Dev, []) -> ok; pp_type_list(Dev, [T]) -> diff --git a/lib/hipe/llvm/hipe_llvm_main.erl b/lib/hipe/llvm/hipe_llvm_main.erl index 3c24425828..476d6fb49c 100644 --- a/lib/hipe/llvm/hipe_llvm_main.erl +++ b/lib/hipe/llvm/hipe_llvm_main.erl @@ -13,7 +13,7 @@ %% chain is invoked in order to produce an object file. rtl_to_native(MFA, RTL, Roots, Options) -> %% Compile to LLVM and get Instruction List (along with infos) - {LLVMCode, RelocsDict, ConstTab} = + {LLVMCode, RelocsDict0, ConstTab0} = hipe_rtl_to_llvm:translate(RTL, Roots), %% Fix function name to an acceptable LLVM identifier (needed for closures) {_Module, Fun, Arity} = hipe_rtl_to_llvm:fix_mfa_name(MFA), @@ -24,34 +24,33 @@ rtl_to_native(MFA, RTL, Roots, Options) -> %% Extract information from object file %% ObjBin = open_object_file(ObjectFile), - %% Read and set the ELF class - elf_format:set_architecture_flag(ObjBin), + Obj = elf_format:read(ObjBin), %% Get labels info (for switches and jump tables) - Labels = elf_format:get_rodata_relocs(ObjBin), - {Switches, Closures} = get_tables(ObjBin), + Labels = elf_format:extract_rela(Obj, ?RODATA), + Tables = get_tables(Obj), %% Associate Labels with Switches and Closures with stack args - {SwitchInfos, ExposedClosures} = - correlate_labels(Switches ++ Closures, Labels), + {SwitchInfos, ExposedClosures} = correlate_labels(Tables, Labels), %% SwitchInfos: [{"table_50", [Labels]}] %% ExposedClosures: [{"table_closures", [Labels]}] - + %% Labelmap contains the offsets of the labels in the code that are %% used for switch's jump tables - LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict), + LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict0), + {RelocsDict, ConstTab} = extract_constants(RelocsDict0, ConstTab0, Obj), %% Get relocation info - TextRelocs = elf_format:get_text_relocs(ObjBin), + TextRelocs = elf_format:extract_rela(Obj, ?TEXT), %% AccRefs contains the offsets of all references to relocatable symbols in %% the code: AccRefs = fix_relocations(TextRelocs, RelocsDict, MFA), %% Get stack descriptors - SDescs = get_sdescs(ObjBin), + SDescs = get_sdescs(Obj), %% FixedSDescs are the stack descriptors after correcting calls that have %% arguments in the stack FixedSDescs = fix_stack_descriptors(RelocsDict, AccRefs, SDescs, ExposedClosures), Refs = AccRefs ++ FixedSDescs, %% Get binary code from object file - BinCode = elf_format:extract_text(ObjBin), + BinCode = elf_format:extract_text(Obj), %% Remove temp files (if needed) ok = remove_temp_folder(Dir, Options), %% Return the code together with information that will be used in the @@ -78,7 +77,8 @@ compile_with_llvm(FunName, Arity, LLVMCode, Options, UseBuffer) -> false -> [] end, {ok, File_llvm} = file:open(Dir ++ Filename ++ ".ll", OpenOpts), - hipe_llvm:pp_ins_list(File_llvm, LLVMCode), + Ver = hipe:get_llvm_version(), %% Should probably cache this + hipe_llvm:pp_ins_list(File_llvm, Ver, LLVMCode), %% delayed_write can cause file:close not to do a close, hence the two calls ok = file:close(File_llvm), __ = file:close(File_llvm), @@ -158,12 +158,10 @@ trans_optlev_flag(Tool, Options) -> %%------------------------------------------------------------------------------ %% @doc Get switch table and closure table. +-spec get_tables(elf_format:elf()) -> [elf_sym()]. get_tables(Elf) -> - %% Search Symbol Table for an entry with name prefixed with "table_": - Triples = elf_format:get_tab_entries(Elf), - Switches = [T || T={"table_" ++ _, _, _} <- Triples], - Closures = [T || T={"table_closures" ++ _, _, _} <- Switches], - {Switches, Closures}. + %% Search Symbol Table for entries where name is prefixed with "table_": + [S || S=#elf_sym{name="table_" ++ _} <- elf_format:elf_symbols(Elf)]. %% @doc This function associates symbols who point to some table of labels with %% the corresponding offsets of the labels in the code. These tables can @@ -171,14 +169,12 @@ get_tables(Elf) -> %% of blocks that contain closure calls with more than ?NR_ARG_REGS. correlate_labels([], _L) -> {[], []}; correlate_labels(Tables, Labels) -> - %% Sort "Tables" based on "ValueOffsets" - OffsetSortedTb = lists:ukeysort(2, Tables), - %% Unzip offset-sorted list of "Switches" - {Names, _Offsets, TablesSizeList} = lists:unzip3(OffsetSortedTb), - %% Associate switch names with labels - L = split_list(Labels, TablesSizeList), - %% Zip back! (to [{SwitchName, Values}]) - NamesValues = lists:zip(Names, L), + %% Assumes that the relocations are sorted + RelocTree = gb_trees:from_orddict( + [{Rel#elf_rel.offset, Rel#elf_rel.addend} || Rel <- Labels]), + %% Lookup all relocations pertaining to each symbol + NamesValues = [{Name, lookup_range(Value, Value+Size, RelocTree)} + || #elf_sym{name=Name, value=Value, size=Size} <- Tables], case lists:keytake("table_closures", 1, NamesValues) of false -> %% No closures in the code, no closure table {NamesValues, []}; @@ -186,6 +182,17 @@ correlate_labels(Tables, Labels) -> {SwitchesNV, ClosureTableNV} end. +%% Fetches all values with a key in [Low, Hi) +-spec lookup_range(_::K, _::K, gb_trees:tree(K,V)) -> [_::V]. +lookup_range(Low, Hi, Tree) -> + lookup_range_1(Hi, gb_trees:iterator_from(Low, Tree)). + +lookup_range_1(Hi, Iter0) -> + case gb_trees:next(Iter0) of + {Key, Value, Iter} when Key < Hi -> [Value | lookup_range_1(Hi, Iter)]; + _ -> [] + end. + %% @doc Create a gb_tree which contains information about the labels that used %% for switch's jump tables. The keys of the gb_tree are of the form %% {MFA, Label} and the values are the actual Offsets. @@ -213,40 +220,80 @@ insert_to_labelmap([{Key, Value}|Rest], LabelMap) -> insert_to_labelmap(Rest, LabelMap) end. +%% Find any LLVM-generated constants and add them to the constant table +extract_constants(RelocsDict0, ConstTab0, Obj) -> + TextRelocs = elf_format:extract_rela(Obj, ?TEXT), + AnonConstSections = + lists:usort([{Sec, Offset} + || #elf_rel{symbol=#elf_sym{type=section, section=Sec}, + addend=Offset} <- TextRelocs]), + lists:foldl( + fun({#elf_shdr{name=Name, type=progbits, addralign=Align, entsize=EntSize, + size=Size} = Section, Offset}, {RelocsDict1, ConstTab1}) + when EntSize > 0, 0 =:= Size rem EntSize, 0 =:= Offset rem EntSize -> + SectionBin = elf_format:section_contents(Section, Obj), + Constant = binary:part(SectionBin, Offset, EntSize), + {ConstTab, ConstLbl} = + hipe_consttab:insert_binary_const(ConstTab1, Align, Constant), + {dict:store({anon, Name, Offset}, {constant, ConstLbl}, RelocsDict1), + ConstTab} + end, {RelocsDict0, ConstTab0}, AnonConstSections). + %% @doc Correlate object file relocation symbols with info from translation to %% llvm code. fix_relocations(Relocs, RelocsDict, MFA) -> - fix_relocs(Relocs, RelocsDict, MFA, []). - -fix_relocs([], _, _, RelocAcc) -> RelocAcc; -fix_relocs([{Name, Offset}|Rs], RelocsDict, {ModName,_,_}=MFA, RelocAcc) -> + lists:map(fun(Reloc) -> fix_reloc(Reloc, RelocsDict, MFA) end, Relocs). + +%% Relocation types and expected addends for x86 and amd64 +-define(PCREL_T, 'pc32'). +-define(PCREL_A, -4). %% Hard-coded in hipe_x86.c and hipe_amd64.c +-ifdef(BIT32). +-define(ABS_T, '32'). +-define(ABS_A, _). %% We support any addend +-else. +-define(ABS_T, '64'). +-define(ABS_A, 0). +-endif. + +fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=undefined, type=notype}, + offset=Offset, type=?PCREL_T, addend=?PCREL_A}, + RelocsDict, {ModName,_,_}) when Name =/= "" -> case dict:fetch(Name, RelocsDict) of - {atom, AtomName} -> - fix_relocs(Rs, RelocsDict, MFA, - [{?LOAD_ATOM, Offset, AtomName}|RelocAcc]); - {constant, Label} -> - fix_relocs(Rs, RelocsDict, MFA, - [{?LOAD_ADDRESS, Offset, {constant, Label}}|RelocAcc]); - {switch, _, JTabLab} -> %% Treat switch exactly as constant - fix_relocs(Rs, RelocsDict, MFA, - [{?LOAD_ADDRESS, Offset, {constant, JTabLab}}|RelocAcc]); - {closure, _}=Closure -> - fix_relocs(Rs, RelocsDict, MFA, - [{?LOAD_ADDRESS, Offset, Closure}|RelocAcc]); - {call, {bif, BifName, _}} -> - fix_relocs(Rs, RelocsDict, MFA, - [{?CALL_LOCAL, Offset, BifName}|RelocAcc]); + {call, {bif, BifName, _}} -> {?CALL_LOCAL, Offset, BifName}; %% MFA calls to functions in the same module are of type 3, while all %% other MFA calls are of type 2. - {call, {ModName,_F,_A}=CallMFA} -> - fix_relocs(Rs, RelocsDict, MFA, - [{?CALL_LOCAL, Offset, CallMFA}|RelocAcc]); - {call, CallMFA} -> - fix_relocs(Rs, RelocsDict, MFA, - [{?CALL_REMOTE, Offset, CallMFA}|RelocAcc]); - Other -> - exit({?MODULE, fix_relocs, - {"Relocation not in relocation dictionary", Other}}) + %% XXX: Does this code break hot code loading (by transforming external + %% calls into local calls?) + {call, {ModName,_F,_A}=CallMFA} -> {?CALL_LOCAL, Offset, CallMFA}; + {call, CallMFA} -> {?CALL_REMOTE, Offset, CallMFA} + end; +fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=undefined, type=notype}, + offset=Offset, type=?ABS_T, addend=?ABS_A}, + RelocsDict, _) when Name =/= "" -> + case dict:fetch(Name, RelocsDict) of + {atom, AtomName} -> {?LOAD_ATOM, Offset, AtomName}; + {constant, Label} -> {?LOAD_ADDRESS, Offset, {constant, Label}}; + {closure, _}=Closure -> {?LOAD_ADDRESS, Offset, Closure} + end; +fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=#elf_shdr{name=?TEXT}, + type=func}, + offset=Offset, type=?PCREL_T, addend=?PCREL_A}, + RelocsDict, MFA) when Name =/= "" -> + case dict:fetch(Name, RelocsDict) of + {call, MFA} -> {?CALL_LOCAL, Offset, MFA} + end; +fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=#elf_shdr{name=?RODATA}, + type=object}, + offset=Offset, type=?ABS_T, addend=?ABS_A}, + RelocsDict, _) when Name =/= "" -> + case dict:fetch(Name, RelocsDict) of + {switch, _, JTabLab} -> %% Treat switch exactly as constant + {?LOAD_ADDRESS, Offset, {constant, JTabLab}} + end; +fix_reloc(#elf_rel{symbol=#elf_sym{type=section, section=#elf_shdr{name=Name}}, + offset=Offset, type=?ABS_T, addend=Addend}, RelocsDict, _) -> + case dict:fetch({anon, Name, Addend}, RelocsDict) of + {constant, Label} -> {?LOAD_ADDRESS, Offset, {constant, Label}} end. %%------------------------------------------------------------------------------ @@ -271,20 +318,14 @@ get_sdescs(Elf) -> T = SPCount * ?SP_ADDR_SIZE, %% Pattern match fields of ".note.gc": <<SPCount:(?bits(?SP_COUNT_SIZE))/integer-little, % Sanity check! - SPAddrs:T/binary, % NOTE: In 64bit they are relocs! + _SPAddrs:T/binary, % NOTE: In 64bit they are relocs! StkFrameSize:(?bits(?SP_STKFRAME_SIZE))/integer-little, StkArity:(?bits(?SP_STKARITY_SIZE))/integer-little, _LiveRootCount:(?bits(?SP_LIVEROOTCNT_SIZE))/integer-little, % Skip Roots/binary>> = NoteGC_bin, LiveRoots = get_liveroots(Roots, []), - %% Extract information about the safe point addresses: - SPOffs = - case elf_format:is64bit() of - true -> %% Find offsets in ".rela.note.gc": - elf_format:get_rela_addends(RelaNoteGC); - false -> %% Find offsets in SPAddrs (in ".note.gc"): - get_spoffs(SPAddrs, []) - end, + %% Extract the safe point offsets: + SPOffs = [A || #elf_rel{addend=A} <- RelaNoteGC], %% Extract Exception Handler labels: ExnHandlers = elf_format:get_exn_handlers(Elf), %% Combine ExnHandlers and Safe point addresses (return addresses): @@ -300,13 +341,6 @@ get_liveroots(<<Root:?bits(?LR_STKINDEX_SIZE)/integer-little, MoreRoots/binary>>, Acc) -> get_liveroots(MoreRoots, [Root | Acc]). -%% @doc Extracts a bunch of integers (safepoint offsets) from a binary. Returns -%% a tuple as need for stack descriptors. -get_spoffs(<<>>, Acc) -> - lists:reverse(Acc); -get_spoffs(<<SPOff:?bits(?SP_ADDR_SIZE)/integer-little, More/binary>>, Acc) -> - get_spoffs(More, [SPOff | Acc]). - combine_ras_and_exns(_, [], Acc) -> lists:reverse(Acc); combine_ras_and_exns(ExnHandlers, [RA | MoreRAs], Acc) -> @@ -489,18 +523,3 @@ unique_folder(FunName, Arity, Options) -> dir_exists(Filename) -> {Flag, Info} = file:read_file_info(Filename), (Flag =:= ok) andalso (element(3, Info) =:= directory). - -%% @doc Function that takes as arguments a list of integers and a list with -%% numbers indicating how many items should each tuple have and splits -%% the original list to a list of lists of integers (with the specified -%% number of elements), i.e. [ [...], [...] ]. --spec split_list([integer()], [integer()]) -> [ [integer()] ]. -split_list(List, ElemsPerTuple) -> - split_list(List, ElemsPerTuple, []). - --spec split_list([integer()], [integer()], [ [integer()] ]) -> [ [integer()] ]. -split_list([], [], Acc) -> - lists:reverse(Acc); -split_list(List, [NumOfElems | MoreNums], Acc) -> - {L1, L2} = lists:split(NumOfElems, List), - split_list(L2, MoreNums, [ L1 | Acc]). diff --git a/lib/hipe/llvm/hipe_rtl_to_llvm.erl b/lib/hipe/llvm/hipe_rtl_to_llvm.erl index d7d8d1b049..b23d756d6c 100644 --- a/lib/hipe/llvm/hipe_rtl_to_llvm.erl +++ b/lib/hipe/llvm/hipe_rtl_to_llvm.erl @@ -266,17 +266,18 @@ trans_alub_overflow(I, Sign, Relocs) -> T2 = mk_temp(), %% T1{1}: Boolean variable indicating overflow I6 = hipe_llvm:mk_extractvalue(T2, ReturnType, T1, "1", []), - case hipe_rtl:alub_cond(I) of - Op when Op =:= overflow orelse Op =:= ltu -> - True_label = mk_jump_label(hipe_rtl:alub_true_label(I)), - False_label = mk_jump_label(hipe_rtl:alub_false_label(I)), - MetaData = branch_metadata(hipe_rtl:alub_pred(I)); - not_overflow -> - True_label = mk_jump_label(hipe_rtl:alub_false_label(I)), - False_label = mk_jump_label(hipe_rtl:alub_true_label(I)), - MetaData = branch_metadata(1 - hipe_rtl:alub_pred(I)) - end, - I7 = hipe_llvm:mk_br_cond(T2, True_label, False_label, MetaData), + {TrueLabel, FalseLabel, MetaData} = + case hipe_rtl:alub_cond(I) of + Op when Op =:= overflow orelse Op =:= ltu -> + {mk_jump_label(hipe_rtl:alub_true_label(I)), + mk_jump_label(hipe_rtl:alub_false_label(I)), + branch_metadata(hipe_rtl:alub_pred(I))}; + not_overflow -> + {mk_jump_label(hipe_rtl:alub_false_label(I)), + mk_jump_label(hipe_rtl:alub_true_label(I)), + branch_metadata(1 - hipe_rtl:alub_pred(I))} + end, + I7 = hipe_llvm:mk_br_cond(T2, TrueLabel, FalseLabel, MetaData), {[I7, I6, I5, I4, I3, I2, I1], NewRelocs}. trans_alub_op(I, Sign) -> diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src index aa86b6dc5b..f8487151d7 100644 --- a/lib/hipe/main/hipe.app.src +++ b/lib/hipe/main/hipe.app.src @@ -88,6 +88,7 @@ hipe_icode2rtl, hipe_icode_bincomp, hipe_icode_callgraph, + hipe_icode_call_elim, hipe_icode_cfg, hipe_icode_coordinator, hipe_icode_ebb, diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl index 0e32da1d36..981265b3e9 100644 --- a/lib/hipe/main/hipe.erl +++ b/lib/hipe/main/hipe.erl @@ -200,8 +200,9 @@ compile/4, compile_core/4, file/1, - file/2, - llvm_support_available/0, + file/2, + get_llvm_version/0, + llvm_support_available/0, load/1, help/0, help_hiper/0, @@ -1165,6 +1166,9 @@ option_text(caller_save_spill_restore) -> "Activates caller save register spills and restores"; option_text(debug) -> "Outputs internal debugging information during compilation"; +option_text(icode_call_elim) -> + "Performs call elimination of BIFs that are side-effect free\n" ++ + "only on some argument types"; option_text(icode_range) -> "Performs integer range analysis on the Icode level"; option_text(icode_ssa_check) -> @@ -1318,6 +1322,7 @@ opt_keys() -> get_called_modules, split_arith, split_arith_unsafe, + icode_call_elim, icode_inline_bifs, icode_ssa_check, icode_ssa_copy_prop, @@ -1399,7 +1404,7 @@ o1_opts(TargetArch) -> o2_opts(TargetArch) -> Common = [icode_ssa_const_prop, icode_ssa_copy_prop, % icode_ssa_struct_reuse, - icode_type, icode_inline_bifs, rtl_lcm, + icode_type, icode_inline_bifs, icode_call_elim, rtl_lcm, rtl_ssa, rtl_ssa_const_prop, spillmin_color, use_indexing, remove_comments, concurrent_comp, binary_opt | o1_opts(TargetArch)], @@ -1429,6 +1434,7 @@ opt_negations() -> {no_icode_inline_bifs, icode_inline_bifs}, {no_icode_range, icode_range}, {no_icode_split_arith, icode_split_arith}, + {no_icode_call_elim, icode_call_elim}, {no_icode_ssa_check, icode_ssa_check}, {no_icode_ssa_copy_prop, icode_ssa_copy_prop}, {no_icode_ssa_const_prop, icode_ssa_const_prop}, @@ -1479,18 +1485,25 @@ opt_expansions(TargetArch) -> [{o1, o1_opts(TargetArch)}, {o2, o2_opts(TargetArch)}, {o3, o3_opts(TargetArch)}, - {to_llvm, llvm_opts(o3)}, - {{to_llvm, o0}, llvm_opts(o0)}, - {{to_llvm, o1}, llvm_opts(o1)}, - {{to_llvm, o2}, llvm_opts(o2)}, - {{to_llvm, o3}, llvm_opts(o3)}, + {to_llvm, llvm_opts(o3, TargetArch)}, + {{to_llvm, o0}, llvm_opts(o0, TargetArch)}, + {{to_llvm, o1}, llvm_opts(o1, TargetArch)}, + {{to_llvm, o2}, llvm_opts(o2, TargetArch)}, + {{to_llvm, o3}, llvm_opts(o3, TargetArch)}, {x87, [x87, inline_fp]}, {inline_fp, case TargetArch of %% XXX: Temporary until x86 has sse2 x86 -> [x87, inline_fp]; _ -> [inline_fp] end}]. -llvm_opts(O) -> - [to_llvm, {llvm_opt, O}, {llvm_llc, O}]. +llvm_opts(O, TargetArch) -> + Base = [to_llvm, {llvm_opt, O}, {llvm_llc, O}], + case TargetArch of + %% A llvm bug present in 3.4 through (at least) 3.8 miscompiles x86 + %% functions that have floats are spilled to stack by clobbering the process + %% pointer (ebp) trying to realign the stack pointer. + x86 -> [no_inline_fp | Base]; + _ -> Base + end. %% This expands "basic" options, which may be tested early and cannot be %% in conflict with options found in the source code. @@ -1520,7 +1533,8 @@ expand_options(Opts, TargetArch) -> proplists:normalize(Opts, [{negations, opt_negations()}, {aliases, opt_aliases()}, {expand, opt_basic_expansions()}, - {expand, opt_expansions(TargetArch)}]). + {expand, opt_expansions(TargetArch)}, + {negations, opt_negations()}]). -spec check_options(comp_options()) -> 'ok'. @@ -1538,18 +1552,27 @@ check_options(Opts) -> -spec llvm_support_available() -> boolean(). llvm_support_available() -> - get_llvm_version() >= 3.4. + get_llvm_version() >= {3,4}. + +-type llvm_version() :: {Major :: integer(), Minor :: integer()}. +-spec get_llvm_version() -> llvm_version() | {0, 0}. get_llvm_version() -> OptStr = os:cmd("opt -version"), SubStr = "LLVM version ", N = length(SubStr), case string:str(OptStr, SubStr) of 0 -> % No opt available - 0.0; + {0, 0}; S -> - case string:to_float(string:sub_string(OptStr, S + N)) of - {error, _} -> 0.0; %XXX: Assumes no revision numbers in versioning - {Float, _} -> Float + case string:tokens(string:sub_string(OptStr, S + N), ".") of + [MajorS, MinorS | _] -> + case {string:to_integer(MajorS), string:to_integer(MinorS)} of + {{Major, ""}, {Minor, _}} + when is_integer(Major), is_integer(Minor) -> + {Major, Minor}; + _ -> {0, 0} + end; + _ -> {0, 0} %XXX: Assumes no revision numbers in versioning end end. diff --git a/lib/hipe/main/hipe_main.erl b/lib/hipe/main/hipe_main.erl index be5050e155..b9d783d20a 100644 --- a/lib/hipe/main/hipe_main.erl +++ b/lib/hipe/main/hipe_main.erl @@ -284,8 +284,9 @@ icode_ssa_type(IcodeSSA, MFA, Options, Servers) -> false -> AnnIcode1 end, AnnIcode3 = icode_range_analysis(AnnIcode2, MFA, Options, Servers), - pp(AnnIcode3, MFA, icode, pp_range_icode, Options, Servers), - hipe_icode_type:unannotate_cfg(AnnIcode3) + AnnIcode4 = icode_eliminate_safe_calls(AnnIcode3, Options), + pp(AnnIcode4, MFA, icode, pp_range_icode, Options, Servers), + hipe_icode_type:unannotate_cfg(AnnIcode4) end. icode_ssa_convert(IcodeCfg, Options) -> @@ -334,6 +335,15 @@ icode_range_analysis(IcodeSSA, MFA, Options, Servers) -> IcodeSSA end. +icode_eliminate_safe_calls(IcodeSSA, Options) -> + case proplists:get_bool(icode_call_elim, Options) of + true -> + ?option_time(hipe_icode_call_elim:cfg(IcodeSSA), + "Icode SSA safe call elimination", Options); + false -> + IcodeSSA + end. + icode_ssa_dead_code_elimination(IcodeSSA, Options) -> IcodeSSA1 = ?option_time(hipe_icode_ssa:remove_dead_code(IcodeSSA), "Icode SSA dead code elimination pass 2", diff --git a/lib/hipe/misc/hipe_consttab.erl b/lib/hipe/misc/hipe_consttab.erl index f361edc79c..226b20fa46 100644 --- a/lib/hipe/misc/hipe_consttab.erl +++ b/lib/hipe/misc/hipe_consttab.erl @@ -87,7 +87,8 @@ % {NewTab, Lbl} insert_sorted_block/4, insert_block/3, - %% insert_global_word/2, + insert_binary_const/3, + %% insert_global_word/2, %% insert_global_block/4, %% update_word/3, % update_word(ConstTab, Value) -> {NewTab, Lbl} %% update_block/5, @@ -196,6 +197,16 @@ insert_block({ConstTab, RefToLabels, NextLabel}, ElementType, InitList) -> {ElementType,InitList}), {insert_backrefs(NewTa, Id, ReferredLabels), Id}. +%% @doc Inserts a binary constant literal into the const table. +-spec insert_binary_const(hipe_consttab(), ct_alignment(), binary()) -> + {hipe_consttab(), hipe_constlbl()}. +insert_binary_const(ConstTab, Alignment, Binary) + when (Alignment =:= 4 orelse Alignment =:= 8 orelse Alignment =:= 16 + orelse Alignment =:= 32), is_binary(Binary), + size(Binary) rem Alignment =:= 0 -> + insert_const(ConstTab, block, Alignment, false, + {byte, binary_to_list(Binary)}). + %% @spec (ConstTab::hipe_consttab(), ElementType::element_type(), %% InitList::block(), SortOrder) -> {hipe_consttab(), hipe_constlbl()} diff --git a/lib/hipe/misc/hipe_consttab.hrl b/lib/hipe/misc/hipe_consttab.hrl index d2dbbe509c..550da0455c 100644 --- a/lib/hipe/misc/hipe_consttab.hrl +++ b/lib/hipe/misc/hipe_consttab.hrl @@ -20,7 +20,7 @@ %% %%----------------------------------------------------------------------------- --type ct_alignment() :: 4 | 8. +-type ct_alignment() :: 4 | 8 | 16 | 32. -type hipe_constlbl() :: non_neg_integer(). -type hipe_consttab() :: {dict:dict(), [hipe_constlbl()], hipe_constlbl()}. diff --git a/lib/hipe/test/Makefile b/lib/hipe/test/Makefile index 09f4fd2129..544888719f 100644 --- a/lib/hipe/test/Makefile +++ b/lib/hipe/test/Makefile @@ -6,7 +6,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # ---------------------------------------------------- MODULES= \ - hipe_SUITE + hipe_SUITE \ + opt_verify_SUITE # .erl files for these modules are automatically generated GEN_MODULES= \ @@ -79,4 +80,4 @@ release_tests_spec: make_emakefile @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) cd "$(RELSYSDIR)";\ erlc hipe_testsuite_driver.erl;\ - erl -noshell -run hipe_testsuite_driver create_all_suites -s erlang halt + erl -noshell -run hipe_testsuite_driver create_all_suites $(GEN_MODULES) -s erlang halt diff --git a/lib/hipe/test/hipe_testsuite_driver.erl b/lib/hipe/test/hipe_testsuite_driver.erl index 64c5c0a7c9..03ec7adfd0 100644 --- a/lib/hipe/test/hipe_testsuite_driver.erl +++ b/lib/hipe/test/hipe_testsuite_driver.erl @@ -1,6 +1,6 @@ -module(hipe_testsuite_driver). --export([create_all_suites/0, run/3]). +-export([create_all_suites/1, run/3]). -include_lib("kernel/include/file.hrl"). @@ -16,25 +16,17 @@ outputfile :: file:io_device(), testcases :: [testcase()]}). --spec create_all_suites() -> 'ok'. +-spec create_all_suites([string()]) -> 'ok'. -create_all_suites() -> - {ok, Cwd} = file:get_cwd(), - Suites = get_suites(Cwd), +create_all_suites(SuitesWithSuiteSuffix) -> + Suites = get_suites(SuitesWithSuiteSuffix), lists:foreach(fun create_suite/1, Suites). --spec get_suites(file:filename()) -> [string()]. +-spec get_suites([string()]) -> [string()]. -get_suites(Dir) -> - case file:list_dir(Dir) of - {error, _} -> []; - {ok, Filenames} -> - FullFilenames = [filename:join(Dir, F) || F <- Filenames], - Dirs = [suffix(filename:basename(F), ?suite_data) || - F <- FullFilenames, - file_type(F) =:= {ok, 'directory'}], - [S || {yes, S} <- Dirs] - end. +get_suites(SuitesWithSuiteSuffix) -> + Prefixes = [suffix(F, ?suite_suffix) || F <- SuitesWithSuiteSuffix], + [S || {yes, S} <- Prefixes]. suffix(String, Suffix) -> case string:rstr(String, Suffix) of diff --git a/lib/hipe/test/opt_verify_SUITE.erl b/lib/hipe/test/opt_verify_SUITE.erl new file mode 100644 index 0000000000..61952e81d7 --- /dev/null +++ b/lib/hipe/test/opt_verify_SUITE.erl @@ -0,0 +1,62 @@ +-module(opt_verify_SUITE). + +-compile([export_all]). + +all() -> + [call_elim]. + +groups() -> + []. + +init_per_suite(Config) -> + case erlang:system_info(hipe_architecture) of + undefined -> {skip, "HiPE not available or enabled"}; + _ -> Config + end. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +call_elim_test_file(Config, FileName, Option) -> + PrivDir = test_server:lookup_config(priv_dir, Config), + TempOut = test_server:temp_name(filename:join(PrivDir, "call_elim_out")), + {ok, TestCase} = compile:file(FileName), + {ok, TestCase} = hipe:c(TestCase, [Option, {pp_range_icode, {file, TempOut}}]), + {ok, Icode} = file:read_file(TempOut), + ok = file:delete(TempOut), + Icode. + +substring_count(Icode, Substring) -> + substring_count(Icode, Substring, 0). +substring_count(Icode, Substring, N) -> + case string:str(Icode, Substring) of + 0 -> N; + I -> substring_count(lists:nthtail(I, Icode), Substring, N+1) + end. + +call_elim() -> + [{doc, "Test that the call elimination optimization pass is ok"}]. +call_elim(Config) -> + DataDir = test_server:lookup_config(data_dir, Config), + F1 = filename:join(DataDir, "call_elim_test.erl"), + Icode1 = call_elim_test_file(Config, F1, icode_call_elim), + 0 = substring_count(binary:bin_to_list(Icode1), "is_key"), + Icode2 = call_elim_test_file(Config, F1, no_icode_call_elim), + true = (0 /= substring_count(binary:bin_to_list(Icode2), "is_key")), + F2 = filename:join(DataDir, "call_elim_test_branches_no_opt_poss.erl"), + Icode3 = call_elim_test_file(Config, F2, icode_call_elim), + 3 = substring_count(binary:bin_to_list(Icode3), "is_key"), + Icode4 = call_elim_test_file(Config, F2, no_icode_call_elim), + 3 = substring_count(binary:bin_to_list(Icode4), "is_key"), + F3 = filename:join(DataDir, "call_elim_test_branches_opt_poss.erl"), + Icode5 = call_elim_test_file(Config, F3, icode_call_elim), + 0 = substring_count(binary:bin_to_list(Icode5), "is_key"), + Icode6 = call_elim_test_file(Config, F3, no_icode_call_elim), + 3 = substring_count(binary:bin_to_list(Icode6), "is_key"), + ok. diff --git a/lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl new file mode 100644 index 0000000000..8b725f8ffe --- /dev/null +++ b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl @@ -0,0 +1,12 @@ +-module(call_elim_test). + +-export([test/0]). + +test() -> + true = has_1_field(#{1=>true}), + true = has_1_field(#{1=>"hej", b=>2}), + true = has_1_field(#{b=>3, 1=>4}), + ok. + +has_1_field(#{1:=_}) -> true; +has_1_field(#{}) -> false. diff --git a/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl new file mode 100644 index 0000000000..7ffae86797 --- /dev/null +++ b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl @@ -0,0 +1,32 @@ +-module(call_elim_test_branches_no_opt_poss). + +-export([test/1]). + +test(A) -> + if A > 0 -> + false = has_a_field(#{b=>true}), + true = has_a_field(#{b=>1, a=>"2"}), + false = has_a_field(#{b=>5, c=>4}), + false = has_tuple_field(#{{ab, 2}=><<"qq">>, 1 =>0}), + false = has_tuple_field(#{up =>down, {ab, 2}=>[]}), + false = has_tuple_field(#{{ab, 2}=>42}); + A =< 0 -> + true = has_a_field(#{a=>q, 'A' =>nej}), + true = has_a_field(#{a=>"hej", false=>true}), + true = has_a_field(#{a=>3}), + true = has_tuple_field(#{{ab, 1}=>q, 'A' =>nej}), + true = has_tuple_field(#{{ab, 1}=>"hej", false=>true}), + true = has_tuple_field(#{{ab, 1}=>3}) + end, + true = has_nil_field(#{[] =>3, b=>"seven"}), + true = has_nil_field(#{"seventeen"=>17}), + ok. + +has_tuple_field(#{{ab, 1}:=_}) -> true; +has_tuple_field(#{}) -> false. + +has_a_field(#{a:=_}) -> true; +has_a_field(#{}) -> false. + +has_nil_field(#{[]:=_}) -> true; +has_nil_field(#{}) -> false. diff --git a/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl new file mode 100644 index 0000000000..c8ddfa1e75 --- /dev/null +++ b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl @@ -0,0 +1,32 @@ +-module(call_elim_test_branches_opt_poss). + +-export([test/1]). + +test(A) -> + if A > 0 -> + true = has_a_field(#{a=>true}), + true = has_a_field(#{b=>1, a=>"2"}), + true = has_a_field(#{a=>5, c=>4}), + true = has_tuple_field(#{{ab, 1}=><<"qq">>, 1 =>0}), + true = has_tuple_field(#{up =>down, {ab, 1}=>[]}), + true = has_tuple_field(#{{ab, 1}=>42}); + A =< 0 -> + true = has_a_field(#{a=>q, 'A' =>nej}), + true = has_a_field(#{a=>"hej", false=>true}), + true = has_a_field(#{a=>3}), + true = has_tuple_field(#{{ab, 1}=>q, 'A' =>nej}), + true = has_tuple_field(#{{ab, 1}=>"hej", false=>true}), + true = has_tuple_field(#{{ab, 1}=>3}) + end, + true = has_nil_field(#{[] =>3, b =>"seven"}), + true = has_nil_field(#{"seventeen"=>17, []=>nil}), + ok. + +has_tuple_field(#{{ab, 1}:=_}) -> true; +has_tuple_field(#{}) -> false. + +has_a_field(#{a:=_}) -> true; +has_a_field(#{}) -> false. + +has_nil_field(#{[]:=_}) -> true; +has_nil_field(#{}) -> false. diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index 65983c528d..ffc512050a 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -151,7 +151,6 @@ MODULES = \ inets_test_lib \ erl_make_certs \ ftp_SUITE \ - ftp_suite_lib \ ftp_format_SUITE \ http_format_SUITE \ httpc_SUITE \ @@ -169,8 +168,6 @@ MODULES = \ httpd_test_lib \ inets_sup_SUITE \ inets_SUITE \ - inets_app_test \ - inets_appup_test \ tftp_test_lib \ tftp_SUITE \ uri_SUITE \ diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index cc40377ebf..08295d4e3c 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -42,8 +42,10 @@ -define(BAD_USER, "baduser"). -define(BAD_DIR, "baddirectory"). -go() -> ct:run_test([{suite,"ftp_SUITE"}, {logdir,"LOG"}]). -gos() -> ct:run_test([{suite,"ftp_SUITE"}, {group,ftps_passive}, {logdir,"LOG"}]). +-record(progress, { + current = 0, + total + }). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- @@ -90,7 +92,14 @@ ftp_tests()-> recv_chunk, type, quote, - ip_v6_disabled + ip_v6_disabled, + progress_report_send, + progress_report_recv, + not_owner, + unexpected_call, + unexpected_cast, + unexpected_bang, + clean_shutdown ]. %%-------------------------------------------------------------------- @@ -117,9 +126,9 @@ ftp_tests()-> -define(default_ftp_servers, [{"vsftpd", fun(__CONF__) -> - DataDir = ?config(data_dir,__CONF__), + DataDir = proplists:get_value(data_dir,__CONF__), ConfFile = filename:join(DataDir, "vsftpd.conf"), - PrivDir = ?config(priv_dir,__CONF__), + PrivDir = proplists:get_value(priv_dir,__CONF__), AnonRoot = PrivDir, Cmd = ["vsftpd "++filename:join(DataDir,"vsftpd.conf"), " -oftpd_banner=erlang_otp_testing", @@ -145,7 +154,7 @@ ftp_tests()-> fun(_StartResult) -> os:cmd("kill `ps ax | grep erlang_otp_testing | awk '/vsftpd/{print $1}'`") end, fun(__CONF__) -> - AnonRoot = ?config(priv_dir,__CONF__), + AnonRoot = proplists:get_value(priv_dir,__CONF__), [{id2ftp, fun(Id) -> filename:join(AnonRoot,Id) end}, {id2ftp_result,fun(Id) -> filename:join(AnonRoot,Id) end} | __CONF__] end, @@ -161,9 +170,9 @@ init_per_suite(Config) -> false -> {skip, "No ftp server found"}; {ok,Data} -> - TstDir = filename:join(?config(priv_dir,Config), "test"), + TstDir = filename:join(proplists:get_value(priv_dir,Config), "test"), file:make_dir(TstDir), - make_cert_files(dsa, rsa, "server-", ?config(data_dir,Config)), + make_cert_files(dsa, rsa, "server-", proplists:get_value(data_dir,Config)), start_ftpd([{test_dir,TstDir}, {ftpd_data,Data} | Config]) @@ -181,8 +190,16 @@ init_per_group(_Group, Config) -> Config. end_per_group(_Group, Config) -> Config. %%-------------------------------------------------------------------- -init_per_testcase(Case, Config0) -> - Group = proplists:get_value(name,?config(tc_group_properties,Config0)), + +init_per_testcase(Case, Config) when (Case =:= progress_report_send) orelse + (Case =:= progress_report_recv) -> + common_init_per_testcase(Case, [{progress, {?MODULE, progress, #progress{}}} | Config]); + +init_per_testcase(Case, Config) -> + common_init_per_testcase(Case, Config). + +common_init_per_testcase(Case, Config0) -> + Group = proplists:get_value(name,proplists:get_value(tc_group_properties,Config0)), try ?MODULE:Case(doc) of Msg -> ct:comment(Msg) catch @@ -203,9 +220,9 @@ init_per_testcase(Case, Config0) -> user -> Config; bad_user -> Config; _ -> - Pid = ?config(ftp,Config), + Pid = proplists:get_value(ftp,Config), ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS(atom_to_list(Group)++"-"++atom_to_list(Case)) ), - ok = ftp:cd(Pid, ?config(priv_dir,Config)), + ok = ftp:cd(Pid, proplists:get_value(priv_dir,Config)), Config end. @@ -213,10 +230,10 @@ init_per_testcase(Case, Config0) -> end_per_testcase(user, _Config) -> ok; end_per_testcase(bad_user, _Config) -> ok; end_per_testcase(_Case, Config) -> - case ?config(tc_status,Config) of + case proplists:get_value(tc_status,Config) of ok -> ok; _ -> - try ftp:latest_ctrl_response(?config(ftp,Config)) + try ftp:latest_ctrl_response(proplists:get_value(ftp,Config)) of {ok,S} -> ct:log("***~n*** Latest ctrl channel response:~n*** ~p~n***",[S]) catch @@ -228,38 +245,43 @@ end_per_testcase(_Case, Config) -> %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- -user(doc) -> ["Open an ftp connection to a host, and logon as anonymous ftp, then logoff"]; +user() -> [ + {doc, "Open an ftp connection to a host, and logon as anonymous ftp," + " then logoff"}]. user(Config) -> - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS("")),% logon ok = ftp:close(Pid), % logoff {error,eclosed} = ftp:pwd(Pid), % check logoff result ok. %%------------------------------------------------------------------------- -bad_user(doc) -> ["Open an ftp connection to a host, and logon with bad user."]; +bad_user() -> + [{doc, "Open an ftp connection to a host, and logon with bad user."}]. bad_user(Config) -> - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS("")), ok. %%------------------------------------------------------------------------- -pwd(doc) -> ["Test ftp:pwd/1 & ftp:lpwd/1"]; +pwd() -> + [{doc, "Test ftp:pwd/1 & ftp:lpwd/1"}]. pwd(Config0) -> Config = set_state([reset], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), {ok, PWD} = ftp:pwd(Pid), {ok, PathLpwd} = ftp:lpwd(Pid), PWD = id2ftp_result("", Config), PathLpwd = id2ftp_result("", Config). %%------------------------------------------------------------------------- -cd(doc) -> ["Open an ftp connection, log on as anonymous ftp, and cd to a" - "directory and to a non-existent directory."]; +cd() -> + ["Open an ftp connection, log on as anonymous ftp, and cd to a" + "directory and to a non-existent directory."]. cd(Config0) -> Dir = "test", Config = set_state([reset,{mkdir,Dir}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:cd(Pid, id2ftp(Dir,Config)), {ok, PWD} = ftp:pwd(Pid), ExpectedPWD = id2ftp_result(Dir, Config), @@ -267,12 +289,12 @@ cd(Config0) -> {error, epath} = ftp:cd(Pid, ?BAD_DIR). %%------------------------------------------------------------------------- -lcd(doc) -> - ["Test api function ftp:lcd/2"]; +lcd() -> + [{doc, "Test api function ftp:lcd/2"}]. lcd(Config0) -> Dir = "test", Config = set_state([reset,{mkdir,Dir}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:lcd(Pid, id2ftp(Dir,Config)), {ok, PWD} = ftp:lpwd(Pid), ExpectedPWD = id2ftp_result(Dir, Config), @@ -280,19 +302,20 @@ lcd(Config0) -> {error, epath} = ftp:lcd(Pid, ?BAD_DIR). %%------------------------------------------------------------------------- -ls(doc) -> ["Open an ftp connection; ls the current directory, and the " - "\"test\" directory. We assume that ls never fails, since " - "it's output is meant to be read by humans. "]; +ls() -> + [{doc, "Open an ftp connection; ls the current directory, and the " + "\"test\" directory. We assume that ls never fails, since " + "it's output is meant to be read by humans. "}]. ls(Config0) -> Config = set_state([reset,{mkdir,"test"}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), {ok, _R1} = ftp:ls(Pid), {ok, _R2} = ftp:ls(Pid, id2ftp("test",Config)), %% neither nlist nor ls operates on a directory %% they operate on a pathname, which *can* be a %% directory, but can also be a filename or a group %% of files (including wildcards). - case ?config(wildcard_support, Config) of + case proplists:get_value(wildcard_support, Config) of true -> {ok, _R3} = ftp:ls(Pid, id2ftp("te*",Config)); _ -> @@ -300,20 +323,21 @@ ls(Config0) -> end. %%------------------------------------------------------------------------- -nlist(doc) -> ["Open an ftp connection; nlist the current directory, and the " +nlist() -> + [{doc,"Open an ftp connection; nlist the current directory, and the " "\"test\" directory. Nlist does not behave consistenly over " "operating systems. On some it is an error to have an empty " - "directory."]; + "directory."}]. nlist(Config0) -> Config = set_state([reset,{mkdir,"test"}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), {ok, _R1} = ftp:nlist(Pid), {ok, _R2} = ftp:nlist(Pid, id2ftp("test",Config)), %% neither nlist nor ls operates on a directory %% they operate on a pathname, which *can* be a %% directory, but can also be a filename or a group %% of files (including wildcards). - case ?config(wildcard_support, Config) of + case proplists:get_value(wildcard_support, Config) of true -> {ok, _R3} = ftp:nlist(Pid, id2ftp("te*",Config)); _ -> @@ -321,13 +345,14 @@ nlist(Config0) -> end. %%------------------------------------------------------------------------- -rename(doc) -> ["Rename a file."]; +rename() -> + [{doc, "Rename a file."}]. rename(Config0) -> Contents = <<"ftp_SUITE test ...">>, OldFile = "old.txt", NewFile = "new.txt", Config = set_state([reset,{mkfile,OldFile,Contents}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:rename(Pid, id2ftp(OldFile,Config), @@ -338,13 +363,14 @@ rename(Config0) -> %%------------------------------------------------------------------------- -send(doc) -> ["Transfer a file with ftp using send/2."]; +send() -> + [{doc, "Transfer a file with ftp using send/2."}]. send(Config0) -> Contents = <<"ftp_SUITE test ...">>, SrcDir = "data", File = "file.txt", Config = set_state([reset,{mkfile,[SrcDir,File],Contents}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), chk_no_file([File],Config), chk_file([SrcDir,File],Contents,Config), @@ -356,14 +382,15 @@ chk_file([SrcDir,File],Contents,Config), chk_file(File, Contents, Config). %%------------------------------------------------------------------------- -send_3(doc) -> ["Transfer a file with ftp using send/3."]; +send_3() -> + [{doc, "Transfer a file with ftp using send/3."}]. send_3(Config0) -> Contents = <<"ftp_SUITE test ...">>, Dir = "incoming", File = "file.txt", RemoteFile = "remfile.txt", Config = set_state([reset,{mkfile,File,Contents},{mkdir,Dir}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:cd(Pid, id2ftp(Dir,Config)), ok = ftp:lcd(Pid, id2ftp("",Config)), @@ -372,23 +399,25 @@ send_3(Config0) -> chk_file([Dir,RemoteFile], Contents, Config). %%------------------------------------------------------------------------- -send_bin(doc) -> ["Send a binary."]; +send_bin() -> + [{doc, "Send a binary."}]. send_bin(Config0) -> BinContents = <<"ftp_SUITE test ...">>, File = "file.txt", Config = set_state([reset], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), {error, enotbinary} = ftp:send_bin(Pid, "some string", id2ftp(File,Config)), ok = ftp:send_bin(Pid, BinContents, id2ftp(File,Config)), chk_file(File, BinContents, Config). %%------------------------------------------------------------------------- -send_chunk(doc) -> ["Send a binary using chunks."]; +send_chunk() -> + [{doc, "Send a binary using chunks."}]. send_chunk(Config0) -> Contents = <<"ftp_SUITE test ...">>, File = "file.txt", Config = set_state([reset,{mkdir,"incoming"}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:send_chunk_start(Pid, id2ftp(File,Config)), {error, echunk} = ftp:cd(Pid, "incoming"), @@ -399,63 +428,69 @@ send_chunk(Config0) -> chk_file(File, <<Contents/binary,Contents/binary>>, Config). %%------------------------------------------------------------------------- -delete(doc) -> ["Delete a file."]; +delete() -> + [{doc, "Delete a file."}]. delete(Config0) -> Contents = <<"ftp_SUITE test ...">>, File = "file.txt", Config = set_state([reset,{mkfile,File,Contents}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:delete(Pid, id2ftp(File,Config)), chk_no_file([File], Config). %%------------------------------------------------------------------------- -mkdir(doc) -> ["Make a remote directory."]; +mkdir() -> + [{doc, "Make a remote directory."}]. mkdir(Config0) -> NewDir = "new_dir", Config = set_state([reset], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:mkdir(Pid, id2ftp(NewDir,Config)), chk_dir([NewDir], Config). %%------------------------------------------------------------------------- -rmdir(doc) -> ["Remove a directory."]; +rmdir() -> + [{doc, "Remove a directory."}]. rmdir(Config0) -> Dir = "dir", Config = set_state([reset,{mkdir,Dir}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:rmdir(Pid, id2ftp(Dir,Config)), chk_no_dir([Dir], Config). %%------------------------------------------------------------------------- -append(doc) -> ["Append a local file twice to a remote file"]; +append() -> + [{doc, "Append a local file twice to a remote file"}]. append(Config0) -> SrcFile = "f_src.txt", DstFile = "f_dst.txt", Contents = <<"ftp_SUITE test ...">>, Config = set_state([reset,{mkfile,SrcFile,Contents}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)), ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)), chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config). %%------------------------------------------------------------------------- -append_bin(doc) -> ["Append a local file twice to a remote file using append_bin"]; +append_bin() -> + [{doc, "Append a local file twice to a remote file using append_bin"}]. append_bin(Config0) -> DstFile = "f_dst.txt", Contents = <<"ftp_SUITE test ...">>, Config = set_state([reset], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)), ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)), chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config). %%------------------------------------------------------------------------- -append_chunk(doc) -> ["Append chunks."]; +append_chunk() -> + [{doc, "Append chunks."}]. append_chunk(Config0) -> File = "f_dst.txt", Contents = [<<"ER">>,<<"LE">>,<<"RL">>], Config = set_state([reset], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:append_chunk_start(Pid, id2ftp(File,Config)), {error, enotbinary} = ftp:append_chunk(Pid, binary_to_list(lists:nth(1,Contents))), ok = ftp:append_chunk(Pid,lists:nth(1,Contents)), @@ -465,53 +500,58 @@ append_chunk(Config0) -> chk_file(File, <<"ERLERL">>, Config). %%------------------------------------------------------------------------- -recv(doc) -> ["Receive a file using recv/2"]; +recv() -> + [{doc, "Receive a file using recv/2"}]. recv(Config0) -> File = "f_dst.txt", SrcDir = "a_dir", Contents = <<"ftp_SUITE test ...">>, Config = set_state([reset, {mkfile,[SrcDir,File],Contents}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:cd(Pid, id2ftp(SrcDir,Config)), ok = ftp:lcd(Pid, id2ftp("",Config)), ok = ftp:recv(Pid, File), chk_file(File, Contents, Config). %%------------------------------------------------------------------------- -recv_3(doc) -> ["Receive a file using recv/3"]; +recv_3() -> + [{doc,"Receive a file using recv/3"}]. recv_3(Config0) -> DstFile = "f_src.txt", SrcFile = "f_dst.txt", Contents = <<"ftp_SUITE test ...">>, Config = set_state([reset, {mkfile,SrcFile,Contents}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:cd(Pid, id2ftp("",Config)), ok = ftp:recv(Pid, SrcFile, id2abs(DstFile,Config)), chk_file(DstFile, Contents, Config). %%------------------------------------------------------------------------- -recv_bin(doc) -> ["Receive a file as a binary."]; +recv_bin() -> + [{doc, "Receive a file as a binary."}]. recv_bin(Config0) -> File = "f_dst.txt", Contents = <<"ftp_SUITE test ...">>, Config = set_state([reset, {mkfile,File,Contents}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), {ok,Received} = ftp:recv_bin(Pid, id2ftp(File,Config)), find_diff(Received, Contents). %%------------------------------------------------------------------------- -recv_chunk(doc) -> ["Receive a file using chunk-wise."]; +recv_chunk() -> + [{doc, "Receive a file using chunk-wise."}]. recv_chunk(Config0) -> File = "big_file.txt", Contents = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ), Config = set_state([reset, {mkfile,File,Contents}], Config0), - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>), ok = ftp:recv_chunk_start(Pid, id2ftp(File,Config)), {ok, ReceivedContents, _Ncunks} = recv_chunk(Pid, <<>>), find_diff(ReceivedContents, Contents). -recv_chunk(Pid, Acc) -> recv_chunk(Pid, Acc, 0). +recv_chunk(Pid, Acc) -> + recv_chunk(Pid, Acc, 0). recv_chunk(Pid, Acc, N) -> case ftp:recv_chunk(Pid) of @@ -521,18 +561,18 @@ recv_chunk(Pid, Acc, N) -> end. %%------------------------------------------------------------------------- -type(doc) -> ["Test that we can change btween ASCCI and binary transfer mode"]; +type() -> + [{doc,"Test that we can change btween ASCCI and binary transfer mode"}]. type(Config) -> - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ok = ftp:type(Pid, ascii), ok = ftp:type(Pid, binary), ok = ftp:type(Pid, ascii), {error, etype} = ftp:type(Pid, foobar). %%------------------------------------------------------------------------- -quote(doc) -> [""]; quote(Config) -> - Pid = ?config(ftp, Config), + Pid = proplists:get_value(ftp, Config), ["257 \""++_Rest] = ftp:quote(Pid, "pwd"), %% 257 [_| _] = ftp:quote(Pid, "help"), %% This negativ test causes some ftp servers to hang. This test @@ -541,48 +581,6 @@ quote(Config) -> %% = ftp:quote(Pid, "list"), ok. - -%%------------------------------------------------------------------------- -ip_v6_disabled(doc) -> ["Test ipv4 command PORT"]; -ip_v6_disabled(_Config) -> - %%% FIXME!!!! What is this??? - ok.%% send(Config). - -%%------------------------------------------------------------------------- -%% big_one(doc) -> -%% ["Create a local file and transfer it to the remote host into the " -%% "the \"incoming\" directory, remove " -%% "the local file. Then open a new connection; cd to \"incoming\", " -%% "lcd to the private directory; receive the file; delete the " -%% "remote file; close connection; check that received file is in " -%% "the correct directory; cleanup." ]; -%% big_one(Config) -> -%% Pid = ?config(ftp, Config), -%% do_recv(Pid, Config). - -%% do_recv(Pid, Config) -> -%% PrivDir = ?config(priv_dir, Config), -%% File = ?config(file, Config), -%% Newfile = ?config(new_file, Config), -%% AbsFile = filename:absname(File, PrivDir), -%% Contents = "ftp_SUITE:recv test ...", -%% ok = file:write_file(AbsFile, list_to_binary(Contents)), -%% ok = ftp:cd(Pid, "incoming"), -%% ftp:delete(Pid, File), % reset -%% ftp:lcd(Pid, PrivDir), -%% ok = ftp:send(Pid, File), -%% ok = file:delete(AbsFile), % cleanup -%% test_server:sleep(100), -%% ok = ftp:lcd(Pid, PrivDir), -%% ok = ftp:recv(Pid, File), -%% {ok, Files} = file:list_dir(PrivDir), -%% true = lists:member(File, Files), -%% ok = file:delete(AbsFile), % cleanup -%% ok = ftp:recv(Pid, File, Newfile), -%% ok = ftp:delete(Pid, File), % cleanup -%% ok. - - %%-------------------------------------------------------------------- %% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- @@ -676,13 +674,114 @@ chk_no_dir(PathList, Config) -> ct:fail("Unexpected error for ~p: ~p",[Path,Error]) end. +%%------------------------------------------------------------------------- +progress_report_send() -> + [{doc, "Test the option progress for ftp:send/[2,3]"}]. +progress_report_send(Config) when is_list(Config) -> + ReportPid = + spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]), + send(Config), + receive + {ReportPid, ok} -> + ok + end. +%%------------------------------------------------------------------------- +progress_report_recv() -> + [{doc, "Test the option progress for ftp:recv/[2,3]"}]. +progress_report_recv(Config) when is_list(Config) -> + ReportPid = + spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]), + recv(Config), + receive + {ReportPid, ok} -> + ok + end. + +%%------------------------------------------------------------------------- + +not_owner() -> + [{doc, "Test what happens if a process that not owns the connection tries " + "to use it"}]. +not_owner(Config) when is_list(Config) -> + Pid = proplists:get_value(ftp, Config), + OtherPid = spawn_link(?MODULE, not_owner, [Pid, self()]), + + receive + {OtherPid, ok} -> + {ok, _} = ftp:pwd(Pid) + end. + + +%%------------------------------------------------------------------------- + + +unexpected_call()-> + [{doc, "Test that behaviour of the ftp process if the api is abused"}]. +unexpected_call(Config) when is_list(Config) -> + Flag = process_flag(trap_exit, true), + Pid = proplists:get_value(ftp, Config), + + %% Serious programming fault, connetion will be shut down + case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of + {error, {connection_terminated, 'API_violation'}} -> + ok; + Unexpected1 -> + exit({unexpected_result, Unexpected1}) + end, + ct:sleep(500), + undefined = process_info(Pid, status), + process_flag(trap_exit, Flag). +%%------------------------------------------------------------------------- + +unexpected_cast()-> + [{doc, "Test that behaviour of the ftp process if the api is abused"}]. +unexpected_cast(Config) when is_list(Config) -> + Flag = process_flag(trap_exit, true), + Pid = proplists:get_value(ftp, Config), + %% Serious programming fault, connetion will be shut down + gen_server:cast(Pid, {self(), foobar, 10}), + ct:sleep(500), + undefined = process_info(Pid, status), + process_flag(trap_exit, Flag). +%%------------------------------------------------------------------------- + +unexpected_bang()-> + [{doc, "Test that connection ignores unexpected bang"}]. +unexpected_bang(Config) when is_list(Config) -> + Flag = process_flag(trap_exit, true), + Pid = proplists:get_value(ftp, Config), + %% Could be an innocent misstake the connection lives. + Pid ! foobar, + ct:sleep(500), + {status, _} = process_info(Pid, status), + process_flag(trap_exit, Flag). + +%%------------------------------------------------------------------------- + +clean_shutdown() -> + [{doc, "Test that owning process that exits with reason " + "'shutdown' does not cause an error message. OTP 6035"}]. + +clean_shutdown(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + LogFile = filename:join([PrivDir,"ticket_6035.log"]), + Host = proplists:get_value(ftpd_host,Config), + try + Pid = spawn(?MODULE, open_wait_6035, [Host, self()]), + error_logger:logfile({open, LogFile}), + true = kill_ftp_proc_6035(Pid, LogFile), + error_logger:logfile(close) + catch + throw:{error, not_found} -> + {skip, "No available FTP servers"} + end. %%-------------------------------------------------------------------- +%% Internal functions %%-------------------------------------------------------------------- -%% find a suitable ftpd -%% + find_executable(Config) -> - FTPservers = case ?config(ftpservers,Config) of + FTPservers = case proplists:get_value(ftpservers,Config) of undefined -> ?default_ftp_servers; L -> L end, @@ -694,11 +793,9 @@ find_executable(Config) -> not_available({Name,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}) -> os:find_executable(Name) == false. -%%-------------------------------------------------------------------- -%% start/stop of ftpd -%% + start_ftpd(Config) -> - {Name,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} = ?config(ftpd_data, Config), + {Name,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} = proplists:get_value(ftpd_data, Config), case StartCmd(Config) of {ok,StartResult} -> [{ftpd_host,Host}, @@ -709,38 +806,31 @@ start_ftpd(Config) -> end. stop_ftpd(Config) -> - {_Name,_StartCmd,_ChkUp,StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config), - StopCommand(?config(ftpd_start_result,Config)). + {_Name,_StartCmd,_ChkUp,StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config), + StopCommand(proplists:get_value(ftpd_start_result,Config)). ps_ftpd(Config) -> - {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config), - ct:log( ChkUp(?config(ftpd_start_result,Config)) ). + {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config), + ct:log( ChkUp(proplists:get_value(ftpd_start_result,Config)) ). ftpd_running(Config) -> - {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config), - ChkUp(?config(ftpd_start_result,Config)). + {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config), + ChkUp(proplists:get_value(ftpd_start_result,Config)). -%%-------------------------------------------------------------------- -%% start/stop of ftpc -%% ftp__open(Config, Options) -> - Host = ?config(ftpd_host,Config), - Port = ?config(ftpd_port,Config), + Host = proplists:get_value(ftpd_host,Config), + Port = proplists:get_value(ftpd_port,Config), ct:log("Host=~p, Port=~p",[Host,Port]), {ok,Pid} = ftp:open(Host, [{port,Port} | Options]), [{ftp,Pid}|Config]. ftp__close(Config) -> - ok = ftp:close(?config(ftp,Config)), + ok = ftp:close(proplists:get_value(ftp,Config)), Config. -%%-------------------------------------------------------------------- -%% split(Cs) -> string:tokens(Cs, "\r\n"). -%%-------------------------------------------------------------------- -%% find_diff(Bin1, Bin2) -> case find_diff(Bin1, Bin2, 1) of {error, {diff,Pos,RC,LC}} -> @@ -753,15 +843,14 @@ find_diff(Bin1, Bin2) -> find_diff(A, A, _) -> true; find_diff(<<H,T1/binary>>, <<H,T2/binary>>, Pos) -> find_diff(T1, T2, Pos+1); find_diff(RC, LC, Pos) -> {error, {diff, Pos, RC, LC}}. -%%-------------------------------------------------------------------- -%% + set_state(Ops, Config) when is_list(Ops) -> lists:foldl(fun set_state/2, Config, Ops); set_state(reset, Config) -> rm('*', id2abs("",Config)), - PrivDir = ?config(priv_dir,Config), + PrivDir = proplists:get_value(priv_dir,Config), file:set_cwd(PrivDir), - ftp:lcd(?config(ftp,Config),PrivDir), + ftp:lcd(proplists:get_value(ftp,Config),PrivDir), set_state({mkdir,""},Config); set_state({mkdir,Id}, Config) -> Abs = id2abs(Id, Config), @@ -787,7 +876,6 @@ mk_path(F, Pfx) -> AbsName end. - rm('*', Pfx) -> {ok,Fs} = file:list_dir(Pfx), lists:foreach(fun(F) -> rm(F, Pfx) end, Fs); @@ -805,17 +893,115 @@ rm(F, Pfx) -> ok end. -%%-------------------------------------------------------------------- -%% +not_owner(FtpPid, Pid) -> + {error, not_connection_owner} = ftp:pwd(FtpPid), + ftp:close(FtpPid), + ct:sleep(100), + Pid ! {self(), ok}. -id2abs(Id, Conf) -> filename:join(?config(priv_dir,Conf),ids(Id)). -id2ftp(Id, Conf) -> (?config(id2ftp,Conf))(ids(Id)). -id2ftp_result(Id, Conf) -> (?config(id2ftp_result,Conf))(ids(Id)). +id2abs(Id, Conf) -> filename:join(proplists:get_value(priv_dir,Conf),ids(Id)). +id2ftp(Id, Conf) -> (proplists:get_value(id2ftp,Conf))(ids(Id)). +id2ftp_result(Id, Conf) -> (proplists:get_value(id2ftp_result,Conf))(ids(Id)). ids([[_|_]|_]=Ids) -> filename:join(Ids); ids(Id) -> Id. -is_expected_absName(Id, File, Conf) -> File = (?config(id2abs,Conf))(Id). -is_expected_ftpInName(Id, File, Conf) -> File = (?config(id2ftp,Conf))(Id). -is_expected_ftpOutName(Id, File, Conf) -> File = (?config(id2ftp_result,Conf))(Id). +is_expected_absName(Id, File, Conf) -> File = (proplists:get_value(id2abs,Conf))(Id). +is_expected_ftpInName(Id, File, Conf) -> File = (proplists:get_value(id2ftp,Conf))(Id). +is_expected_ftpOutName(Id, File, Conf) -> File = (proplists:get_value(id2ftp_result,Conf))(Id). + + +progress(#progress{} = Progress , _File, {file_size, Total}) -> + progress_report_receiver ! start, + Progress#progress{total = Total}; + +progress(#progress{total = Total, current = Current} + = Progress, _File, {transfer_size, 0}) -> + progress_report_receiver ! finish, + case Total of + unknown -> + ok; + Current -> + ok; + _ -> + ct:fail({error, {progress, {total, Total}, + {current, Current}}}) + end, + Progress; +progress(#progress{current = Current} = Progress, _File, + {transfer_size, Size}) -> + progress_report_receiver ! update, + Progress#progress{current = Current + Size}. + +progress_report_receiver_init(Pid, N) -> + register(progress_report_receiver, self()), + receive + start -> + ok + end, + progress_report_receiver_loop(Pid, N-1). + +progress_report_receiver_loop(Pid, N) -> + receive + update -> + progress_report_receiver_loop(Pid, N); + finish when N =:= 0 -> + Pid ! {self(), ok}; + finish -> + Pid ! {self(), ok}, + receive + start -> + ok + end, + progress_report_receiver_loop(Pid, N-1) + end. + +kill_ftp_proc_6035(Pid, LogFile) -> + receive + open -> + exit(Pid, shutdown), + kill_ftp_proc_6035(Pid, LogFile); + {open_failed, Reason} -> + exit({skip, {failed_openening_server_connection, Reason}}) + after + 5000 -> + is_error_report_6035(LogFile) + end. + +open_wait_6035({_Tag, FtpServer}, From) -> + case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of + {ok, Pid} -> + _LoginResult = ftp:user(Pid,"anonymous","kldjf"), + From ! open, + receive + dummy -> + ok + after + 10000 -> + ok + end, + ok; + {error, Reason} -> + From ! {open_failed, {Reason, FtpServer}}, + ok + end. + +is_error_report_6035(LogFile) -> + Res = + case file:read_file(LogFile) of + {ok, Bin} -> + Txt = binary_to_list(Bin), + read_log_6035(Txt); + _ -> + false + end, + %% file:delete(LogFile), + Res. + +read_log_6035("=ERROR REPORT===="++_Rest) -> + true; +read_log_6035([_|T]) -> + read_log_6035(T); +read_log_6035([]) -> + false. diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl index cbe12e566b..2c17e2657c 100644 --- a/lib/inets/test/ftp_format_SUITE.erl +++ b/lib/inets/test/ftp_format_SUITE.erl @@ -24,18 +24,13 @@ -include_lib("common_test/include/ct.hrl"). -include("ftp_internal.hrl"). -%% Test server specific exports --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2]). +%% Note: This directive should only be used in test suites. +-compile(export_all). -%% Test cases must be exported. --export([ ftp_150/1, - ftp_200/1, ftp_220/1, ftp_226/1, ftp_257/1, ftp_331/1, ftp_425/1, - ftp_other_status_codes/1, ftp_multiple_lines/1, - ftp_multipel_ctrl_messages/1, format_error/1]). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{seconds,5}} + ]. all() -> [{group, ftp_response}, format_error]. @@ -60,23 +55,16 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_, Config) -> - Dog = test_server:timetrap(?t:minutes(1)), - NewConfig = lists:keydelete(watchdog, 1, Config), - [{watchdog, Dog} | NewConfig]. - -end_per_testcase(_, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), + Config. +end_per_testcase(_, _) -> ok. %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -ftp_150(doc) -> - ["Especially check that respons can be devided in a random place."]; -ftp_150(suite) -> - []; +ftp_150() -> + [{doc, "Especially check that respons can be devided in a random place."}]. ftp_150(Config) when is_list(Config) -> FtpResponse = ["150 ASCII data conn", "ection for /bin/ls ", "(134.138.177", ".89,50434) (0 bytes).\r\n"], @@ -84,14 +72,11 @@ ftp_150(Config) when is_list(Config) -> "150 ASCII data connection for /bin/ls " "(134.138.177.89,50434) (0 bytes).\r\n" = Msg = parse(ftp_response, parse_lines, [[], start], FtpResponse), - {pos_prel, _} = ftp_response:interpret(Msg), - ok. - -ftp_200(doc) -> - ["Especially check that respons can be devided after the first status " - "code character and in the end delimiter."]; -ftp_200(suite) -> - []; + {pos_prel, _} = ftp_response:interpret(Msg). + +ftp_200() -> + [{doc, "Especially check that respons can be devided after the first status " + "code character and in the end delimiter."}]. ftp_200(Config) when is_list(Config) -> FtpResponse = ["2", "00 PORT command successful.", [?CR], [?LF]], @@ -100,11 +85,9 @@ ftp_200(Config) when is_list(Config) -> {pos_compl, _} = ftp_response:interpret(Msg), ok. -ftp_220(doc) -> - ["Especially check that respons can be devided after the " - "first with space "]; -ftp_220(suite) -> - []; +ftp_220() -> + [{doc, "Especially check that respons can be devided after the " + "first with space "}]. ftp_220(Config) when is_list(Config) -> FtpResponse = ["220 ","fingon FTP server (SunOS 5.8) ready.\r\n"], @@ -113,11 +96,9 @@ ftp_220(Config) when is_list(Config) -> {pos_compl, _} = ftp_response:interpret(Msg), ok. -ftp_226(doc) -> - ["Especially check that respons can be devided after second status code" - " character and in the end delimiter."]; -ftp_226(suite) -> - []; +ftp_226() -> + [{doc, "Especially check that respons can be devided after second status code" + " character and in the end delimiter."}]. ftp_226(Config) when is_list(Config) -> FtpResponse = ["22" "6 Transfer complete.\r", [?LF]], @@ -126,10 +107,8 @@ ftp_226(Config) when is_list(Config) -> {pos_compl, _} = ftp_response:interpret(Msg), ok. -ftp_257(doc) -> - ["Especially check that quoted chars do not cause a problem."]; -ftp_257(suite) -> - []; +ftp_257() -> + [{doc, "Especially check that quoted chars do not cause a problem."}]. ftp_257(Config) when is_list(Config) -> FtpResponse = ["257 \"/\" is current directory.\r\n"], @@ -138,11 +117,9 @@ ftp_257(Config) when is_list(Config) -> {pos_compl, _} = ftp_response:interpret(Msg), ok. -ftp_331(doc) -> - ["Especially check that respons can be devided after the third status " - " status code character."]; -ftp_331(suite) -> - []; +ftp_331() -> + [{doc, "Especially check that respons can be devided after the third status " + " status code character."}]. ftp_331(Config) when is_list(Config) -> %% Brake before white space after code FtpResponse = @@ -153,10 +130,8 @@ ftp_331(Config) when is_list(Config) -> {pos_interm, _} = ftp_response:interpret(Msg), ok. -ftp_425(doc) -> - ["Especially check a message that was received in only one part."]; -ftp_425(suite) -> - []; +ftp_425() -> + [{doc, "Especially check a message that was received in only one part."}]. ftp_425(Config) when is_list(Config) -> FtpResponse = ["425 Can't build data connection: Connection refused.\r\n"], @@ -166,10 +141,8 @@ ftp_425(Config) when is_list(Config) -> {trans_neg_compl, _} = ftp_response:interpret(Msg), ok. -ftp_multiple_lines(doc) -> - ["Especially check multiple lines devided in significant places"]; -ftp_multiple_lines(suite) -> - []; +ftp_multiple_lines() -> + [{doc, "Especially check multiple lines devided in significant places"}]. ftp_multiple_lines(Config) when is_list(Config) -> FtpResponse = ["21", "4","-The", " following commands are recognized:\r\n" @@ -247,13 +220,11 @@ ftp_multiple_lines(Config) when is_list(Config) -> FtpResponse2), ok. -ftp_other_status_codes(doc) -> - ["Check that other valid status codes, than the ones above, are handled" +ftp_other_status_codes() -> + [{doc, "Check that other valid status codes, than the ones above, are handled" "by ftp_response:interpret/1. Note there are som ftp status codes" "that will not be received with the current ftp instruction support," - "they are not included here."]; -ftp_other_status_codes(suite) -> - []; + "they are not included here."}]. ftp_other_status_codes(Config) when is_list(Config) -> %% 1XX @@ -289,11 +260,9 @@ ftp_other_status_codes(Config) when is_list(Config) -> {efnamena, _ } = ftp_response:interpret("553 Foobar\r\n"), ok. -ftp_multipel_ctrl_messages(doc) -> - ["The ftp server may send more than one control message as a reply," - "check that they are handled one at the time."]; -ftp_multipel_ctrl_messages(suite) -> - []; +ftp_multipel_ctrl_messages() -> + [{doc, "The ftp server may send more than one control message as a reply," + "check that they are handled one at the time."}]. ftp_multipel_ctrl_messages(Config) when is_list(Config) -> FtpResponse = ["200 PORT command successful.\r\n200 Foobar\r\n"], @@ -306,10 +275,6 @@ ftp_multipel_ctrl_messages(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -format_error(doc) -> - [""]; -format_error(suite) -> - []; format_error(Config) when is_list(Config) -> "Synchronisation error during chunk sending." = ftp:formaterror(echunk), @@ -346,7 +311,7 @@ parse(Module, Function, [AccLines, StatusCode], [Data | Rest]) -> {continue, {NewData, NewAccLines, NewStatusCode}} -> case Rest of [] -> - test_server:fail({wrong_input, Data, Rest}); + ct:fail({wrong_input, Data, Rest}); [_ | _] -> parse(Module, Function, [NewAccLines, NewStatusCode], [binary_to_list(NewData) ++ hd(Rest) | tl(Rest)]) diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl deleted file mode 100644 index 0f82b1c1c3..0000000000 --- a/lib/inets/test/ftp_suite_lib.erl +++ /dev/null @@ -1,1695 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2016. 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(ftp_suite_lib). - - --include_lib("common_test/include/ct.hrl"). --include("inets_test_lib.hrl"). - -%% Test server specific exports -% -export([init_per_testcase/2, end_per_testcase/2]). - --compile(export_all). - - --record(progress, { - current = 0, - total - }). - - - --define(FTP_USER, "anonymous"). --define(FTP_PASS, passwd()). --define(FTP_PORT, 21). - --define(BAD_HOST, "badhostname"). --define(BAD_USER, "baduser"). --define(BAD_DIR, "baddirectory"). - --ifdef(ftp_debug_client). --define(ftp_open(Host, Flags), - do_ftp_open(Host, [{debug, debug}, - {timeout, timer:seconds(15)} | Flags])). --else. --ifdef(ftp_trace_client). --define(ftp_open(Host, Flags), - do_ftp_open(Host, [{debug, trace}, - {timeout, timer:seconds(15)} | Flags])). --else. --define(ftp_open(Host, Flags), - do_ftp_open(Host, [{verbose, true}, - {timeout, timer:seconds(15)} | Flags])). --endif. --endif. - -%% -- Tickets -- - -tickets(doc) -> - "Test cases for reported bugs"; -tickets(suite) -> - [ticket_6035]. - -%% -- - -ftpd_init(FtpdTag, Config) -> - %% Get the host name(s) of FTP server - Hosts = - case ct:get_config(ftpd_hosts) of - undefined -> - ftpd_hosts(data_dir(Config)); - H -> - H - end, - p("ftpd_init -> " - "~n Hosts: ~p" - "~n Config: ~p" - "~n FtpdTag: ~p", [Hosts, Config, FtpdTag]), - %% Get the first host that actually have a running FTP server - case lists:keysearch(FtpdTag, 1, Hosts) of - {value, {_, TagHosts}} when is_list(TagHosts) -> - inets:start(), - case (catch get_ftpd_host(TagHosts)) of - {ok, Host} -> - inets:stop(), - [{ftp_remote_host, Host}|Config]; - _ -> - inets:stop(), - Reason = lists:flatten( - io_lib:format("Could not find a valid " - "FTP server for ~p (~p)", - [FtpdTag, TagHosts])), - {skip, Reason} - end; - _ -> - Reason = lists:flatten( - io_lib:format("No host(s) running FTPD server " - "for ~p", [FtpdTag])), - {skip, Reason} - end. - -ftpd_fin(Config) -> - lists:keydelete(ftp_remote_host, 1, Config). - -get_ftpd_host([]) -> - {error, no_host}; -get_ftpd_host([Host|Hosts]) -> - p("get_ftpd_host -> entry with" - "~n Host: ~p" - "~n", [Host]), - case (catch ftp:open(Host, [{port, ?FTP_PORT}, {timeout, 20000}])) of - {ok, Pid} -> - (catch ftp:close(Pid)), - {ok, Host}; - _ -> - get_ftpd_host(Hosts) - end. - - -%%-------------------------------------------------------------------- - -dirty_select_ftpd_host(Config) -> - Hosts = - case ct:get_config(ftpd_hosts) of - undefined -> - ftpd_hosts(data_dir(Config)); - H -> - H - end, - dirty_select_ftpd_host2(Hosts). - -dirty_select_ftpd_host2([]) -> - throw({error, not_found}); -dirty_select_ftpd_host2([{PlatformTag, Hosts} | PlatformHosts]) -> - case dirty_select_ftpd_host3(Hosts) of - none -> - dirty_select_ftpd_host2(PlatformHosts); - {ok, Host} -> - {PlatformTag, Host} - end. - -dirty_select_ftpd_host3([]) -> - none; -dirty_select_ftpd_host3([Host|Hosts]) when is_list(Host) -> - case dirty_select_ftpd_host4(Host) of - true -> - {ok, Host}; - false -> - dirty_select_ftpd_host3(Hosts) - end; -dirty_select_ftpd_host3([_|Hosts]) -> - dirty_select_ftpd_host3(Hosts). - -%% This is a very simple and dirty test that there is a -%% (FTP) deamon on the other end. -dirty_select_ftpd_host4(Host) -> - Port = 21, - IpFam = inet, - Opts = [IpFam, binary, {packet, 0}, {active, false}], - Timeout = ?SECS(5), - case gen_tcp:connect(Host, Port, Opts, Timeout) of - {ok, Sock} -> - gen_tcp:close(Sock), - true; - _Error -> - false - end. - - -%%-------------------------------------------------------------------- - -test_filenames() -> - {ok, Host} = inet:gethostname(), - File = Host ++ "_ftp_test.txt", - NewFile = "new_" ++ File, - {File, NewFile}. - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(Case, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) - when (Case =:= open) orelse - (Case =:= open_port) -> - put(ftp_testcase, Case), - io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), - inets:start(), - NewConfig = data_dir(Config), - watch_dog(NewConfig); - -init_per_testcase(Case, Config) -> - put(ftp_testcase, Case), - do_init_per_testcase(Case, Config). - -do_init_per_testcase(Case, Config) - when (Case =:= passive_user) -> - io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]), - inets:start(), - NewConfig = close_connection(watch_dog(Config)), - Host = ftp_host(Config), - case (catch ?ftp_open(Host, [{mode, passive}])) of - {ok, Pid} -> - [{ftp, Pid} | data_dir(NewConfig)]; - {skip, _} = SKIP -> - SKIP - end; - -do_init_per_testcase(Case, Config) - when (Case =:= active_user) -> - io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), - inets:start(), - NewConfig = close_connection(watch_dog(Config)), - Host = ftp_host(Config), - case (catch ?ftp_open(Host, [{mode, active}])) of - {ok, Pid} -> - [{ftp, Pid} | data_dir(NewConfig)]; - {skip, _} = SKIP -> - SKIP - end; - -do_init_per_testcase(Case, Config) - when (Case =:= progress_report_send) orelse - (Case =:= progress_report_recv) -> - inets:start(), - io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), - NewConfig = close_connection(watch_dog(Config)), - Host = ftp_host(Config), - Opts = [{port, ?FTP_PORT}, - {verbose, true}, - {progress, {?MODULE, progress, #progress{}}}], - case ftp:open(Host, Opts) of - {ok, Pid} -> - ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), - [{ftp, Pid} | data_dir(NewConfig)]; - {skip, _} = SKIP -> - SKIP - end; - -do_init_per_testcase(Case, Config) -> - io:format(user,"~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), - inets:start(), - NewConfig = close_connection(watch_dog(Config)), - Host = ftp_host(Config), - Opts1 = - if - ((Case =:= passive_ip_v6_disabled) orelse - (Case =:= active_ip_v6_disabled)) -> - [{ipfamily, inet}]; - true -> - [] - end, - Opts2 = - case string:tokens(atom_to_list(Case), [$_]) of - ["active" | _] -> - [{mode, active} | Opts1]; - _ -> - [{mode, passive} | Opts1] - end, - case (catch ?ftp_open(Host, Opts2)) of - {ok, Pid} -> - ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), - [{ftp, Pid} | data_dir(NewConfig)]; - {skip, _} = SKIP -> - SKIP - end. - - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(Case, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(_, Config) -> - NewConfig = close_connection(Config), - Dog = ?config(watchdog, NewConfig), - inets:stop(), - test_server:timetrap_cancel(Dog), - ok. - - -%%------------------------------------------------------------------------- -%% Suites similar for all hosts. -%%------------------------------------------------------------------------- - -passive(suite) -> - [ - passive_user, - passive_pwd, - passive_cd, - passive_lcd, - passive_ls, - passive_nlist, - passive_rename, - passive_delete, - passive_mkdir, - passive_send, - passive_send_bin, - passive_send_chunk, - passive_append, - passive_append_bin, - passive_append_chunk, - passive_recv, - passive_recv_bin, - passive_recv_chunk, - passive_type, - passive_quote, - passive_ip_v6_disabled - ]. - -active(suite) -> - [ - active_user, - active_pwd, - active_cd, - active_lcd, - active_ls, - active_nlist, - active_rename, - active_delete, - active_mkdir, - active_send, - active_send_bin, - active_send_chunk, - active_append, - active_append_bin, - active_append_chunk, - active_recv, - active_recv_bin, - active_recv_chunk, - active_type, - active_quote, - active_ip_v6_disabled - ]. - - - -%%------------------------------------------------------------------------- -%% Test cases starts here. -%%------------------------------------------------------------------------- - -open(doc) -> - ["Open an ftp connection to a host and close the connection." - "Also check that !-messages does not disturbe the connection"]; -open(suite) -> - []; -open(Config) when is_list(Config) -> - Host = ftp_host(Config), - (catch tc_open(Host)). - - -tc_open(Host) -> - p("tc_open -> entry with" - "~n Host: ~p", [Host]), - {ok, Pid} = ?ftp_open(Host, []), - ok = ftp:close(Pid), - p("tc_open -> try (ok) open 1"), - {ok, Pid1} = - ftp:open({option_list, [{host,Host}, - {port, ?FTP_PORT}, - {flags, [verbose]}, - {timeout, 30000}]}), - ok = ftp:close(Pid1), - - p("tc_open -> try (fail) open 2"), - {error, ehost} = - ftp:open({option_list, [{port, ?FTP_PORT}, {flags, [verbose]}]}), - {ok, Pid2} = ftp:open(Host), - ok = ftp:close(Pid2), - - p("tc_open -> try (ok) open 3"), - {ok, NewHost} = inet:getaddr(Host, inet), - {ok, Pid3} = ftp:open(NewHost), - ftp:user(Pid3, ?FTP_USER, ?FTP_PASS), - Pid3 ! foobar, - test_server:sleep(5000), - {message_queue_len, 0} = process_info(self(), message_queue_len), - ["200" ++ _] = ftp:quote(Pid3, "NOOP"), - ok = ftp:close(Pid3), - - %% Bad input that has default values are ignored and the defult - %% is used. - p("tc_open -> try (ok) open 4"), - {ok, Pid4} = - ftp:open({option_list, [{host, Host}, - {port, badarg}, - {flags, [verbose]}, - {timeout, 30000}]}), - test_server:sleep(100), - ok = ftp:close(Pid4), - - p("tc_open -> try (ok) open 5"), - {ok, Pid5} = - ftp:open({option_list, [{host, Host}, - {port, ?FTP_PORT}, - {flags, [verbose]}, - {timeout, -42}]}), - test_server:sleep(100), - ok = ftp:close(Pid5), - - p("tc_open -> try (ok) open 6"), - {ok, Pid6} = - ftp:open({option_list, [{host, Host}, - {port, ?FTP_PORT}, - {flags, [verbose]}, - {mode, cool}]}), - test_server:sleep(100), - ok = ftp:close(Pid6), - - p("tc_open -> try (ok) open 7"), - {ok, Pid7} = - ftp:open(Host, [{port, ?FTP_PORT}, {verbose, true}, {timeout, 30000}]), - ok = ftp:close(Pid7), - - p("tc_open -> try (ok) open 8"), - {ok, Pid8} = - ftp:open(Host, ?FTP_PORT), - ok = ftp:close(Pid8), - - p("tc_open -> try (ok) open 9"), - {ok, Pid9} = - ftp:open(Host, [{port, ?FTP_PORT}, - {verbose, true}, - {timeout, 30000}, - {dtimeout, -99}]), - ok = ftp:close(Pid9), - - p("tc_open -> try (ok) open 10"), - {ok, Pid10} = - ftp:open(Host, [{port, ?FTP_PORT}, - {verbose, true}, - {timeout, 30000}, - {dtimeout, "foobar"}]), - ok = ftp:close(Pid10), - - p("tc_open -> try (ok) open 11"), - {ok, Pid11} = - ftp:open(Host, [{port, ?FTP_PORT}, - {verbose, true}, - {timeout, 30000}, - {dtimeout, 1}]), - ok = ftp:close(Pid11), - - p("tc_open -> done"), - ok. - - -%%------------------------------------------------------------------------- - -open_port(doc) -> - ["Open an ftp connection to a host with given port number " - "and close the connection."]; % See also OTP-3892 -open_port(suite) -> - []; -open_port(Config) when is_list(Config) -> - Host = ftp_host(Config), - {ok, Pid} = ftp:open(Host, [{port, ?FTP_PORT}]), - ok = ftp:close(Pid), - {error, ehost} = ftp:open(?BAD_HOST, []), - ok. - - -%%------------------------------------------------------------------------- - -passive_user(doc) -> - ["Open an ftp connection to a host, and logon as anonymous ftp."]; -passive_user(suite) -> - []; -passive_user(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - p("Pid: ~p",[Pid]), - do_user(Pid). - - -%%------------------------------------------------------------------------- - -passive_pwd(doc) -> - ["Test ftp:pwd/1 & ftp:lpwd/1"]; -passive_pwd(suite) -> - []; -passive_pwd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_pwd(Pid). - - -%%------------------------------------------------------------------------- - -passive_cd(doc) -> - ["Open an ftp connection, log on as anonymous ftp, and cd to the" - "directory \"/pub\" and the to the non-existent directory."]; -passive_cd(suite) -> - []; -passive_cd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_cd(Pid). - - -%%------------------------------------------------------------------------- - -passive_lcd(doc) -> - ["Test api function ftp:lcd/2"]; -passive_lcd(suite) -> - []; -passive_lcd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - PrivDir = ?config(priv_dir, Config), - do_lcd(Pid, PrivDir). - - -%%------------------------------------------------------------------------- - -passive_ls(doc) -> - ["Open an ftp connection; ls the current directory, and the " - "\"incoming\" directory. We assume that ls never fails, since " - "it's output is meant to be read by humans. "]; -passive_ls(suite) -> - []; -passive_ls(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_ls(Pid). - - -%%------------------------------------------------------------------------- - -passive_nlist(doc) -> - ["Open an ftp connection; nlist the current directory, and the " - "\"incoming\" directory. Nlist does not behave consistenly over " - "operating systems. On some it is an error to have an empty " - "directory."]; -passive_nlist(suite) -> - []; -passive_nlist(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - WildcardSupport = ?config(wildcard_support, Config), - do_nlist(Pid, WildcardSupport). - - -%%------------------------------------------------------------------------- - -passive_rename(doc) -> - ["Transfer a file to the server, and rename it; then remove it."]; -passive_rename(suite) -> - []; -passive_rename(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_rename(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_delete(doc) -> - ["Transfer a file to the server, and then delete it"]; -passive_delete(suite) -> - []; -passive_delete(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_delete(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_mkdir(doc) -> - ["Make a remote directory, cd to it, go to parent directory, and " - "remove the directory."]; -passive_mkdir(suite) -> - []; -passive_mkdir(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_mkdir(Pid). - - -%%------------------------------------------------------------------------- - -passive_send(doc) -> - ["Create a local file in priv_dir; open an ftp connection to a host; " - "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " - "priv_dir; send the file; get a directory listing and check that " - "the file is on the list;, delete the remote file; get another listing " - "and check that the file is not on the list; close the session; " - "delete the local file."]; -passive_send(suite) -> - []; -passive_send(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_append(doc) -> - ["Create a local file in priv_dir; open an ftp connection to a host; " - "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " - "priv_dir; append the file to a file at the remote side that not exits" - "this will create the file at the remote side. Then it append the file " - "again. When this is done it recive the remote file and control that" - "the content is doubled in it.After that it will remove the files"]; -passive_append(suite) -> - []; -passive_append(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_send_bin(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "send a binary; remove file; close the connection."]; -passive_send_bin(suite) -> - []; -passive_send_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send_bin(Pid, Config). - -%%------------------------------------------------------------------------- - -passive_append_bin(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "append a binary twice; get the file and compare the content" - "remove file; close the connection."]; -passive_append_bin(suite) -> - []; -passive_append_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append_bin(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_send_chunk(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "send chunks; remove file; close the connection."]; -passive_send_chunk(suite) -> - []; -passive_send_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_append_chunk(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "append chunks;control content remove file; close the connection."]; -passive_append_chunk(suite) -> - []; -passive_append_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_recv(doc) -> - ["Create a local file and transfer it to the remote host into the " - "the \"incoming\" directory, remove " - "the local file. Then open a new connection; cd to \"incoming\", " - "lcd to the private directory; receive the file; delete the " - "remote file; close connection; check that received file is in " - "the correct directory; cleanup." ]; -passive_recv(suite) -> - []; -passive_recv(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_recv_bin(doc) -> - ["Send a binary to the remote host; and retreive " - "the file; then remove the file."]; -passive_recv_bin(suite) -> - []; -passive_recv_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv_bin(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_recv_chunk(doc) -> - ["Send a binary to the remote host; Connect again, and retreive " - "the file; then remove the file."]; -passive_recv_chunk(suite) -> - []; -passive_recv_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_type(doc) -> - ["Test that we can change btween ASCCI and binary transfer mode"]; -passive_type(suite) -> - []; -passive_type(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_type(Pid). - - -%%------------------------------------------------------------------------- - -passive_quote(doc) -> - [""]; -passive_quote(suite) -> - []; -passive_quote(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_quote(Pid). - - -%%------------------------------------------------------------------------- - -passive_ip_v6_disabled(doc) -> - ["Test ipv4 command PASV"]; -passive_ip_v6_disabled(suite) -> - []; -passive_ip_v6_disabled(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_user(doc) -> - ["Open an ftp connection to a host, and logon as anonymous ftp."]; -active_user(suite) -> - []; -active_user(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_user(Pid). - - -%%------------------------------------------------------------------------- - -active_pwd(doc) -> - ["Test ftp:pwd/1 & ftp:lpwd/1"]; -active_pwd(suite) -> - []; -active_pwd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_pwd(Pid). - - -%%------------------------------------------------------------------------- - -active_cd(doc) -> - ["Open an ftp connection, log on as anonymous ftp, and cd to the" - "directory \"/pub\" and to a non-existent directory."]; -active_cd(suite) -> - []; -active_cd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_cd(Pid). - - -%%------------------------------------------------------------------------- - -active_lcd(doc) -> - ["Test api function ftp:lcd/2"]; -active_lcd(suite) -> - []; -active_lcd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - PrivDir = ?config(priv_dir, Config), - do_lcd(Pid, PrivDir). - - -%%------------------------------------------------------------------------- - -active_ls(doc) -> - ["Open an ftp connection; ls the current directory, and the " - "\"incoming\" directory. We assume that ls never fails, since " - "it's output is meant to be read by humans. "]; -active_ls(suite) -> - []; -active_ls(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_ls(Pid). - - -%%------------------------------------------------------------------------- - -active_nlist(doc) -> - ["Open an ftp connection; nlist the current directory, and the " - "\"incoming\" directory. Nlist does not behave consistenly over " - "operating systems. On some it is an error to have an empty " - "directory."]; -active_nlist(suite) -> - []; -active_nlist(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - WildcardSupport = ?config(wildcard_support, Config), - do_nlist(Pid, WildcardSupport). - - -%%------------------------------------------------------------------------- - -active_rename(doc) -> - ["Transfer a file to the server, and rename it; then remove it."]; -active_rename(suite) -> - []; -active_rename(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_rename(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_delete(doc) -> - ["Transfer a file to the server, and then delete it"]; -active_delete(suite) -> - []; -active_delete(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_delete(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_mkdir(doc) -> - ["Make a remote directory, cd to it, go to parent directory, and " - "remove the directory."]; -active_mkdir(suite) -> - []; -active_mkdir(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_mkdir(Pid). - - -%%------------------------------------------------------------------------- - -active_send(doc) -> - ["Create a local file in priv_dir; open an ftp connection to a host; " - "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " - "priv_dir; send the file; get a directory listing and check that " - "the file is on the list;, delete the remote file; get another listing " - "and check that the file is not on the list; close the session; " - "delete the local file."]; -active_send(suite) -> - []; -active_send(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_append(doc) -> - ["Create a local file in priv_dir; open an ftp connection to a host; " - "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " - "priv_dir; append the file to a file at the remote side that not exits" - "this will create the file at the remote side. Then it append the file " - "again. When this is done it recive the remote file and control that" - "the content is doubled in it.After that it will remove the files"]; -active_append(suite) -> - []; -active_append(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_send_bin(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "send a binary; remove file; close the connection."]; -active_send_bin(suite) -> - []; -active_send_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send_bin(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_append_bin(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "append a binary twice; get the file and compare the content" - "remove file; close the connection."]; -active_append_bin(suite) -> - []; -active_append_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append_bin(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_send_chunk(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "send chunks; remove file; close the connection."]; -active_send_chunk(suite) -> - []; -active_send_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_append_chunk(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "append chunks;control content remove file; close the connection."]; -active_append_chunk(suite) -> - []; -active_append_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_recv(doc) -> - ["Create a local file and transfer it to the remote host into the " - "the \"incoming\" directory, remove " - "the local file. Then open a new connection; cd to \"incoming\", " - "lcd to the private directory; receive the file; delete the " - "remote file; close connection; check that received file is in " - "the correct directory; cleanup." ]; -active_recv(suite) -> - []; -active_recv(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_recv_bin(doc) -> - ["Send a binary to the remote host; and retreive " - "the file; then remove the file."]; -active_recv_bin(suite) -> - []; -active_recv_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv_bin(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_recv_chunk(doc) -> - ["Send a binary to the remote host; Connect again, and retreive " - "the file; then remove the file."]; -active_recv_chunk(suite) -> - []; -active_recv_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_type(doc) -> - ["Test that we can change btween ASCCI and binary transfer mode"]; -active_type(suite) -> - []; -active_type(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_type(Pid). - - -%%------------------------------------------------------------------------- - -active_quote(doc) -> - [""]; -active_quote(suite) -> - []; -active_quote(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_quote(Pid). - - -%%------------------------------------------------------------------------- - -active_ip_v6_disabled(doc) -> - ["Test ipv4 command PORT"]; -active_ip_v6_disabled(suite) -> - []; -active_ip_v6_disabled(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send(Pid, Config). - - -%%------------------------------------------------------------------------- - -api_missuse(doc)-> - ["Test that behaviour of the ftp process if the api is abused"]; -api_missuse(suite) -> []; -api_missuse(Config) when is_list(Config) -> - p("api_missuse -> entry"), - Flag = process_flag(trap_exit, true), - Pid = ?config(ftp, Config), - Host = ftp_host(Config), - - %% Serious programming fault, connetion will be shut down - p("api_missuse -> verify bad call termination (~p)", [Pid]), - case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of - {error, {connection_terminated, 'API_violation'}} -> - ok; - Unexpected1 -> - exit({unexpected_result, Unexpected1}) - end, - test_server:sleep(500), - undefined = process_info(Pid, status), - - p("api_missuse -> start new client"), - {ok, Pid2} = ?ftp_open(Host, []), - %% Serious programming fault, connetion will be shut down - p("api_missuse -> verify bad cast termination"), - gen_server:cast(Pid2, {self(), foobar, 10}), - test_server:sleep(500), - undefined = process_info(Pid2, status), - - p("api_missuse -> start new client"), - {ok, Pid3} = ?ftp_open(Host, []), - %% Could be an innocent misstake the connection lives. - p("api_missuse -> verify bad bang"), - Pid3 ! foobar, - test_server:sleep(500), - {status, _} = process_info(Pid3, status), - process_flag(trap_exit, Flag), - p("api_missuse -> done"), - ok. - - -%%------------------------------------------------------------------------- - -not_owner(doc) -> - ["Test what happens if a process that not owns the connection tries " - "to use it"]; -not_owner(suite) -> - []; -not_owner(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - OtherPid = spawn_link(?MODULE, not_owner, [Pid, self()]), - - receive - {OtherPid, ok} -> - {ok, _} = ftp:pwd(Pid) - end, - ok. - -not_owner(FtpPid, Pid) -> - {error, not_connection_owner} = ftp:pwd(FtpPid), - ftp:close(FtpPid), - test_server:sleep(100), - Pid ! {self(), ok}. - - -%%------------------------------------------------------------------------- - - -progress_report(doc) -> - ["Solaris 8 sparc test the option progress."]; -progress_report(suite) -> - [progress_report_send, progress_report_recv]. - - -%% -- - -progress_report_send(doc) -> - ["Test the option progress for ftp:send/[2,3]"]; -progress_report_send(suite) -> - []; -progress_report_send(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - ReportPid = - spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]), - do_send(Pid, Config), - receive - {ReportPid, ok} -> - ok - end. - - -%% -- - -progress_report_recv(doc) -> - ["Test the option progress for ftp:recv/[2,3]"]; -progress_report_recv(suite) -> - []; -progress_report_recv(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - ReportPid = - spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]), - do_recv(Pid, Config), - receive - {ReportPid, ok} -> - ok - end, - ok. - -progress(#progress{} = Progress , _File, {file_size, Total}) -> - progress_report_receiver ! start, - Progress#progress{total = Total}; -progress(#progress{total = Total, current = Current} - = Progress, _File, {transfer_size, 0}) -> - progress_report_receiver ! finish, - case Total of - unknown -> - ok; - Current -> - ok; - _ -> - test_server:fail({error, {progress, {total, Total}, - {current, Current}}}) - end, - Progress; -progress(#progress{current = Current} = Progress, _File, - {transfer_size, Size}) -> - progress_report_receiver ! update, - Progress#progress{current = Current + Size}. - -progress_report_receiver_init(Pid, N) -> - register(progress_report_receiver, self()), - receive - start -> - ok - end, - progress_report_receiver_loop(Pid, N-1). - -progress_report_receiver_loop(Pid, N) -> - receive - update -> - progress_report_receiver_loop(Pid, N); - finish when N =:= 0 -> - Pid ! {self(), ok}; - finish -> - Pid ! {self(), ok}, - receive - start -> - ok - end, - progress_report_receiver_loop(Pid, N-1) - end. - - -%%------------------------------------------------------------------------- -%% Ticket test cases -%%------------------------------------------------------------------------- - -ticket_6035(doc) -> ["Test that owning process that exits with reason " - "'shutdown' does not cause an error message."]; -ticket_6035(suite) -> []; -ticket_6035(Config) -> - p("ticket_6035 -> entry with" - "~n Config: ~p", [Config]), - PrivDir = ?config(priv_dir, Config), - LogFile = filename:join([PrivDir,"ticket_6035.log"]), - try - begin - p("ticket_6035 -> select ftpd host"), - Host = dirty_select_ftpd_host(Config), - p("ticket_6035 -> ftpd host selected (~p) => now spawn ftp owner", [Host]), - Pid = spawn(?MODULE, open_wait_6035, [Host, self()]), - p("ticket_6035 -> waiter spawned: ~p => now open error logfile (~p)", - [Pid, LogFile]), - error_logger:logfile({open, LogFile}), - p("ticket_6035 -> error logfile open => now kill waiter process"), - true = kill_ftp_proc_6035(Pid, LogFile), - p("ticket_6035 -> waiter process killed => now close error logfile"), - error_logger:logfile(close), - p("ticket_6035 -> done", []), - ok - end - catch - throw:{error, not_found} -> - {skip, "No available FTP servers"} - end. - -kill_ftp_proc_6035(Pid, LogFile) -> - p("kill_ftp_proc_6035 -> entry"), - receive - open -> - p("kill_ftp_proc_6035 -> received open => now issue shutdown"), - exit(Pid, shutdown), - kill_ftp_proc_6035(Pid, LogFile); - {open_failed, Reason} -> - p("kill_ftp_proc_6035 -> received open_failed" - "~n Reason: ~p", [Reason]), - exit({skip, {failed_openening_server_connection, Reason}}) - after - 5000 -> - p("kill_ftp_proc_6035 -> timeout"), - is_error_report_6035(LogFile) - end. - -open_wait_6035({Tag, FtpServer}, From) -> - p("open_wait_6035 -> try connect to [~p] ~s for ~p", [Tag, FtpServer, From]), - case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of - {ok, Pid} -> - p("open_wait_6035 -> connected (~p), now login", [Pid]), - LoginResult = ftp:user(Pid,"anonymous","kldjf"), - p("open_wait_6035 -> login result: ~p", [LoginResult]), - From ! open, - receive - dummy -> - p("open_wait_6035 -> received dummy"), - ok - after - 10000 -> - p("open_wait_6035 -> timeout"), - ok - end, - p("open_wait_6035 -> done(ok)"), - ok; - {error, Reason} -> - p("open_wait_6035 -> open failed" - "~n Reason: ~p", [Reason]), - From ! {open_failed, {Reason, FtpServer}}, - p("open_wait_6035 -> done(error)"), - ok - end. - -is_error_report_6035(LogFile) -> - p("is_error_report_6035 -> entry"), - Res = - case file:read_file(LogFile) of - {ok, Bin} -> - Txt = binary_to_list(Bin), - p("is_error_report_6035 -> logfile read: ~n~p", [Txt]), - read_log_6035(Txt); - _ -> - false - end, - p("is_error_report_6035 -> logfile read result: " - "~n ~p", [Res]), - %% file:delete(LogFile), - Res. - -read_log_6035("=ERROR REPORT===="++_Rest) -> - p("read_log_6035 -> ERROR REPORT detected"), - true; -read_log_6035([H|T]) -> - p("read_log_6035 -> OTHER: " - "~p", [H]), - read_log_6035(T); -read_log_6035([]) -> - p("read_log_6035 -> done"), - false. - - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- -do_user(Pid) -> - {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS), - {error, euser} = ftp:user(Pid, ?FTP_USER++"\r\nPASS "++?FTP_PASS, ?FTP_PASS), - {error, euser} = ftp:user(Pid, ?FTP_USER, ?FTP_PASS++"\r\nCWD ."), - ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), - ok. - -do_pwd(Pid) -> - {ok, "/"} = ftp:pwd(Pid), - {ok, Path} = ftp:lpwd(Pid), - {ok, Path} = file:get_cwd(), - ok. - -do_cd(Pid) -> - ok = ftp:cd(Pid, "/pub"), - {error, epath} = ftp:cd(Pid, ?BAD_DIR), - {error, efnamena} = ftp:cd(Pid, "/pub\r\nCWD ."), - ok. - -do_lcd(Pid, Dir) -> - ok = ftp:lcd(Pid, Dir), - {error, epath} = ftp:lcd(Pid, ?BAD_DIR), - ok. - - -do_ls(Pid) -> - {ok, _} = ftp:ls(Pid), - {ok, _} = ftp:ls(Pid, "incoming"), - %% neither nlist nor ls operates on a directory - %% they operate on a pathname, which *can* be a - %% directory, but can also be a filename or a group - %% of files (including wildcards). - {ok, _} = ftp:ls(Pid, "incom*"), - %% but \r\n can't be in the wildcard - {error, efnamena} = ftp:ls(Pid, "incoming\r\nCWD ."), - ok. - -do_nlist(Pid, WildcardSupport) -> - {ok, _} = ftp:nlist(Pid), - {ok, _} = ftp:nlist(Pid, "incoming"), - {error, efnamena} = ftp:ls(Pid, "incoming\r\nCWD ."), - %% neither nlist nor ls operates on a directory - %% they operate on a pathname, which *can* be a - %% directory, but can also be a filename or a group - %% of files (including wildcards). - case WildcardSupport of - true -> - {ok, _} = ftp:nlist(Pid, "incom*"), - ok; - _ -> - ok - end. - -do_rename(Pid, Config) -> - PrivDir = ?config(priv_dir, Config), - LFile = ?config(file, Config), - NewLFile = ?config(new_file, Config), - AbsLFile = filename:absname(LFile, PrivDir), - Contents = "ftp_SUITE test ...", - ok = file:write_file(AbsLFile, list_to_binary(Contents)), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:lcd(Pid, PrivDir), - ftp:delete(Pid, LFile), % reset - ftp:delete(Pid, NewLFile), % reset - ok = ftp:send(Pid, LFile), - {error, epath} = ftp:rename(Pid, NewLFile, LFile), - {error, efnamena} = ftp:rename(Pid, NewLFile++"\r\nRNTO "++LFile++"\r\nRNFR "++NewLFile, LFile), - {error, efnamena} = ftp:rename(Pid, NewLFile, LFile++"\r\nCWD ."), - ok = ftp:rename(Pid, LFile, NewLFile), - ftp:delete(Pid, LFile), % cleanup - ftp:delete(Pid, NewLFile), % cleanup - ok. - -do_delete(Pid, Config) -> - PrivDir = ?config(priv_dir, Config), - LFile = ?config(file, Config), - AbsLFile = filename:absname(LFile, PrivDir), - Contents = "ftp_SUITE test ...", - ok = file:write_file(AbsLFile, list_to_binary(Contents)), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:lcd(Pid, PrivDir), - ftp:delete(Pid,LFile), % reset - {error, efnamena} = ftp:delete(Pid,LFile++"\r\nCWD ."), - ok = ftp:send(Pid, LFile), - ok = ftp:delete(Pid,LFile), - ok. - -do_mkdir(Pid) -> - NewDir = "earl_" ++ - integer_to_list(erlang:unique_integer([positive])), - - ok = ftp:cd(Pid, "incoming"), - {ok, CurrDir} = ftp:pwd(Pid), - {error, efnamena} = ftp:mkdir(Pid, NewDir++"\r\nCWD ."), - {error, efnamena} = ftp:rmdir(Pid, NewDir++"\r\nCWD ."), - ok = ftp:mkdir(Pid, NewDir), - ok = ftp:cd(Pid, NewDir), - ok = ftp:cd(Pid, CurrDir), - ok = ftp:rmdir(Pid, NewDir), - ok. - -do_send(Pid, Config) -> - PrivDir = ?config(priv_dir, Config), - LFile = ?config(file, Config), - RFile = LFile ++ ".remote", - AbsLFile = filename:absname(LFile, PrivDir), - Contents = "ftp_SUITE test ...", - ok = file:write_file(AbsLFile, list_to_binary(Contents)), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:lcd(Pid, PrivDir), - {error, efnamena} = ftp:send(Pid, LFile, RFile++"1\r\nCWD ."), - ok = ftp:send(Pid, LFile, RFile), - {ok, RFilesString} = ftp:nlist(Pid), - RFiles = split(RFilesString), - true = lists:member(RFile, RFiles), - ok = ftp:delete(Pid, RFile), - case ftp:nlist(Pid) of - {error, epath} -> - ok; % No files - {ok, RFilesString1} -> - RFiles1 = split(RFilesString1), - false = lists:member(RFile, RFiles1) - end, - ok = file:delete(AbsLFile). - -do_append(Pid, Config) -> - PrivDir = ?config(priv_dir, Config), - LFile = ?config(file, Config), - RFile = ?config(new_file, Config), - AbsLFile = filename:absname(LFile, PrivDir), - Contents = "ftp_SUITE test:appending\r\n", - - ok = file:write_file(AbsLFile, list_to_binary(Contents)), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:lcd(Pid, PrivDir), - - %% remove files from earlier failed test case - ftp:delete(Pid, RFile), - ftp:delete(Pid, LFile), - - {error, efnamena} = ftp:append(Pid, LFile, RFile++"1\r\nCWD ."), - ok = ftp:append(Pid, LFile, RFile), - ok = ftp:append(Pid, LFile, RFile), - ok = ftp:append(Pid, LFile), - - %% Control the contents of the file - {ok, Bin1} = ftp:recv_bin(Pid, RFile), - ok = ftp:delete(Pid, RFile), - ok = file:delete(AbsLFile), - ok = check_content(binary_to_list(Bin1), Contents, double), - - {ok, Bin2} = ftp:recv_bin(Pid, LFile), - ok = ftp:delete(Pid, LFile), - ok = check_content(binary_to_list(Bin2), Contents, singel), - ok. - -do_send_bin(Pid, Config) -> - File = ?config(file, Config), - Contents = "ftp_SUITE test ...", - Bin = list_to_binary(Contents), - ok = ftp:cd(Pid, "incoming"), - {error, enotbinary} = ftp:send_bin(Pid, Contents, File), - {error, efnamena} = ftp:send_bin(Pid, Bin, File++"1\r\nCWD ."), - ok = ftp:send_bin(Pid, Bin, File), - {ok, RFilesString} = ftp:nlist(Pid), - RFiles = split(RFilesString), - true = lists:member(File, RFiles), - ok = ftp:delete(Pid, File), - ok. - -do_append_bin(Pid, Config) -> - File = ?config(file, Config), - Contents = "ftp_SUITE test ...", - Bin = list_to_binary(Contents), - ok = ftp:cd(Pid, "incoming"), - {error, enotbinary} = ftp:append_bin(Pid, Contents, File), - {error, efnamena} = ftp:append_bin(Pid, Bin, File++"1\r\nCWD ."), - ok = ftp:append_bin(Pid, Bin, File), - ok = ftp:append_bin(Pid, Bin, File), - %% Control the contents of the file - {ok, Bin2} = ftp:recv_bin(Pid, File), - ok = ftp:delete(Pid,File), - ok = check_content(binary_to_list(Bin2),binary_to_list(Bin), double). - -do_send_chunk(Pid, Config) -> - File = ?config(file, Config), - Contents = "ftp_SUITE test ...", - Bin = list_to_binary(Contents), - ok = ftp:cd(Pid, "incoming"), - {error, efnamena} = ftp:send_chunk_start(Pid, File++"1\r\nCWD ."), - ok = ftp:send_chunk_start(Pid, File), - {error, echunk} = ftp:cd(Pid, "incoming"), - {error, enotbinary} = ftp:send_chunk(Pid, Contents), - ok = ftp:send_chunk(Pid, Bin), - ok = ftp:send_chunk(Pid, Bin), - ok = ftp:send_chunk_end(Pid), - {ok, RFilesString} = ftp:nlist(Pid), - RFiles = split(RFilesString), - true = lists:member(File, RFiles), - ok = ftp:delete(Pid, File), - ok. - -do_append_chunk(Pid, Config) -> - File = ?config(file, Config), - Contents = ["ER","LE","RL"], - ok = ftp:cd(Pid, "incoming"), - {error, efnamena} = ftp:append_chunk_start(Pid, File++"1\r\nCWD ."), - ok = ftp:append_chunk_start(Pid, File), - {error, enotbinary} = ftp:append_chunk(Pid, lists:nth(1,Contents)), - ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(1,Contents))), - ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(2,Contents))), - ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(3,Contents))), - ok = ftp:append_chunk_end(Pid), - %%Control the contents of the file - {ok, Bin2} = ftp:recv_bin(Pid, File), - ok = check_content(binary_to_list(Bin2),"ERL", double), - ok = ftp:delete(Pid, File), - ok. - -do_recv(Pid, Config) -> - PrivDir = ?config(priv_dir, Config), - File = ?config(file, Config), - Newfile = ?config(new_file, Config), - AbsFile = filename:absname(File, PrivDir), - Contents = "ftp_SUITE:recv test ...", - ok = file:write_file(AbsFile, list_to_binary(Contents)), - ok = ftp:cd(Pid, "incoming"), - ftp:delete(Pid, File), % reset - ftp:lcd(Pid, PrivDir), - ok = ftp:send(Pid, File), - ok = file:delete(AbsFile), % cleanup - test_server:sleep(100), - ok = ftp:lcd(Pid, PrivDir), - {error, efnamena} = ftp:recv(Pid, File++"\r\nCWD ."), - ok = ftp:recv(Pid, File), - {ok, Files} = file:list_dir(PrivDir), - true = lists:member(File, Files), - ok = file:delete(AbsFile), % cleanup - ok = ftp:recv(Pid, File, Newfile), - ok = ftp:delete(Pid, File), % cleanup - ok. - -do_recv_bin(Pid, Config) -> - File = ?config(file, Config), - Contents1 = "ftp_SUITE test ...", - Bin1 = list_to_binary(Contents1), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:send_bin(Pid, Bin1, File), - test_server:sleep(100), - {error, efnamena} = ftp:recv_bin(Pid, File++"\r\nCWD ."), - {ok, Bin2} = ftp:recv_bin(Pid, File), - ok = ftp:delete(Pid, File), % cleanup - Contents2 = binary_to_list(Bin2), - Contents1 = Contents2, - ok. - -do_recv_chunk(Pid, Config) -> - File = ?config(file, Config), - Data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" - "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" - "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" - "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG" - "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH" - "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", - - Contents1 = lists:flatten(lists:duplicate(10, Data)), - Bin1 = list_to_binary(Contents1), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:type(Pid, binary), - ok = ftp:send_bin(Pid, Bin1, File), - test_server:sleep(100), - {error, "ftp:recv_chunk_start/2 not called"} = recv_chunk(Pid, <<>>), - {error, efnamena} = ftp:recv_chunk_start(Pid, File++"\r\nCWD ."), - ok = ftp:recv_chunk_start(Pid, File), - {ok, Contents2} = recv_chunk(Pid, <<>>), - ok = ftp:delete(Pid, File), % cleanup - ok = find_diff(Contents2, Contents1, 1), - ok. - -do_type(Pid) -> - ok = ftp:type(Pid, ascii), - ok = ftp:type(Pid, binary), - ok = ftp:type(Pid, ascii), - {error, etype} = ftp:type(Pid, foobar), - ok. - -do_quote(Pid) -> - ["257 \"/\""++_Rest] = ftp:quote(Pid, "pwd"), %% 257 - [_| _] = ftp:quote(Pid, "help"), - %% This negativ test causes some ftp servers to hang. This test - %% is not important for the client, so we skip it for now. - %%["425 Can't build data connection: Connection refused."] - %% = ftp:quote(Pid, "list"), - ok. - - watch_dog(Config) -> - Dog = test_server:timetrap(inets_test_lib:minutes(1)), - NewConfig = lists:keydelete(watchdog, 1, Config), - [{watchdog, Dog} | NewConfig]. - - close_connection(Config) -> - case ?config(ftp, Config) of - Pid when is_pid(Pid) -> - ok = ftp:close(Pid), - lists:delete({ftp, Pid}, Config); - _ -> - Config - end. - -ftp_host(Config) -> - case ?config(ftp_remote_host, Config) of - undefined -> - exit({skip, "No host specified"}); - Host -> - Host - end. - -check_content(RContent, LContent, Amount) -> - LContent2 = case Amount of - double -> - LContent ++ LContent; - singel -> - LContent - end, - case string:equal(RContent, LContent2) of - true -> - ok; - false -> - %% Find where the diff is - Where = find_diff(RContent, LContent2, 1), - Where - end. - -find_diff(A, A, _) -> - ok; -find_diff([H|T1], [H|T2], Pos) -> - find_diff(T1, T2, Pos+1); -find_diff(RC, LC, Pos) -> - {error, {diff, Pos, RC, LC}}. - -recv_chunk(Pid, Acc) -> - case ftp:recv_chunk(Pid) of - ok -> - {ok, binary_to_list(Acc)}; - {ok, Bin} -> - recv_chunk(Pid, <<Acc/binary, Bin/binary>>); - Error -> - Error - end. - -split(Cs) -> - split(Cs, [], []). - -split([$\r, $\n| Cs], I, Is) -> - split(Cs, [], [lists:reverse(I)| Is]); -split([C| Cs], I, Is) -> - split(Cs, [C| I], Is); -split([], I, Is) -> - lists:reverse([lists:reverse(I)| Is]). - -do_ftp_open(Host, Opts) -> - p("do_ftp_open -> entry with" - "~n Host: ~p" - "~n Opts: ~p", [Host, Opts]), - case ftp:open(Host, Opts) of - {ok, _} = OK -> - OK; - {error, Reason} -> - Str = - lists:flatten( - io_lib:format("Unable to reach test FTP server ~p (~p)", - [Host, Reason])), - throw({skip, Str}) - end. - - -passwd() -> - Host = - case inet:gethostname() of - {ok, H} -> - H; - _ -> - "localhost" - end, - "ftp_SUITE@" ++ Host. - -ftpd_hosts(Config) -> - DataDir = ?config(data_dir, Config), - FileName = filename:join([DataDir, "../ftp_SUITE_data/", ftpd_hosts]), - p("FileName: ~p", [FileName]), - case file:consult(FileName) of - {ok, [Hosts]} when is_list(Hosts) -> - Hosts; - _ -> - [] - end. - -wrapper(Prefix,doc,Func) -> - Prefix++Func(doc); -wrapper(_,X,Func) -> - Func(X). - -data_dir(Config) -> - case ?config(data_dir, Config) of - List when (length(List) > 0) -> - PathList = filename:split(List), - {NewPathList,_} = lists:split((length(PathList)-1), PathList), - DataDir = filename:join(NewPathList ++ [ftp_SUITE_data]), - NewConfig = - lists:keyreplace(data_dir,1,Config, {data_dir,DataDir}), - NewConfig; - _ -> Config - end. - - - -p(F) -> - p(F, []). - -p(F, A) -> - case get(ftp_testcase) of - undefined -> - io:format("~w [~w] " ++ F ++ "~n", [?MODULE, self() | A]); - TC when is_atom(TC) -> - io:format("~w [~w] ~w:" ++ F ++ "~n", [?MODULE, self(), TC | A]) - end. diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl index e977bd1b9b..a2b463e98c 100644 --- a/lib/inets/test/http_format_SUITE.erl +++ b/lib/inets/test/http_format_SUITE.erl @@ -55,13 +55,9 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_, Config) -> - Dog = test_server:timetrap(?t:minutes(1)), - NewConfig = lists:keydelete(watchdog, 1, Config), - [{watchdog, Dog} | NewConfig]. + Config. -end_per_testcase(_, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_, _) -> ok. %%------------------------------------------------------------------------- diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index e6c4e48feb..42772923e4 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -42,7 +42,8 @@ %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- suite() -> - [{ct_hooks,[ts_install_cth]}]. + [{ct_hooks,[ts_install_cth]} + ]. all() -> [ @@ -137,8 +138,9 @@ misc() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - PrivDir = ?config(priv_dir, Config), - DataDir = ?config(data_dir, Config), + ct:timetrap({seconds, 30}), + PrivDir = proplists:get_value(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), inets_test_lib:start_apps([inets]), ServerRoot = filename:join(PrivDir, "server_root"), DocRoot = filename:join(ServerRoot, "htdocs"), @@ -147,7 +149,7 @@ init_per_suite(Config) -> end_per_suite(Config) -> inets_test_lib:stop_apps([inets]), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), inets_test_lib:del_dirs(PrivDir), ok. @@ -159,6 +161,7 @@ init_per_group(misc = Group, Config) -> Config; init_per_group(Group, Config0) when Group =:= sim_https; Group =:= https-> + ct:timetrap({seconds, 30}), start_apps(Group), StartSsl = try ssl:start() catch @@ -199,7 +202,15 @@ init_per_testcase(persistent_connection, Config) -> {max_keep_alive_length, 3}], persistent_connection), Config; - +init_per_testcase(wait_for_whole_response, Config) -> + ct:timetrap({seconds, 60*3}), + Config; +init_per_testcase(Case, Config) when Case == post; + Case == delete; + Case == post_delete; + Case == post_stream -> + ct:timetrap({seconds, 30}), + Config; init_per_testcase(_Case, Config) -> Config. @@ -356,7 +367,7 @@ pipeline(Config) when is_list(Config) -> {ok, _} = httpc:request(get, Request, [], [], pipeline), %% Make sure pipeline session is registerd - test_server:sleep(4000), + ct:sleep(4000), keep_alive_requests(Request, pipeline). %%-------------------------------------------------------------------- @@ -366,7 +377,7 @@ persistent_connection(Config) when is_list(Config) -> {ok, _} = httpc:request(get, Request, [], [], persistent), %% Make sure pipeline session is registerd - test_server:sleep(4000), + ct:sleep(4000), keep_alive_requests(Request, persistent). %%------------------------------------------------------------------------- @@ -394,7 +405,7 @@ async(Config) when is_list(Config) -> save_to_file() -> [{doc, "Test to save the http body to a file"}]. save_to_file(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FilePath = filename:join(PrivDir, "dummy.html"), URL = url(group_name(Config), "/dummy.html", Config), Request = {URL, []}, @@ -408,7 +419,7 @@ save_to_file(Config) when is_list(Config) -> save_to_file_async() -> [{doc,"Test to save the http body to a file"}]. save_to_file_async(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FilePath = filename:join(PrivDir, "dummy.html"), URL = url(group_name(Config), "/dummy.html", Config), Request = {URL, []}, @@ -868,7 +879,7 @@ headers() -> headers(Config) when is_list(Config) -> URL = url(group_name(Config), "/dummy.html", Config), - DocRoot = ?config(doc_root, Config), + DocRoot = proplists:get_value(doc_root, Config), {ok, FileInfo} = file:read_file_info(filename:join([DocRoot,"dummy.html"])), @@ -1212,11 +1223,11 @@ not_streamed_test(Request, To) -> end. url(http, End, Config) -> - Port = ?config(port, Config), + Port = proplists:get_value(port, Config), {ok,Host} = inet:gethostname(), ?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End; url(https, End, Config) -> - Port = ?config(port, Config), + Port = proplists:get_value(port, Config), {ok,Host} = inet:gethostname(), ?TLS_URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End; url(sim_http, End, Config) -> @@ -1224,10 +1235,10 @@ url(sim_http, End, Config) -> url(sim_https, End, Config) -> url(https, End, Config). url(http, UserInfo, End, Config) -> - Port = ?config(port, Config), + Port = proplists:get_value(port, Config), ?URL_START ++ UserInfo ++ integer_to_list(Port) ++ End; url(https, UserInfo, End, Config) -> - Port = ?config(port, Config), + Port = proplists:get_value(port, Config), ?TLS_URL_START ++ UserInfo ++ integer_to_list(Port) ++ End; url(sim_http, UserInfo, End, Config) -> url(http, UserInfo, End, Config); @@ -1235,7 +1246,7 @@ url(sim_https, UserInfo, End, Config) -> url(https, UserInfo, End, Config). group_name(Config) -> - GroupProp = ?config(tc_group_properties, Config), + GroupProp = proplists:get_value(tc_group_properties, Config), proplists:get_value(name, GroupProp). server_start(sim_http, _) -> @@ -1257,11 +1268,11 @@ server_start(_, HttpdConfig) -> proplists:get_value(port, Info). server_config(http, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), [{port, 0}, {server_name,"httpc_test"}, {server_root, ServerRoot}, - {document_root, ?config(doc_root, Config)}, + {document_root, proplists:get_value(doc_root, Config)}, {bind_address, any}, {ipfamily, inet_version()}, {mime_type, "text/plain"}, @@ -1283,7 +1294,7 @@ start_apps(_) -> ok. ssl_config(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), [{certfile, filename:join(DataDir, "ssl_server_cert.pem")}, {verify, verify_none} ]. @@ -1931,9 +1942,9 @@ handle_uri(_,"/once.html",_,_,Socket,_) -> "Content-Length:32\r\n\r\n", send(Socket, Head), send(Socket, "<HTML><BODY>fo"), - test_server:sleep(1000), + ct:sleep(1000), send(Socket, "ob"), - test_server:sleep(1000), + ct:sleep(1000), send(Socket, "ar</BODY></HTML>"); handle_uri(_,"/invalid_http.html",_,_,_,_) -> diff --git a/lib/inets/test/httpc_cookie_SUITE.erl b/lib/inets/test/httpc_cookie_SUITE.erl index 0eca3edae2..8140967bca 100644 --- a/lib/inets/test/httpc_cookie_SUITE.erl +++ b/lib/inets/test/httpc_cookie_SUITE.erl @@ -23,14 +23,8 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -%% Test server specific exports --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). - -%% Test cases must be exported. --export([session_cookies_only/1, netscape_cookies/1, - cookie_cancel/1, cookie_expires/1, persistent_cookie/1, - domain_cookie/1, secure_cookie/1, update_cookie/1, - update_cookie_session/1, cookie_attributes/1]). +%% Note: This directive should only be used in test suites. +-compile(export_all). -define(URL, "http://myhost.cookie.test.org"). -define(URL_DOMAIN, "http://myhost2.cookie.test.org"). @@ -38,86 +32,10 @@ %% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(session_cookies_only = Case, Config0) -> - tsp("init_per_testcase(~p) -> entry with" - "~n Config0: ~p", [Case, Config0]), - Config = init_workdir(Case, Config0), - application:start(inets), - httpc:set_options([{cookies, verify}]), - watch_dog(Config); - -init_per_testcase(Case, Config0) -> - tsp("init_per_testcase(~p) -> entry with" - "~n Config0: ~p", [Case, Config0]), - Config = init_workdir(Case, Config0), - CaseDir = ?config(case_top_dir, Config), - application:load(inets), - application:set_env(inets, services, [{httpc, {default, CaseDir}}]), - application:start(inets), - httpc:set_options([{cookies, verify}]), - watch_dog(Config). - -watch_dog(Config) -> - Dog = test_server:timetrap(inets_test_lib:minutes(10)), - NewConfig = lists:keydelete(watchdog, 1, Config), - [{watchdog, Dog} | NewConfig]. - -init_workdir(Case, Config) -> - PrivDir = ?config(priv_dir, Config), - SuiteTopDir = filename:join(PrivDir, ?MODULE), - case file:make_dir(SuiteTopDir) of - ok -> - ok; - {error, eexist} -> - ok; - Error -> - tsf({failed_creating_subsuite_top_dir, Error}) - end, - - CaseTopDir = filename:join(SuiteTopDir, Case), - ?line ok = file:make_dir(CaseTopDir), - [{suite_top_dir, SuiteTopDir}, - {case_top_dir, CaseTopDir} | Config]. - - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - tsp("end_per_testcase(~p) -> entry with" - "~n Config: ~p", [Case, Config]), - application:stop(inets), - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{seconds,15}} + ]. all() -> [ @@ -148,17 +66,55 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(session_cookies_only = Case, Config0) -> + Config = init_workdir(Case, Config0), + application:start(inets), + httpc:set_options([{cookies, verify}]), + Config; +init_per_testcase(cookie_expires = Case, Config0) -> + Config = init_workdir(Case, Config0), + CaseDir = proplists:get_value(case_top_dir, Config), + application:start(inets), + application:set_env(inets, services, [{httpc, {default, CaseDir}}]), + application:start(inets), + httpc:set_options([{cookies, verify}]), + Config; +init_per_testcase(Case, Config0) -> + Config = init_workdir(Case, Config0), + CaseDir = proplists:get_value(case_top_dir, Config), + application:load(inets), + application:set_env(inets, services, [{httpc, {default, CaseDir}}]), + application:start(inets), + httpc:set_options([{cookies, verify}]), + Config. + +init_workdir(Case, Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + SuiteTopDir = filename:join(PrivDir, ?MODULE), + case file:make_dir(SuiteTopDir) of + ok -> + ok; + {error, eexist} -> + ok; + Error -> + ct:fail({failed_creating_subsuite_top_dir, Error}) + end, + + CaseTopDir = filename:join(SuiteTopDir, Case), + ok = file:make_dir(CaseTopDir), + [{suite_top_dir, SuiteTopDir}, + {case_top_dir, CaseTopDir} | Config]. + +end_per_testcase(_, _) -> + application:stop(inets). + %% Test cases starts here. %%-------------------------------------------------------------------- -session_cookies_only(doc) -> - ["Test that all cookies are handled as session cookies if there" - "does not exist a directory to save presitent cookies in."]; -session_cookies_only(suite) -> - []; +session_cookies_only() -> + [{doc, "Test that all cookies are handled as session cookies if there" + "does not exist a directory to save presitent cookies in."}]. session_cookies_only(Config) when is_list(Config) -> - tsp("session_cookies_only -> Cookies 1: ~p", [httpc:which_cookies()]), - SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" ";max-age=60000"}], httpc:store_cookies(SetCookieHeaders, ?URL), @@ -166,36 +122,22 @@ session_cookies_only(Config) when is_list(Config) -> httpc:cookie_header(?URL), application:stop(inets), application:start(inets), - {"cookie", ""} = httpc:cookie_header(?URL), - - tsp("session_cookies_only -> Cookies 2: ~p", [httpc:which_cookies()]), - ok. + {"cookie", ""} = httpc:cookie_header(?URL). -netscape_cookies(doc) -> - ["Test that the old (original) format of cookies are accepted."]; -netscape_cookies(suite) -> - []; +netscape_cookies() -> + [{doc, "Test that the old (original) format of cookies are accepted."}]. netscape_cookies(Config) when is_list(Config) -> - tsp("netscape_cookies -> Cookies 1: ~p", [httpc:which_cookies()]), - Expires = future_netscape_date(), SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; " "expires=" ++ Expires}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie", "$Version=0; test_cookie=true; $Path=/"} = - httpc:cookie_header(?URL), + httpc:cookie_header(?URL). - tsp("netscape_cookies -> Cookies 2: ~p", [httpc:which_cookies()]), - ok. - -cookie_cancel(doc) -> - ["A cookie can be canceld by sending the same cookie with max-age=0 " - "this test cheks that cookie is canceled."]; -cookie_cancel(suite) -> - []; +cookie_cancel() -> + [{doc, "A cookie can be canceld by sending the same cookie with max-age=0 " + "this test cheks that cookie is canceled."}]. cookie_cancel(Config) when is_list(Config) -> - tsp("cookie_cancel -> Cookies 1: ~p", [httpc:which_cookies()]), - SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" "max-age=60000"}], httpc:store_cookies(SetCookieHeaders, ?URL), @@ -204,155 +146,60 @@ cookie_cancel(Config) when is_list(Config) -> NewSetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;max-age=0"}], httpc:store_cookies(NewSetCookieHeaders, ?URL), - {"cookie", ""} = httpc:cookie_header(?URL), + {"cookie", ""} = httpc:cookie_header(?URL). - tsp("cookie_cancel -> Cookies 2: ~p", [httpc:which_cookies()]), - ok. - -cookie_expires(doc) -> - ["Test that a cookie is not used when it has expired"]; -cookie_expires(suite) -> - []; +cookie_expires() -> + [{doc, "Test that a cookie is not used when it has expired"}]. cookie_expires(Config) when is_list(Config) -> - tsp("cookie_expires -> Cookies 1: ~p", [httpc:which_cookies()]), - SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" "max-age=5"}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie", "$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL), - test_server:sleep(10000), - {"cookie", ""} = httpc:cookie_header(?URL), + timer:sleep(10000), + {"cookie", ""} = httpc:cookie_header(?URL). - tsp("cookie_expires -> Cookies 2: ~p", [httpc:which_cookies()]), - ok. - -persistent_cookie(doc) -> - ["Test domian cookie attribute"]; -persistent_cookie(suite) -> - []; +persistent_cookie() -> + [{doc, "Test domian cookie attribute"}]. persistent_cookie(Config) when is_list(Config)-> - tsp("persistent_cookie -> Cookies 1: ~p", [httpc:which_cookies()]), - SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" "max-age=60000"}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie", "$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL), - CaseDir = ?config(case_top_dir, Config), + CaseDir = proplists:get_value(case_top_dir, Config), application:stop(inets), application:load(inets), application:set_env(inets, services, [{httpc, {default, CaseDir}}]), application:start(inets), httpc:set_options([{cookies, enabled}]), - {"cookie","$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL), + {"cookie","$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL). - tsp("persistent_cookie -> Cookies 2: ~p", [httpc:which_cookies()]), - ok. - - -domain_cookie(doc) -> - ["Test the domian cookie attribute"]; -domain_cookie(suite) -> - []; +domain_cookie() -> + [{doc, "Test the domian cookie attribute"}]. domain_cookie(Config) when is_list(Config) -> - tsp("domain_cookie -> Cookies 1: ~p", [httpc:which_cookies()]), - SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" "domain=.cookie.test.org"}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie","$Version=0; test_cookie=true; $Path=/; " "$Domain=.cookie.test.org"} = - httpc:cookie_header(?URL_DOMAIN), + httpc:cookie_header(?URL_DOMAIN). - tsp("domain_cookie -> Cookies 2: ~p", [httpc:which_cookies()]), - ok. - - -secure_cookie(doc) -> - ["Test the secure cookie attribute"]; -secure_cookie(suite) -> - []; +secure_cookie() -> + [{doc, "Test the secure cookie attribute"}]. secure_cookie(Config) when is_list(Config) -> - tsp("secure_cookie -> entry with" - "~n Config: ~p", [Config]), - - %% httpc:reset_cookies(), - - tsp("secure_cookie -> Cookies 1: ~p", [httpc:which_cookies()]), - SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; secure"}], - tsp("secure_cookie -> store cookies (1)"), ok = httpc:store_cookies(SetCookieHeaders, ?URL), - - tsp("secure_cookie -> Cookies 2: ~p", [httpc:which_cookies()]), - - tsp("secure_cookie -> check cookie (secure)"), check_cookie("$Version=0; test_cookie=true; $Path=/", ?URL_SECURE), - - tsp("secure_cookie -> check cookie (plain)"), check_cookie("", ?URL), - - tsp("secure_cookie -> store cookies (2)"), SetCookieHeaders1 = [{"set-cookie", "test1_cookie=true; path=/; secure"}], ok = httpc:store_cookies(SetCookieHeaders1, ?URL), - - tsp("secure_cookie -> Cookies 3: ~p", [httpc:which_cookies()]), - - tsp("secure_cookie -> cookie header (3)"), check_cookie("$Version=0; test_cookie=true; $Path=/; " "test1_cookie=true; $Path=/", - ?URL_SECURE), -%% {"cookie","$Version=0; test_cookie=true; $Path=/; " -%% "test1_cookie=true; $Path=/"} = httpc:cookie_header(?URL_SECURE), - - tsp("secure_cookie -> Cookies 4: ~p", [httpc:which_cookies()]), - - tsp("secure_cookie -> done"), - ok. + ?URL_SECURE). -expect_cookie_header(No, ExpectedCookie) -> - case httpc:cookie_header(?URL) of - {"cookie", ExpectedCookie} -> - ok; - {"cookie", BadCookie} -> - io:format("Bad Cookie ~w: " - "~n Expected: ~s" - "~n Received: ~s" - "~n", [No, ExpectedCookie, BadCookie]), - exit({bad_cookie_header, No, ExpectedCookie, BadCookie}) - end. - -print_cookies(Pre) -> - io:format("~s: ", [Pre]), - print_cookies2(httpc:which_cookies()). - -print_cookies2([]) -> - ok; -print_cookies2([{cookies, Cookies}|Rest]) -> - print_cookies3("Cookies", Cookies), - print_cookies2(Rest); -print_cookies2([{session_cookies, Cookies}|Rest]) -> - print_cookies3("Session Cookies", Cookies), - print_cookies2(Rest); -print_cookies2([_|Rest]) -> - print_cookies2(Rest). - -print_cookies3(Header, []) -> - io:format(" ~s: []", [Header]); -print_cookies3(Header, Cookies) -> - io:format(" ~s: ", [Header]), - Prefix = " ", - PrintCookie = - fun(Cookie) -> - io:format("~s", [httpc_cookie:image_of(Prefix, Cookie)]) - end, - lists:foreach(PrintCookie, Cookies). - -update_cookie(doc)-> - ["Test that a (plain) cookie can be updated."]; -update_cookie(suite) -> - []; +update_cookie()-> + [{doc, "Test that a (plain) cookie can be updated."}]. update_cookie(Config) when is_list(Config) -> print_cookies("Cookies before store"), @@ -377,10 +224,8 @@ update_cookie(Config) when is_list(Config) -> "test_cookie=false; $Path=/", expect_cookie_header(2, ExpectCookie2). -update_cookie_session(doc)-> - ["Test that a session cookie can be updated."]; -update_cookie_session(suite) -> - []; +update_cookie_session()-> + [{doc, "Test that a session cookie can be updated."}]. update_cookie_session(Config) when is_list(Config)-> print_cookies("Cookies before store"), @@ -400,23 +245,57 @@ update_cookie_session(Config) when is_list(Config)-> expect_cookie_header(2, ExpectedCookie2). -cookie_attributes(doc) -> - ["Test attribute not covered by the other test cases"]; -cookie_attributes(suite) -> - []; +cookie_attributes() -> + [{doc, "Test attribute not covered by the other test cases"}]. cookie_attributes(Config) when is_list(Config) -> SetCookieHeaders = [{"set-cookie", "test_cookie=true;version=1;" "comment=foobar; "%% Comment "foo=bar;" %% Nonsense should be ignored "max-age=60000"}], httpc:store_cookies(SetCookieHeaders, ?URL), - {"cookie","$Version=1; test_cookie=true"} = httpc:cookie_header(?URL), - ok. + {"cookie","$Version=1; test_cookie=true"} = httpc:cookie_header(?URL). %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +print_cookies(Pre) -> + io:format("~s: ", [Pre]), + print_cookies2(httpc:which_cookies()). + +print_cookies2([]) -> + ok; +print_cookies2([{cookies, Cookies}|Rest]) -> + print_cookies3("Cookies", Cookies), + print_cookies2(Rest); +print_cookies2([{session_cookies, Cookies}|Rest]) -> + print_cookies3("Session Cookies", Cookies), + print_cookies2(Rest); +print_cookies2([_|Rest]) -> + print_cookies2(Rest). + +print_cookies3(Header, []) -> + io:format(" ~s: []", [Header]); +print_cookies3(Header, Cookies) -> + io:format(" ~s: ", [Header]), + Prefix = " ", + PrintCookie = + fun(Cookie) -> + io:format("~s", [httpc_cookie:image_of(Prefix, Cookie)]) + end, + lists:foreach(PrintCookie, Cookies). + +expect_cookie_header(No, ExpectedCookie) -> + case httpc:cookie_header(?URL) of + {"cookie", ExpectedCookie} -> + ok; + {"cookie", BadCookie} -> + io:format("Bad Cookie ~w: " + "~n Expected: ~s" + "~n Received: ~s" + "~n", [No, ExpectedCookie, BadCookie]), + exit({bad_cookie_header, No, ExpectedCookie, BadCookie}) + end. check_cookie(Expect, URL) -> case httpc:cookie_header(URL) of @@ -426,12 +305,12 @@ check_cookie(Expect, URL) -> case lists:prefix(Expect, Unexpected) of true -> Extra = Unexpected -- Expect, - tsf({extra_cookie_info, Extra}); + ct:fail({extra_cookie_info, Extra}); false -> - tsf({unknown_cookie, Expect, Unexpected}) + ct:fail({unknown_cookie, Expect, Unexpected}) end; Bad -> - tsf({bad_cookies, Bad}) + ct:fail({bad_cookies, Bad}) end. @@ -509,11 +388,3 @@ month_str(11) ->"Nov"; month_str(12) ->"Dec". -tsp(F) -> - tsp(F, []). -tsp(F, A) -> - test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). - -tsf(Reason) -> - test_server:fail(Reason). - diff --git a/lib/inets/test/httpc_proxy_SUITE.erl b/lib/inets/test/httpc_proxy_SUITE.erl index 2f6f0bc26f..198b245399 100644 --- a/lib/inets/test/httpc_proxy_SUITE.erl +++ b/lib/inets/test/httpc_proxy_SUITE.erl @@ -141,7 +141,7 @@ end_per_testcase(_Case, Config) -> %% internal functions apps(_Case, Config) -> - case ?config(protocol, Config) of + case proplists:get_value(protocol, Config) of https -> [ssl]; _ -> @@ -438,7 +438,7 @@ header_value(Name, [{HeaderName,HeaderValue}|Headers]) -> https_connect_error(doc) -> ["Error from CONNECT tunnel should be returned"]; https_connect_error(Config) when is_list(Config) -> - {HttpServer,HttpPort} = ?config(http, Config), + {HttpServer,HttpPort} = proplists:get_value(http, Config), Method = get, %% using HTTPS scheme with HTTP port to trigger connection error URL = "https://" ++ HttpServer ++ ":" ++ @@ -477,7 +477,7 @@ app_start(App, Config) -> inets -> application:stop(App), ok = application:start(App), - case ?config(proxy, Config) of + case proplists:get_value(proxy, Config) of undefined -> ok; {_,ProxySpec} -> ok = httpc:set_options([{proxy,ProxySpec}]) @@ -495,7 +495,7 @@ app_stop(App) -> application:stop(App). make_cert_files(Alg, Prefix, Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), CaInfo = {CaCert,_} = erl_make_certs:make_cert([{key,Alg}]), {Cert,CertKey} = erl_make_certs:make_cert([{key,Alg},{issuer,CaInfo}]), CaCertFile = filename:join(PrivDir, Prefix++"cacerts.pem"), @@ -513,8 +513,8 @@ der_to_pem(File, Entries) -> url(AbsPath, Config) -> - Protocol = ?config(protocol, Config), - {ServerName,ServerPort} = ?config(Protocol, Config), + Protocol = proplists:get_value(protocol, Config), + {ServerName,ServerPort} = proplists:get_value(Protocol, Config), atom_to_list(Protocol) ++ "://" ++ ServerName ++ ":" ++ integer_to_list(ServerPort) ++ AbsPath. @@ -548,8 +548,8 @@ init_local_proxy_string(String, Config) -> |Config]. rcmd_local_proxy(Args, Config) -> - DataDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Script = filename:join(DataDir, ?LOCAL_PROXY_SCRIPT), rcmd(Script, Args, [{cd,PrivDir}]). diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 93520c1cb4..f5167116ce 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -46,7 +46,9 @@ %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- suite() -> - [{ct_hooks,[ts_install_cth]}]. + [{ct_hooks,[ts_install_cth]}, + {timetrap,{seconds, 120}} + ]. all() -> [ @@ -149,8 +151,8 @@ load() -> ]. init_per_suite(Config) -> - PrivDir = ?config(priv_dir, Config), - DataDir = ?config(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), inets_test_lib:stop_apps([inets]), ServerRoot = filename:join(PrivDir, "server_root"), inets_test_lib:del_dirs(ServerRoot), @@ -218,22 +220,22 @@ init_per_group(http_0_9, Config) -> [{http_version, "HTTP/0.9"} | Config] end; init_per_group(http_htaccess = Group, Config) -> - Path = ?config(doc_root, Config), + Path = proplists:get_value(doc_root, Config), catch remove_htaccess(Path), - create_htaccess_data(Path, ?config(address, Config)), + create_htaccess_data(Path, proplists:get_value(address, Config)), ok = start_apps(Group), init_httpd(Group, [{type, ip_comm} | Config]); init_per_group(https_htaccess = Group, Config) -> - Path = ?config(doc_root, Config), + Path = proplists:get_value(doc_root, Config), catch remove_htaccess(Path), - create_htaccess_data(Path, ?config(address, Config)), + create_htaccess_data(Path, proplists:get_value(address, Config)), init_ssl(Group, Config); init_per_group(auth_api, Config) -> [{auth_prefix, ""} | Config]; init_per_group(auth_api_dets, Config) -> [{auth_prefix, "dets_"} | Config]; init_per_group(auth_api_mnesia, Config) -> - start_mnesia(?config(node, Config)), + start_mnesia(proplists:get_value(node, Config)), [{auth_prefix, "mnesia_"} | Config]; init_per_group(_, Config) -> Config. @@ -271,7 +273,8 @@ end_per_group(_, _) -> %%-------------------------------------------------------------------- init_per_testcase(Case, Config) when Case == host; Case == trace -> - Prop = ?config(tc_group_properties, Config), + ct:timetrap({seconds, 20}), + Prop = proplists:get_value(tc_group_properties, Config), Name = proplists:get_value(name, Prop), Cb = case Name of http_1_0 -> @@ -282,11 +285,13 @@ init_per_testcase(Case, Config) when Case == host; Case == trace -> [{version_cb, Cb} | proplists:delete(version_cb, Config)]; init_per_testcase(range, Config) -> - DocRoot = ?config(doc_root, Config), + ct:timetrap({seconds, 20}), + DocRoot = proplists:get_value(doc_root, Config), create_range_data(DocRoot), Config; init_per_testcase(_, Config) -> + ct:timetrap({seconds, 20}), Config. end_per_testcase(_Case, _Config) -> @@ -300,10 +305,10 @@ head() -> [{doc, "HTTP HEAD request for static page"}]. head(Config) when is_list(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - ok = httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), ?config(node, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), proplists:get_value(node, Config), http_request("HEAD /index.html ", Version, Host), [{statuscode, head_status(Version)}, {version, Version}]). @@ -312,13 +317,13 @@ get() -> [{doc, "HTTP GET request for static page"}]. get(Config) when is_list(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Type = ?config(type, Config), - ok = httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Type = proplists:get_value(type, Config), + ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), transport_opts(Type, Config), - ?config(node, Config), + proplists:get_value(node, Config), http_request("GET /index.html ", Version, Host), [{statuscode, 200}, {header, "Content-Type", "text/html"}, @@ -339,8 +344,8 @@ basic_auth() -> [{doc, "Test Basic authentication with WWW-Authenticate header"}]. basic_auth(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), basic_auth_requiered(Config), %% Authentication OK! ["one:OnePassword" user first in user list] ok = auth_status(auth_request("/open/dummy.html", "one", "onePassword", Version, Host), Config, @@ -380,15 +385,15 @@ auth_api() -> [{doc, "Test mod_auth API"}]. auth_api(Config) when is_list(Config) -> - Prefix = ?config(auth_prefix, Config), + Prefix = proplists:get_value(auth_prefix, Config), do_auth_api(Prefix, Config). do_auth_api(AuthPrefix, Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Port = ?config(port, Config), - Node = ?config(node, Config), - ServerRoot = ?config(server_root, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Port = proplists:get_value(port, Config), + Node = proplists:get_value(node, Config), + ServerRoot = proplists:get_value(server_root, Config), ok = http_status("GET / ", Config, [{statuscode, 200}]), ok = auth_status(auth_request("/", "one", "WrongPassword", Version, Host), Config, @@ -550,12 +555,12 @@ ipv6(Config) when is_list(Config) -> {ok, Hostname0} = inet:gethostname(), case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of true -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), URI = http_request("GET / ", Version, Host), - httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), [inet6], - ?config(code, Config), + httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), [inet6], + proplists:get_value(code, Config), URI, [{statuscode, 200}, {version, Version}]); false -> @@ -576,11 +581,11 @@ htaccess() -> [{doc, "Test mod_auth API"}]. htaccess(Config) when is_list(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Type = ?config(type, Config), - Port = ?config(port, Config), - Node = ?config(node, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Type = proplists:get_value(type, Config), + Port = proplists:get_value(port, Config), + Node = proplists:get_value(node, Config), %% Control that authentication required! %% Control that the pages that shall be %% authenticated really need authenticatin @@ -691,23 +696,23 @@ host() -> [{doc, "Test host header"}]. host(Config) when is_list(Config) -> - Cb = ?config(version_cb, Config), - Cb:host(?config(type, Config), ?config(port, Config), - ?config(host, Config), ?config(node, Config)). + Cb = proplists:get_value(version_cb, Config), + Cb:host(proplists:get_value(type, Config), proplists:get_value(port, Config), + proplists:get_value(host, Config), proplists:get_value(node, Config)). %%------------------------------------------------------------------------- chunked() -> [{doc, "Check that the server accepts chunked requests."}]. chunked(Config) when is_list(Config) -> - httpd_1_1:chunked(?config(type, Config), ?config(port, Config), - ?config(host, Config), ?config(node, Config)). + httpd_1_1:chunked(proplists:get_value(type, Config), proplists:get_value(port, Config), + proplists:get_value(host, Config), proplists:get_value(node, Config)). %%------------------------------------------------------------------------- expect() -> ["Check that the server handles request with the expect header " "field appropiate"]. expect(Config) when is_list(Config) -> - httpd_1_1:expect(?config(type, Config), ?config(port, Config), - ?config(host, Config), ?config(node, Config)). + httpd_1_1:expect(proplists:get_value(type, Config), proplists:get_value(port, Config), + proplists:get_value(host, Config), proplists:get_value(node, Config)). %%------------------------------------------------------------------------- max_clients_1_1() -> [{doc, "Test max clients limit"}]. @@ -762,10 +767,10 @@ esi(Config) when is_list(Config) -> %%------------------------------------------------------------------------- mod_esi_chunk_timeout(Config) when is_list(Config) -> - ok = httpd_1_1:mod_esi_chunk_timeout(?config(type, Config), - ?config(port, Config), - ?config(host, Config), - ?config(node, Config)). + ok = httpd_1_1:mod_esi_chunk_timeout(proplists:get_value(type, Config), + proplists:get_value(port, Config), + proplists:get_value(host, Config), + proplists:get_value(node, Config)). %%------------------------------------------------------------------------- cgi() -> @@ -846,7 +851,7 @@ cgi(Config) when is_list(Config) -> cgi_chunked_encoding_test() -> [{doc, "Test chunked encoding together with mod_cgi "}]. cgi_chunked_encoding_test(Config) when is_list(Config) -> - Host = ?config(host, Config), + Host = proplists:get_value(host, Config), Script = case test_server:os_type() of {win32, _} -> @@ -858,9 +863,9 @@ cgi_chunked_encoding_test(Config) when is_list(Config) -> ["GET " ++ Script ++ " HTTP/1.1\r\nHost:"++ Host ++"\r\n\r\n", "GET /cgi-bin/erl/httpd_example/newformat HTTP/1.1\r\nHost:" ++ Host ++"\r\n\r\n"], - httpd_1_1:mod_cgi_chunked_encoding_test(?config(type, Config), ?config(port, Config), + httpd_1_1:mod_cgi_chunked_encoding_test(proplists:get_value(type, Config), proplists:get_value(port, Config), Host, - ?config(node, Config), + proplists:get_value(node, Config), Requests). %%------------------------------------------------------------------------- alias_1_1() -> @@ -920,52 +925,52 @@ range() -> [{doc, "Test Range header"}]. range(Config) when is_list(Config) -> - httpd_1_1:range(?config(type, Config), ?config(port, Config), - ?config(host, Config), ?config(node, Config)). + httpd_1_1:range(proplists:get_value(type, Config), proplists:get_value(port, Config), + proplists:get_value(host, Config), proplists:get_value(node, Config)). %%------------------------------------------------------------------------- if_modified_since() -> [{doc, "Test If-Modified-Since header"}]. if_modified_since(Config) when is_list(Config) -> - httpd_1_1:if_test(?config(type, Config), ?config(port, Config), - ?config(host, Config), ?config(node, Config), - ?config(doc_root, Config)). + httpd_1_1:if_test(proplists:get_value(type, Config), proplists:get_value(port, Config), + proplists:get_value(host, Config), proplists:get_value(node, Config), + proplists:get_value(doc_root, Config)). %%------------------------------------------------------------------------- trace() -> [{doc, "Test TRACE method"}]. trace(Config) when is_list(Config) -> - Cb = ?config(version_cb, Config), - Cb:trace(?config(type, Config), ?config(port, Config), - ?config(host, Config), ?config(node, Config)). + Cb = proplists:get_value(version_cb, Config), + Cb:trace(proplists:get_value(type, Config), proplists:get_value(port, Config), + proplists:get_value(host, Config), proplists:get_value(node, Config)). %%------------------------------------------------------------------------- light() -> ["Test light load"]. light(Config) when is_list(Config) -> - httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config), - ?config(node, Config), 10). + httpd_load:load_test(proplists:get_value(type, Config), proplists:get_value(port, Config), proplists:get_value(host, Config), + proplists:get_value(node, Config), 10). %%------------------------------------------------------------------------- medium() -> ["Test medium load"]. medium(Config) when is_list(Config) -> - httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config), - ?config(node, Config), 100). + httpd_load:load_test(proplists:get_value(type, Config), proplists:get_value(port, Config), proplists:get_value(host, Config), + proplists:get_value(node, Config), 100). %%------------------------------------------------------------------------- heavy() -> ["Test heavy load"]. heavy(Config) when is_list(Config) -> - httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config), - ?config(node, Config), + httpd_load:load_test(proplists:get_value(type, Config), proplists:get_value(port, Config), proplists:get_value(host, Config), + proplists:get_value(node, Config), 1000). %%------------------------------------------------------------------------- content_length() -> ["Tests that content-length is correct OTP-5775"]. content_length(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - ok = httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), ?config(node, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), proplists:get_value(node, Config), http_request("GET /cgi-bin/erl/httpd_example:get_bin ", Version, Host), [{statuscode, 200}, @@ -975,10 +980,10 @@ content_length(Config) -> bad_hex() -> ["Tests that a URI with a bad hexadecimal code is handled OTP-6003"]. bad_hex(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - ok = httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), ?config(node, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), proplists:get_value(node, Config), http_request("GET http://www.erlang.org/%skalle ", Version, Host), [{statuscode, 400}, @@ -987,10 +992,10 @@ bad_hex(Config) -> missing_CR() -> ["Tests missing CR in delimiter OTP-7304"]. missing_CR(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - ok = httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), ?config(node, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), proplists:get_value(node, Config), http_request_missing_CR("GET /index.html ", Version, Host), [{statuscode, 200}, {version, Version}]). @@ -1001,12 +1006,12 @@ customize() -> customize(Config) when is_list(Config) -> Version = "HTTP/1.1", - Host = ?config(host, Config), - Type = ?config(type, Config), - ok = httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), + Host = proplists:get_value(host, Config), + Type = proplists:get_value(type, Config), + ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), transport_opts(Type, Config), - ?config(node, Config), + proplists:get_value(node, Config), http_request("GET /index.html ", Version, Host), [{statuscode, 200}, {header, "Content-Type", "text/html"}, @@ -1019,12 +1024,12 @@ add_default() -> add_default(Config) when is_list(Config) -> Version = "HTTP/1.1", - Host = ?config(host, Config), - Type = ?config(type, Config), - ok = httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), + Host = proplists:get_value(host, Config), + Type = proplists:get_value(type, Config), + ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), transport_opts(Type, Config), - ?config(node, Config), + proplists:get_value(node, Config), http_request("GET /index.html ", Version, Host), [{statuscode, 200}, {header, "Content-Type", "text/html"}, @@ -1036,24 +1041,24 @@ add_default(Config) when is_list(Config) -> max_header() -> ["Denial Of Service (DOS) attack, prevented by max_header"]. max_header(Config) when is_list(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), case Version of "HTTP/0.9" -> {skip, not_implemented}; _ -> - dos_hostname(?config(type, Config), ?config(port, Config), Host, - ?config(node, Config), Version, ?MAX_HEADER_SIZE) + dos_hostname(proplists:get_value(type, Config), proplists:get_value(port, Config), Host, + proplists:get_value(node, Config), Version, ?MAX_HEADER_SIZE) end. %%------------------------------------------------------------------------- max_content_length() -> ["Denial Of Service (DOS) attack, prevented by max_content_length"]. max_content_length(Config) when is_list(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - garbage_content_length(?config(type, Config), ?config(port, Config), Host, - ?config(node, Config), Version). + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + garbage_content_length(proplists:get_value(type, Config), proplists:get_value(port, Config), Host, + proplists:get_value(node, Config), Version). %%------------------------------------------------------------------------- security_1_1(Config) when is_list(Config) -> @@ -1065,15 +1070,15 @@ security_1_0(Config) when is_list(Config) -> security() -> ["Test mod_security"]. security(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Port = ?config(port, Config), - Node = ?config(node, Config), - ServerRoot = ?config(server_root, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Port = proplists:get_value(port, Config), + Node = proplists:get_value(node, Config), + ServerRoot = proplists:get_value(server_root, Config), global:register_name(mod_security_test, self()), % Receive events - test_server:sleep(5000), + ct:sleep(5000), OpenDir = filename:join([ServerRoot, "htdocs", "open"]), @@ -1171,7 +1176,7 @@ security(Config) -> ["one"] = list_auth_users(Node, Port, OpenDir), %% Wait for successful auth to timeout. - test_server:sleep(?AUTH_TIMEOUT*1001), + ct:sleep(?AUTH_TIMEOUT*1001), [] = list_auth_users(Node, Port), @@ -1200,11 +1205,11 @@ disturbing_reconfiger_dies(Config) when is_list(Config) -> do_reconfiger_dies([{http_version, "HTTP/1.1"} | Config], disturbing). do_reconfiger_dies(Config, DisturbingType) -> - Server = ?config(server_pid, Config), - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Port = ?config(port, Config), - Type = ?config(type, Config), + Server = proplists:get_value(server_pid, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Port = proplists:get_value(port, Config), + Type = proplists:get_value(type, Config), HttpdConfig = httpd:info(Server), BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host), @@ -1235,11 +1240,11 @@ disturbing_0_9(Config) when is_list(Config) -> disturbing([{http_version, "HTTP/0.9"} | Config]). disturbing(Config) when is_list(Config)-> - Server = ?config(server_pid, Config), - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Port = ?config(port, Config), - Type = ?config(type, Config), + Server = proplists:get_value(server_pid, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Port = proplists:get_value(port, Config), + Type = proplists:get_value(type, Config), HttpdConfig = httpd:info(Server), BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host), {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)), @@ -1267,11 +1272,11 @@ non_disturbing_0_9(Config) when is_list(Config) -> non_disturbing([{http_version, "HTTP/0.9"} | Config]). non_disturbing(Config) when is_list(Config)-> - Server = ?config(server_pid, Config), - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Port = ?config(port, Config), - Type = ?config(type, Config), + Server = proplists:get_value(server_pid, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Port = proplists:get_value(port, Config), + Type = proplists:get_value(type, Config), HttpdConfig = httpd:info(Server), BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host), @@ -1296,15 +1301,15 @@ non_disturbing(Config) when is_list(Config)-> %% Internal functions ----------------------------------- %%-------------------------------------------------------------------- url(http, End, Config) -> - Port = ?config(port, Config), + Port = proplists:get_value(port, Config), {ok,Host} = inet:gethostname(), ?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End. do_max_clients(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Port = ?config(port, Config), - Type = ?config(type, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Port = proplists:get_value(port, Config), + Type = proplists:get_value(type, Config), Request = http_request("GET /index.html ", Version, Host), BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host), @@ -1314,7 +1319,7 @@ do_max_clients(Config) -> ok = httpd_test_lib:verify_request(Type, Host, Port, transport_opts(Type, Config), - ?config(node, Config), + proplists:get_value(node, Config), Request, [{statuscode, 503}, {version, Version}]), @@ -1327,7 +1332,7 @@ do_max_clients(Config) -> ok = httpd_test_lib:verify_request(Type, Host, Port, transport_opts(Type, Config), - ?config(node, Config), + proplists:get_value(node, Config), Request, [{statuscode, 200}, {version, Version}]). @@ -1406,7 +1411,7 @@ server_start(_, HttpdConfig) -> {Pid, proplists:get_value(port, Info)}. init_ssl(Group, Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), CaKey = {_Trusted,_} = erl_make_certs:make_cert([{key, dsa}, {subject, @@ -1457,54 +1462,54 @@ server_config(https_custom, Config) -> server_config(https_limit, Config) -> [{max_clients, 1}] ++ server_config(https, Config); server_config(http_basic_auth, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), auth_conf(ServerRoot) ++ server_config(http, Config); server_config(https_basic_auth, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), auth_conf(ServerRoot) ++ server_config(https, Config); server_config(http_auth_api, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), auth_api_conf(ServerRoot, plain) ++ server_config(http, Config); server_config(https_auth_api, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), auth_api_conf(ServerRoot, plain) ++ server_config(https, Config); server_config(http_auth_api_dets, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), auth_api_conf(ServerRoot, dets) ++ server_config(http, Config); server_config(https_auth_api_dets, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), auth_api_conf(ServerRoot, dets) ++ server_config(https, Config); server_config(http_auth_api_mnesia, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), auth_api_conf(ServerRoot, mnesia) ++ server_config(http, Config); server_config(https_auth_api_mnesia, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), auth_api_conf(ServerRoot, mnesia) ++ server_config(https, Config); server_config(http_htaccess, Config) -> auth_access_conf() ++ server_config(http, Config); server_config(https_htaccess, Config) -> auth_access_conf() ++ server_config(https, Config); server_config(http_security, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(http, Config); server_config(https_security, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(https, Config); server_config(http_mime_types, Config0) -> Config1 = basic_conf() ++ server_config(http, Config0), - ServerRoot = ?config(server_root, Config0), + ServerRoot = proplists:get_value(server_root, Config0), MimeTypesFile = filename:join([ServerRoot,"config", "mime.types"]), [{mime_types, MimeTypesFile} | proplists:delete(mime_types, Config1)]; server_config(http, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), [{port, 0}, {socket_type, {ip_comm, [{nodelay, true}]}}, {server_name,"httpd_test"}, {server_root, ServerRoot}, - {document_root, ?config(doc_root, Config)}, + {document_root, proplists:get_value(doc_root, Config)}, {bind_address, any}, - {ipfamily, ?config(ipfamily, Config)}, + {ipfamily, proplists:get_value(ipfamily, Config)}, {max_header_size, 256}, {max_header_action, close}, {directory_index, ["index.html", "welcome.html"]}, @@ -1519,7 +1524,7 @@ server_config(http, Config) -> ]; server_config(https, Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), [{socket_type, {essl, [{nodelay, true}, {cacertfile, @@ -1693,35 +1698,35 @@ mod_security_conf(SecFile, Dir) -> http_status(Request, Config, Expected) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Type = ?config(type, Config), - httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Type = proplists:get_value(type, Config), + httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), transport_opts(Type, Config), - ?config(node, Config), + proplists:get_value(node, Config), http_request(Request, Version, Host), Expected ++ [{version, Version}]). http_status(Request, HeadersAndBody, Config, Expected) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Type = ?config(type, Config), - httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Type = proplists:get_value(type, Config), + httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), transport_opts(Type, Config), - ?config(node, Config), + proplists:get_value(node, Config), http_request(Request, Version, Host, HeadersAndBody), Expected ++ [{version, Version}]). auth_status(AuthRequest, Config, Expected) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Type = ?config(type, Config), - httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), + Version = proplists:get_value(http_version, Config), + Host = proplists:get_value(host, Config), + Type = proplists:get_value(type, Config), + httpd_test_lib:verify_request(proplists:get_value(type, Config), Host, + proplists:get_value(port, Config), transport_opts(Type, Config), - ?config(node, Config), + proplists:get_value(node, Config), AuthRequest, Expected ++ [{version, Version}]). @@ -1773,11 +1778,11 @@ cleanup_mnesia() -> ok. transport_opts(ssl, Config) -> - PrivDir = ?config(priv_dir, Config), - [?config(ipfamily, Config), + PrivDir = proplists:get_value(priv_dir, Config), + [proplists:get_value(ipfamily, Config), {cacertfile, filename:join(PrivDir, "public_key_cacert.pem")}]; transport_opts(_, Config) -> - [?config(ipfamily, Config)]. + [proplists:get_value(ipfamily, Config)]. %%% mod_range @@ -2076,4 +2081,4 @@ peer(Config) -> "true"; _ -> "false" - end.
\ No newline at end of file + end. diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index 352ff3b1ae..f413248092 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -30,7 +30,8 @@ -define(URL_START, "http://localhost:"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 30}}]. all() -> [uri_too_long_414, @@ -66,8 +67,8 @@ end_per_group(_GroupName, Config) -> init_per_suite(Config) -> inets_test_lib:stop_apps([inets]), inets_test_lib:start_apps([inets]), - PrivDir = ?config(priv_dir, Config), - DataDir = ?config(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), Dummy = "<HTML> @@ -152,7 +153,7 @@ end_per_testcase(_Case, Config) -> uri_too_long_414() -> [{doc, "Test that too long uri's get 414 HTTP code"}]. uri_too_long_414(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), + HttpdConf = proplists:get_value(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{max_uri_size, 10} | HttpdConf]), Info = httpd:info(Pid), @@ -173,7 +174,7 @@ uri_too_long_414(Config) when is_list(Config) -> header_too_long_413() -> [{doc,"Test that too long headers's get 413 HTTP code"}]. header_too_long_413(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), + HttpdConf = proplists:get_value(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{max_header_size, 10} | HttpdConf]), Info = httpd:info(Pid), @@ -192,7 +193,7 @@ header_too_long_413(Config) when is_list(Config) -> entity_too_long() -> [{doc, "Test that too long versions and method strings are rejected"}]. entity_too_long(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), + HttpdConf = proplists:get_value(httpd_conf, Config), {ok, Pid} = inets:start(httpd, HttpdConf), Info = httpd:info(Pid), Port = proplists:get_value(port, Info), @@ -259,7 +260,7 @@ erl_script_nocache_opt(doc) -> erl_script_nocache_opt(suite) -> []; erl_script_nocache_opt(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), + HttpdConf = proplists:get_value(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{port, 0}, {erl_script_nocache, true} | HttpdConf]), Info = httpd:info(Pid), Port = proplists:get_value(port, Info), @@ -282,7 +283,7 @@ erl_script_nocache_opt(Config) when is_list(Config) -> escaped_url_in_error_body() -> [{doc, "Test Url-encoding see OTP-8940"}]. escaped_url_in_error_body(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), + HttpdConf = proplists:get_value(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]), Info = httpd:info(Pid), Port = proplists:get_value(port, Info), @@ -324,7 +325,7 @@ keep_alive_timeout(doc) -> keep_alive_timeout(suite) -> []; keep_alive_timeout(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), + HttpdConf = proplists:get_value(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{port, 0}, {keep_alive, true}, {keep_alive_timeout, 2} | HttpdConf]), Info = httpd:info(Pid), Port = proplists:get_value(port, Info), @@ -348,9 +349,9 @@ script_timeout(Config) when is_list(Config) -> ok. verify_script_timeout(Config, ScriptTimeout, StatusCode) -> - HttpdConf = ?config(httpd_conf, Config), - CgiScript = ?config(cgi_sleep, Config), - CgiDir = ?config(cgi_dir, Config), + HttpdConf = proplists:get_value(httpd_conf, Config), + CgiScript = proplists:get_value(cgi_sleep, Config), + CgiDir = proplists:get_value(cgi_dir, Config), {ok, Pid} = inets:start(httpd, [{port, 0}, {script_alias, {"/cgi-bin/", CgiDir ++ "/"}}, @@ -371,7 +372,7 @@ verify_script_timeout(Config, ScriptTimeout, StatusCode) -> slowdose() -> [{doc, "Testing minimum bytes per second option"}]. slowdose(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), + HttpdConf = proplists:get_value(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{port, 0}, {minimum_bytes_per_second, 200}|HttpdConf]), Info = httpd:info(Pid), Port = proplists:get_value(port, Info), @@ -386,9 +387,9 @@ slowdose(Config) when is_list(Config) -> %%------------------------------------------------------------------------- verify_script_nocache(Config, CgiNoCache, EsiNoCache, CgiOption, EsiOption) -> - HttpdConf = ?config(httpd_conf, Config), - CgiScript = ?config(cgi_printenv, Config), - CgiDir = ?config(cgi_dir, Config), + HttpdConf = proplists:get_value(httpd_conf, Config), + CgiScript = proplists:get_value(cgi_printenv, Config), + CgiDir = proplists:get_value(cgi_dir, Config), {ok, Pid} = inets:start(httpd, [{port, 0}, {script_alias, {"/cgi-bin/", CgiDir ++ "/"}}, diff --git a/lib/inets/test/httpd_block.erl b/lib/inets/test/httpd_block.erl index 44f6f644ad..45547e6d4e 100644 --- a/lib/inets/test/httpd_block.erl +++ b/lib/inets/test/httpd_block.erl @@ -133,7 +133,7 @@ block_disturbing_active_timeout_released(Type, Port, Host, Node) -> block_non_disturbing_active_timeout_not_released(Type, Port, Host, Node) -> process_flag(trap_exit, true), Poller = long_poll(Type, Host, Port, Node, 200, 60000), - test_server:sleep(5000), + ct:sleep(5000), ok = block_nd_server(Node, Host, Port, 40000), await_normal_process_exit(Poller, "poller", 60000), blocked = get_admin_state(Node, Host, Port), diff --git a/lib/inets/test/httpd_load.erl b/lib/inets/test/httpd_load.erl index 37ebc4fc90..7f4d16139f 100644 --- a/lib/inets/test/httpd_load.erl +++ b/lib/inets/test/httpd_load.erl @@ -63,7 +63,7 @@ load_test(Fun, URIs, Type, Host, Port, Node, 0, List) -> {'EXIT', Pid, Reason} -> Str = lists:flatten(io_lib:format("client ~p exited: ~p", [Pid,Reason])), - test_server:fail(Str); + ct:fail(Str); _ -> load_test(Fun, URIs, Type, Host, Port, Node, 0, List) end; @@ -86,12 +86,11 @@ load_test_client(Fun, [URI|URIs], Type, Host, Port, Node, Boss, Timeout) -> {'EXIT', {suite_failed, connection_closed, _, _}} -> %% Some platforms seems to handle heavy load badly. %% So, back off and see if this helps - %%?LOG("load_test_client->requestfailed:connection_closed"[]), 2 * Timeout; _ -> Timeout end, - test_server:sleep(Timeout1), + ct:sleep(Timeout1), load_test_client(Fun, URIs, Type, Host, Port, Node, Boss, Timeout1). load_test_client_done(Boss) -> diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index 7a1250cb29..d9118aa1a4 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -86,240 +86,116 @@ actions(Type, Port, Host, Node) -> security(ServerRoot, Type, Port, Host, Node) -> global:register_name(mod_security_test, self()), % Receive events - - tsp("security -> " - "sleep"), - test_server:sleep(5000), + + ct:sleep(5000), OpenDir = filename:join([ServerRoot, "htdocs", "open"]), %% Test blocking / unblocking of users. %% /open, require user one Aladdin - tsp("security -> " - "blocking and unblocking of users - " - "remove all existing users"), + remove_users(Node, ServerRoot, Host, Port, "open"), - tsp("security -> " - "blocking and unblocking of users - " - "auth request for nonex user 'one' - expect 401"), auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword", [{statuscode, 401}]), - tsp("security -> " - "blocking and unblocking of users - " - "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "onePassword"}]}, Node, Port), - tsp("security -> " - "blocking and unblocking of users - " - "auth request for nonex user 'two' - expect 401"), auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword", [{statuscode, 401}]), - tsp("security -> " - "blocking and unblocking of users - " - "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "two"}, {password, "twoPassword"}]}, Node, Port), - - tsp("security -> " - "blocking and unblocking of users - " - "auth request for nonex user 'Alladin' - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "Aladdin", "AladdinPassword", [{statuscode, 401}]), - tsp("security -> " - "blocking and unblocking of users - " - "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "Aladdin"}, {password, "AladdinPassword"}]}, Node, Port), - - tsp("security -> " - "blocking and unblocking of users - " - "add user 'one'"), add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []), - tsp("security -> " - "blocking and unblocking of users - " - "add user 'two'"), add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []), - tsp("security -> " - "blocking and unblocking of users - " - "auth request 1 for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), - - tsp("security -> " - "blocking and unblocking of users - " - "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "WrongPassword"}]}, Node, Port), - - tsp("security -> " - "blocking and unblocking of users - " - "auth request 2 for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), - tsp("security -> " - "blocking and unblocking of users - " - "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "WrongPassword"}]}, Node, Port), - - tsp("security -> " - "blocking and unblocking of users - " - "await block security event (two failed attempts)"), - receive_security_event({event, user_block, Port, OpenDir, + receive_security_event({event, user_block, Port, OpenDir, [{user, "one"}]}, Node, Port), - tsp("security -> " - "blocking and unblocking of users - " - "unregister - no more security events"), global:unregister_name(mod_security_test), % No more events. - tsp("security -> " - "blocking and unblocking of users - " - "auth request for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), - tsp("security -> " - "blocking and unblocking of users - " - "auth request for user 'one' with correct password - expect 403"), auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", [{statuscode, 403}]), %% User "one" should be blocked now.. - tsp("security -> " - "blocking and unblocking of users - " - "list blocked users - 'one' should be the only one"), case list_blocked_users(Node, Port) of [{"one",_, Port, OpenDir,_}] -> ok; Blocked -> - tsp(" *** unexpected blocked users ***" - "~n Blocked: ~p", [Blocked]), exit({unexpected_blocked, Blocked}) end, - tsp("security -> " - "blocking and unblocking of users - " - "list users blocked for dir '~p' - " - "user 'one' should be the only one", [OpenDir]), [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir), - tsp("security -> " - "blocking and unblocking of users - " - "unblock user 'one' for dir '~p'", [OpenDir]), true = unblock_user(Node, "one", Port, OpenDir), %% User "one" should not be blocked any more. - tsp("security -> " - "blocking and unblocking of users - " - "ensure user 'one' is no longer blocked"), [] = list_blocked_users(Node, Port), - - tsp("security -> " - "blocking and unblocking of users - " - "auth request for user 'one' with correct password - expect 200"), auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", [{statuscode, 200}]), %% Test list_auth_users & auth_timeout - - tsp("security -> " - "list-auth-users and auth-timeout - " - "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), - tsp("security -> " - "list-auth-users and auth-timeout - " - "auth request for user 'two' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword", [{statuscode, 401}]), - - tsp("security -> " - "list-auth-users and auth-timeout - " - "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), - tsp("security -> " - "list-auth-users and auth-timeout - " - "list auth users for dir '~p' - expect user 'one'", [OpenDir]), ["one"] = list_auth_users(Node, Port, OpenDir), - tsp("security -> " - "list-auth-users and auth-timeout - " - "auth request for user 'two' with correct password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", [{statuscode, 401}]), - tsp("security -> " - "list-auth-users and auth-timeout - " - "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), - tsp("security -> " - "list-auth-users and auth-timeout - " - "list auth users for dir '~p' - expect user 'one'", [OpenDir]), ["one"] = list_auth_users(Node, Port, OpenDir), %% Wait for successful auth to timeout. - tsp("security -> " - "list-auth-users and auth-timeout - " - "wait for successful auth to timeout"), - test_server:sleep(?AUTH_TIMEOUT*1001), - - tsp("security -> " - "list-auth-users and auth-timeout - " - "list auth users - expect none"), + ct:sleep(?AUTH_TIMEOUT*1001), + [] = list_auth_users(Node, Port), - tsp("security -> " - "list-auth-users and auth-timeout - " - "list auth users for dir '~p'~n - expect none", [OpenDir]), + [] = list_auth_users(Node, Port, OpenDir), %% "two" is blocked. - tsp("security -> " - "list-auth-users and auth-timeout - " - "unblock user 'two' for dir '~p'", [OpenDir]), true = unblock_user(Node, "two", Port, OpenDir), - - %% Test explicit blocking. Block user 'two'. - tsp("security -> " - "explicit blocking - list blocked users - should be none"), [] = list_blocked_users(Node,Port,OpenDir), - tsp("security -> " - "explicit blocking - " - "block user 'two' for dir '~p'", [OpenDir]), true = block_user(Node, "two", Port, OpenDir, 10), - - tsp("security -> " - "explicit blocking - " - "auth request for user 'two' with correct password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", - [{statuscode, 401}]), - tsp("security -> " - "done"). - + [{statuscode, 401}]). %%------------------------------------------------------------------------- auth(Type, Port, Host, Node) -> @@ -743,7 +619,6 @@ cgi(Type, Port, Host, Node) -> end, %% The length (> 100) is intentional -%% tsp("cgi -> request 01 with length > 100"), ok = httpd_test_lib: verify_request(Type, Host, Port, Node, "POST /cgi-bin/" ++ Script3 ++ @@ -771,55 +646,51 @@ cgi(Type, Port, Host, Node) -> {version, "HTTP/1.0"}, {header, "content-type", "text/plain"}]), -%% tsp("cgi -> request 02"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /cgi-bin/"++ Script ++ " HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), -%% tsp("cgi -> request 03"), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /cgi-bin/not_there " "HTTP/1.0\r\n\r\n", [{statuscode, 404},{statuscode, 500}, {version, "HTTP/1.0"}]), -%% tsp("cgi -> request 04"), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /cgi-bin/"++ Script ++ "?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), -%% tsp("cgi -> request 04"), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "POST /cgi-bin/"++ Script ++ " HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), -%% tsp("cgi -> request 05"), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /htbin/"++ Script ++ " HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), -%% tsp("cgi -> request 06"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /htbin/not_there " "HTTP/1.0\r\n\r\n", [{statuscode, 404},{statuscode, 500}, {version, "HTTP/1.0"}]), -%% tsp("cgi -> request 07"), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /htbin/"++ Script ++ "?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), -%% tsp("cgi -> request 08"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "POST /htbin/"++ Script ++ " HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), -%% tsp("cgi -> request 09"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "POST /htbin/"++ Script ++ " HTTP/1.0\r\n\r\n", @@ -827,31 +698,25 @@ cgi(Type, Port, Host, Node) -> {version, "HTTP/1.0"}]), %% Execute an existing, but bad CGI script.. -%% tsp("cgi -> request 10 - bad script"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "POST /htbin/"++ Script2 ++ " HTTP/1.0\r\n\r\n", [{statuscode, 404}, {version, "HTTP/1.0"}]), -%% tsp("cgi -> request 11 - bad script"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "POST /cgi-bin/"++ Script2 ++ " HTTP/1.0\r\n\r\n", [{statuscode, 404}, {version, "HTTP/1.0"}]), -%% tsp("cgi -> done"), - %% Check "ScriptNoCache" directive (default: false) ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /cgi-bin/" ++ Script ++ " HTTP/1.0\r\n\r\n", [{statuscode, 200}, {no_header, "cache-control"}, - {version, "HTTP/1.0"}]), - ok. - + {version, "HTTP/1.0"}]). %%-------------------------------------------------------------------- esi(Type, Port, Host, Node) -> @@ -1018,18 +883,15 @@ list_users(Node, Root, _Host, Port, Dir) -> receive_security_event(Event, Node, Port) -> - tsp("receive_security_event -> await ~w event", [element(2, Event)]), receive Event -> - tsp("receive_security_event -> " - "received expected ~w event", [element(2, Event)]), ok; {'EXIT', _, _} -> receive_security_event(Event, Node, Port) after 5000 -> %% Flush the message queue, to see if we got something... Msgs = inets_test_lib:flush(), - tsf({expected_event_not_received, Msgs}) + ct:fail({expected_event_not_received, Msgs}) end. @@ -1045,10 +907,10 @@ receive_security_event(Event, Node, Port) -> %% {'EXIT', _, _} -> %% receive_security_event(Event, Node, Port); %% Other -> -%% test_server:fail({unexpected_event, +%% ct:fail({unexpected_event, %% {expected, Event}, {received, Other}}) %% after 5000 -> -%% test_server:fail(no_event_recived) +%% ct:fail(no_event_recived) %% end. @@ -1130,17 +992,4 @@ check_lists_members1(L1,L2) -> {error,{lists_not_equal,L1,L2}}. -%% p(F) -> -%% p(F, []). - -%% p(F, A) -> -%% io:format(user, "~w:" ++ F ++ "~n", [?MODULE|A]). - -tsp(F) -> - inets_test_lib:tsp(F). -tsp(F, A) -> - inets_test_lib:tsp(F, A). - -tsf(Reason) -> - test_server:fail(Reason). diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl index 928d9dc391..5eaf3a28a0 100644 --- a/lib/inets/test/inets_SUITE.erl +++ b/lib/inets/test/inets_SUITE.erl @@ -28,10 +28,13 @@ -define(NUM_DEFAULT_SERVICES, 1). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{seconds,5}} + ]. all() -> - [{group, app_test}, {group, appup_test}, + [{group, app_test}, {group, services_test}, httpd_reload]. groups() -> @@ -42,8 +45,7 @@ groups() -> start_ftpc, start_tftpd ]}, - {app_test, [], [{inets_app_test, all}]}, - {appup_test, [], [{inets_appup_test, all}]}]. + {app_test, [], [app, appup]}]. init_per_group(_GroupName, Config) -> Config. @@ -84,6 +86,10 @@ end_per_suite(_Config) -> %% Note: This function is free to add any key/value pairs to the Config %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- +init_per_testcase(httpd_reload, Config) -> + inets:stop(), + ct:timetrap({seconds, 40}), + Config; init_per_testcase(_Case, Config) -> inets:stop(), Config. @@ -102,6 +108,15 @@ end_per_testcase(_, Config) -> %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- +app() -> + [{doc, "Test that the inets app file is ok"}]. +app(Config) when is_list(Config) -> + ok = ?t:app_test(inets). +%%-------------------------------------------------------------------- +appup() -> + [{doc, "Test that the inets appup file is ok"}]. +appup(Config) when is_list(Config) -> + ok = ?t:appup_test(inets). start_inets() -> [{doc, "Test inets API functions"}]. @@ -134,7 +149,7 @@ start_httpc() -> [{doc, "Start/stop of httpc service"}]. start_httpc(Config) when is_list(Config) -> process_flag(trap_exit, true), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), ok = inets:start(), {ok, Pid0} = inets:start(httpc, [{profile, foo}]), @@ -145,7 +160,7 @@ start_httpc(Config) when is_list(Config) -> inets:stop(httpc, Pid0), - test_server:sleep(100), + ct:sleep(100), Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], false = lists:member(Pid0, Pids1), @@ -188,7 +203,7 @@ start_httpd() -> [{doc, "Start/stop of httpd service"}]. start_httpd(Config) when is_list(Config) -> process_flag(trap_exit, true), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), HttpdConf = [{server_name, "httpd_test"}, {server_root, PrivDir}, {document_root, PrivDir}, {bind_address, any}], @@ -199,7 +214,7 @@ start_httpd(Config) when is_list(Config) -> [_|_] = inets:services_info(), inets:stop(httpd, Pid0), - test_server:sleep(500), + ct:sleep(500), Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], false = lists:member(Pid0, Pids1), {ok, Pid1} = @@ -212,7 +227,7 @@ start_httpd(Config) when is_list(Config) -> {'EXIT', Pid1, shutdown} -> ok after 100 -> - test_server:fail(stand_alone_not_shutdown) + ct:fail(stand_alone_not_shutdown) end, ok = inets:stop(), File0 = filename:join(PrivDir, "httpd.conf"), @@ -279,44 +294,38 @@ start_httpd(Config) when is_list(Config) -> start_ftpc(doc) -> [{doc, "Start/stop of ftpc service"}]; -start_ftpc(Config) when is_list(Config) -> +start_ftpc(Config0) when is_list(Config0) -> process_flag(trap_exit, true), ok = inets:start(), - try - begin - {_Tag, FtpdHost} = ftp_suite_lib:dirty_select_ftpd_host(Config), - case inets:start(ftpc, [{host, FtpdHost}]) of - {ok, Pid0} -> - Pids0 = [ServicePid || {_, ServicePid} <- - inets:services()], - true = lists:member(Pid0, Pids0), - [_|_] = inets:services_info(), - inets:stop(ftpc, Pid0), - test_server:sleep(100), - Pids1 = [ServicePid || {_, ServicePid} <- - inets:services()], - false = lists:member(Pid0, Pids1), - {ok, Pid1} = - inets:start(ftpc, [{host, FtpdHost}], stand_alone), - Pids2 = [ServicePid || {_, ServicePid} <- - inets:services()], - false = lists:member(Pid1, Pids2), - ok = inets:stop(stand_alone, Pid1), - receive - {'EXIT', Pid1, shutdown} -> - ok - after 100 -> - ct:fail(stand_alone_not_shutdown) - end, - ok = inets:stop(), - ok; - _ -> - {skip, "Unable to reach selected FTP server " ++ FtpdHost} - end - end - catch - throw:{error, not_found} -> - {skip, "No available FTP servers"} + case ftp_SUITE:init_per_suite(Config0) of + {skip, _} = Skip -> + Skip; + Config -> + FtpdHost = proplists:get_value(ftpd_host,Config), + {ok, Pid0} = inets:start(ftpc, [{host, FtpdHost}]), + Pids0 = [ServicePid || {_, ServicePid} <- + inets:services()], + true = lists:member(Pid0, Pids0), + [_|_] = inets:services_info(), + inets:stop(ftpc, Pid0), + ct:sleep(100), + Pids1 = [ServicePid || {_, ServicePid} <- + inets:services()], + false = lists:member(Pid0, Pids1), + {ok, Pid1} = + inets:start(ftpc, [{host, FtpdHost}], stand_alone), + Pids2 = [ServicePid || {_, ServicePid} <- + inets:services()], + false = lists:member(Pid1, Pids2), + ok = inets:stop(stand_alone, Pid1), + receive + {'EXIT', Pid1, shutdown} -> + ok + after 100 -> + ct:fail(stand_alone_not_shutdown) + end, + ok = inets:stop(), + catch ftp_SUITE:end_per_SUITE(Config) end. %%------------------------------------------------------------------------- @@ -331,7 +340,7 @@ start_tftpd(Config) when is_list(Config) -> true = lists:member(Pid0, Pids0), [_|_] = inets:services_info(), inets:stop(tftpd, Pid0), - test_server:sleep(100), + ct:sleep(100), Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], false = lists:member(Pid0, Pids1), {ok, Pid1} = @@ -343,7 +352,7 @@ start_tftpd(Config) when is_list(Config) -> {'EXIT', Pid1, shutdown} -> ok after 100 -> - test_server:fail(stand_alone_not_shutdown) + ct:fail(stand_alone_not_shutdown) end, ok = inets:stop(), application:load(inets), @@ -360,34 +369,34 @@ httpd_reload() -> [{doc, "Reload httpd configuration without restarting service"}]. httpd_reload(Config) when is_list(Config) -> process_flag(trap_exit, true), - PrivDir = ?config(priv_dir, Config), - DataDir = ?config(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), HttpdConf = [{server_name, "httpd_test"}, {server_root, PrivDir}, {document_root, PrivDir}, {bind_address, "localhost"}], ok = inets:start(), - test_server:sleep(5000), + ct:sleep(5000), {ok, Pid0} = inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf]), - test_server:sleep(5000), + ct:sleep(5000), [{port, Port0}] = httpd:info(Pid0, [port]), - test_server:sleep(5000), + ct:sleep(5000), [{document_root, PrivDir}] = httpd:info(Pid0, [document_root]), - test_server:sleep(5000), + ct:sleep(5000), ok = httpd:reload_config([{port, Port0}, {ipfamily, inet}, {server_name, "httpd_test"}, {server_root, PrivDir}, {document_root, DataDir}, {bind_address, "localhost"}], non_disturbing), - test_server:sleep(5000), + ct:sleep(5000), [{document_root, DataDir}] = httpd:info(Pid0, [document_root]), - test_server:sleep(5000), + ct:sleep(5000), ok = httpd:reload_config([{port, Port0}, {ipfamily, inet}, {server_name, "httpd_test"}, diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl deleted file mode 100644 index c6d0715e1f..0000000000 --- a/lib/inets/test/inets_app_test.erl +++ /dev/null @@ -1,245 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2015. 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% -%% -%% -%%---------------------------------------------------------------------- -%% Purpose: Verify the application specifics of the inets application -%%---------------------------------------------------------------------- --module(inets_app_test). - --compile(export_all). - --include("inets_test_lib.hrl"). - - -% t() -> megaco_test_lib:t(?MODULE). -% t(Case) -> megaco_test_lib:t({?MODULE, Case}). - - -%% Test server callbacks -init_per_testcase(_, Config) -> - Config. - -end_per_testcase(_Case, Config) -> - Config. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -all() -> - [fields, modules, exportall, app_depend]. - -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init_per_suite(suite) -> []; -init_per_suite(doc) -> []; -init_per_suite(Config) when is_list(Config) -> - case is_app(inets) of - {ok, AppFile} -> - io:format("AppFile: ~n~p~n", [AppFile]), - inets:print_version_info(), - [{app_file, AppFile}|Config]; - {error, Reason} -> - fail(Reason) - end. - -is_app(App) -> - LibDir = code:lib_dir(App), - File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]), - case file:consult(File) of - {ok, [{application, App, AppFile}]} -> - {ok, AppFile}; - Error -> - {error, {invalid_format, Error}} - end. - - -end_per_suite(suite) -> []; -end_per_suite(doc) -> []; -end_per_suite(Config) when is_list(Config) -> - Config. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -fields(suite) -> - []; -fields(doc) -> - []; -fields(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Fields = [vsn, description, modules, registered, applications], - case check_fields(Fields, AppFile, []) of - [] -> - ok; - Missing -> - fail({missing_fields, Missing}) - end. - -check_fields([], _AppFile, Missing) -> - Missing; -check_fields([Field|Fields], AppFile, Missing) -> - check_fields(Fields, AppFile, check_field(Field, AppFile, Missing)). - -check_field(Name, AppFile, Missing) -> - io:format("checking field: ~p~n", [Name]), - case lists:keymember(Name, 1, AppFile) of - true -> - Missing; - false -> - [Name|Missing] - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -modules(suite) -> - []; -modules(doc) -> - []; -modules(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Mods = key1search(modules, AppFile), - EbinList = get_ebin_mods(inets), - case missing_modules(Mods, EbinList, []) of - [] -> - ok; - Missing -> - throw({error, {missing_modules, Missing}}) - end, - case extra_modules(Mods, EbinList, []) of - [] -> - ok; - Extra -> - throw({error, {extra_modules, Extra}}) - end, - {ok, Mods}. - -get_ebin_mods(App) -> - LibDir = code:lib_dir(App), - EbinDir = filename:join([LibDir,"ebin"]), - {ok, Files0} = file:list_dir(EbinDir), - Files1 = [lists:reverse(File) || File <- Files0], - [list_to_atom(lists:reverse(Name)) || [$m,$a,$e,$b,$.|Name] <- Files1]. - - -missing_modules([], _Ebins, Missing) -> - Missing; -missing_modules([Mod|Mods], Ebins, Missing) -> - case lists:member(Mod, Ebins) of - true -> - missing_modules(Mods, Ebins, Missing); - false -> - io:format("missing module: ~p~n", [Mod]), - missing_modules(Mods, Ebins, [Mod|Missing]) - end. - - -extra_modules(_Mods, [], Extra) -> - Extra; -extra_modules(Mods, [Mod|Ebins], Extra) -> - case lists:member(Mod, Mods) of - true -> - extra_modules(Mods, Ebins, Extra); - false -> - io:format("supefluous module: ~p~n", [Mod]), - extra_modules(Mods, Ebins, [Mod|Extra]) - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -exportall(suite) -> - []; -exportall(doc) -> - []; -exportall(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Mods = key1search(modules, AppFile), - check_export_all(Mods). - - -check_export_all([]) -> - ok; -check_export_all([Mod|Mods]) -> - case (catch apply(Mod, module_info, [compile])) of - {'EXIT', {undef, _}} -> - check_export_all(Mods); - O -> - case lists:keysearch(options, 1, O) of - false -> - check_export_all(Mods); - {value, {options, List}} -> - case lists:member(export_all, List) of - true -> - throw({error, {export_all, Mod}}); - false -> - check_export_all(Mods) - end - end - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -app_depend(suite) -> - []; -app_depend(doc) -> - []; -app_depend(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Apps = key1search(applications, AppFile), - check_apps(Apps). - - -check_apps([]) -> - ok; -check_apps([App|Apps]) -> - case is_app(App) of - {ok, _} -> - check_apps(Apps); - Error -> - throw({error, {missing_app, {App, Error}}}) - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -fail(Reason) -> - exit({suite_failed, Reason}). - -key1search(Key, L) -> - case lists:keysearch(Key, 1, L) of - undefined -> - fail({not_found, Key, L}); - {value, {Key, Value}} -> - Value - end. diff --git a/lib/inets/test/inets_appup_test.erl b/lib/inets/test/inets_appup_test.erl deleted file mode 100644 index b76cd59141..0000000000 --- a/lib/inets/test/inets_appup_test.erl +++ /dev/null @@ -1,71 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2016. 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% -%% -%% -%%---------------------------------------------------------------------- -%% Purpose: Verify the application specifics of the Inets application -%%---------------------------------------------------------------------- --module(inets_appup_test). - --compile(export_all). --include_lib("common_test/include/ct.hrl"). - - -%% Test server callbacks -init_per_testcase(_Case, Config) -> - Config. - -end_per_testcase(_Case, Config) -> - Config. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -all() -> - [appup]. - -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init_per_suite(suite) -> []; -init_per_suite(doc) -> []; -init_per_suite(Config) when is_list(Config) -> - Config. - - -end_per_suite(suite) -> []; -end_per_suite(doc) -> []; -end_per_suite(Config) when is_list(Config) -> - Config. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -appup() -> - [{doc, "Perform a simple check of the inets appup file"}]. -appup(Config) when is_list(Config) -> - ok = ?t:appup_test(inets). diff --git a/lib/inets/test/inets_socketwrap_SUITE.erl b/lib/inets/test/inets_socketwrap_SUITE.erl index cfbda3ccf5..18df995215 100644 --- a/lib/inets/test/inets_socketwrap_SUITE.erl +++ b/lib/inets/test/inets_socketwrap_SUITE.erl @@ -61,8 +61,8 @@ end_per_testcase(_, Config) -> start_httpd_fd() -> [{doc, "Start/stop of httpd service with socket wrapper"}]. start_httpd_fd(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), - DataDir = ?config(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), HttpdConf = [{port, 80}, {ipfamily, inet}, {server_name, "httpd_fd_test"}, {server_root, PrivDir}, {document_root, PrivDir}, {bind_address, any}], @@ -94,7 +94,7 @@ start_httpd_fd(Config) when is_list(Config) -> start_tftpd_fd() -> [{doc, "Start/stop of tfpd service with socket wrapper"}]. start_tftpd_fd(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), case setup_node_info(node()) of {skip, _} = Skip -> Skip; diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl index 9836cd76e0..5b8b1463c8 100644 --- a/lib/inets/test/inets_sup_SUITE.erl +++ b/lib/inets/test/inets_sup_SUITE.erl @@ -26,7 +26,10 @@ %% Note: This directive should only be used in test suites. -compile(export_all). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{seconds, 10}} + ]. all() -> [default_tree, ftpc_worker, tftpd_worker, @@ -50,9 +53,7 @@ end_per_suite(_) -> ok. init_per_testcase(httpd_subtree, Config) -> - Dog = test_server:timetrap(?t:minutes(1)), - NewConfig = lists:keydelete(watchdog, 1, Config), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Dir = filename:join(PrivDir, "root"), ok = file:make_dir(Dir), @@ -67,7 +68,7 @@ init_per_testcase(httpd_subtree, Config) -> inets:stop(), inets:start(), inets:start(httpd, SimpleConfig), - [{watchdog, Dog} | NewConfig] + Config catch _:Reason -> inets:stop(), @@ -75,9 +76,7 @@ init_per_testcase(httpd_subtree, Config) -> end; init_per_testcase(httpd_subtree_profile, Config) -> - Dog = test_server:timetrap(?t:minutes(1)), - NewConfig = lists:keydelete(watchdog, 1, Config), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Dir = filename:join(PrivDir, "root"), ok = file:make_dir(Dir), @@ -93,7 +92,7 @@ init_per_testcase(httpd_subtree_profile, Config) -> inets:stop(), inets:start(), {ok, _} = inets:start(httpd, SimpleConfig), - [{watchdog, Dog} | NewConfig] + Config catch _:Reason -> inets:stop(), @@ -102,24 +101,18 @@ init_per_testcase(httpd_subtree_profile, Config) -> init_per_testcase(_Case, Config) -> - Dog = test_server:timetrap(?t:minutes(5)), - NewConfig = lists:keydelete(watchdog, 1, Config), inets:stop(), ok = inets:start(), - [{watchdog, Dog} | NewConfig]. + Config. end_per_testcase(Case, Config) when Case == httpd_subtree; Case == httpd_subtree_profile -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Dir = filename:join(PrivDir, "root"), inets_test_lib:del_dirs(Dir), ok; -end_per_testcase(_, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_, _) -> inets:stop(), ok. @@ -164,29 +157,25 @@ default_tree(Config) when is_list(Config) -> ftpc_worker() -> [{doc, "Makes sure the ftp worker processes are added and removed " "appropriatly to/from the supervison tree."}]. -ftpc_worker(Config) when is_list(Config) -> +ftpc_worker(Config0) when is_list(Config0) -> [] = supervisor:which_children(ftp_sup), - try - begin - {_Tag, FtpdHost} = ftp_suite_lib:dirty_select_ftpd_host(Config), - case inets:start(ftpc, [{host, FtpdHost}]) of - {ok, Pid} -> - case supervisor:which_children(ftp_sup) of - [{_,_, worker, [ftp]}] -> - inets:stop(ftpc, Pid), - test_server:sleep(5000), - [] = supervisor:which_children(ftp_sup), - ok; - Children -> - exit({unexpected_children, Children}) - end; - _ -> - {skip, "Unable to reach test FTP server"} + case ftp_SUITE:init_per_suite(Config0) of + {skip, _} = Skip -> + Skip; + Config -> + FtpdHost = proplists:get_value(ftpd_host,Config), + {ok, Pid} = inets:start(ftpc, [{host, FtpdHost}]), + case supervisor:which_children(ftp_sup) of + [{_,_, worker, [ftp]}] -> + inets:stop(ftpc, Pid), + ct:sleep(5000), + [] = supervisor:which_children(ftp_sup), + catch ftp_SUITE:end_per_SUITE(Config), + ok; + Children -> + catch ftp_SUITE:end_per_SUITE(Config), + exit({unexpected_children, Children}) end - end - catch - throw:{error, not_found} -> - {skip, "No available FTP servers"} end. tftpd_worker() -> @@ -200,7 +189,7 @@ tftpd_worker(Config) when is_list(Config) -> [{_,Pid0, worker, _}] = supervisor:which_children(tftp_sup), inets:stop(tftpd, Pid0), - test_server:sleep(5000), + ct:sleep(5000), [] = supervisor:which_children(tftp_sup), ok. diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl index a36ab0b1cf..d436395290 100644 --- a/lib/inets/test/inets_test_lib.hrl +++ b/lib/inets/test/inets_test_lib.hrl @@ -21,96 +21,8 @@ %%---------------------------------------------------------------------- %% Purpose: Define common macros for testing %%---------------------------------------------------------------------- - -%% - Print macros - - --ifdef(inets_debug). --define(DEBUG(F,A), inets_test_lib:debug(F, A, ?MODULE, ?LINE)). --else. --define(DEBUG(F,A),ok). --endif. - --ifdef(inets_log). --define(LOG(F,A), inets_test_lib:log(F, A, ?MODULE, ?LINE)). --else. --define(LOG(F,A),ok). --endif. - --define(INFO(F,A), inets_test_lib:info(F, A, ?MODULE, ?LINE)). --define(PRINT(F,A), inets_test_lib:print(F, A, ?MODULE, ?LINE)). - - -%% - Macros stolen from the test server - - --ifndef(line). --define(line,put(test_server_loc,{?MODULE,?LINE}),). --endif. - - -%% - OS Command and stuff - --define(OSCMD(Cmd), inets_test_lib:oscmd(Cmd)). - --define(PRINT_SYSTEM_INFO(P), inets_test_lib:print_system_info(P)). - --define(RUN_ON_OS(OS, FUN), inets_test_lib:run_on_os(OS, FUN)). --define(RUN_ON_WINDOWS(FUN), inets_test_lib:run_on_windows(FUN)). - - -%% - Test case macros - - --define(EXPANDABLE(I, C, F), inets_test_lib:expandable(I, C, F)). --define(OS_BASED_SKIP(Skippable), - inets_test_lib:os_based_skip(Skippable)). - --define(NON_PC_TC_MAYBE_SKIP(Config, Condition), - inets_test_lib:non_pc_tc_maybe_skip(Config, Condition, ?MODULE, ?LINE)). - - - %% - Misc macros - -define(ENSURE_STARTED(A), inets_test_lib:ensure_started(A)). --define(UPDATE(K,V,C), inets_test_lib:update_config(K,V,C)). --define(CONFIG(K,C), inets_test_lib:get_config(K,C)). --define(HOSTNAME(), inets_test_lib:hostname()). --define(SZ(X), inets_test_lib:sz(X)). - - -%% - Test case macros - - --define(SKIP(Reason), inets_test_lib:skip(Reason, ?MODULE, ?LINE)). --define(FAIL(Reason), inets_test_lib:fail(Reason, ?MODULE, ?LINE)). - - -%% - Socket macros - - --define(CONNECT(M,H,P), inets_test_lib:connect(M,H,P)). --define(SEND(M,S,D), inets_test_lib:send(M,S,D)). --define(CSEND(M,S,D,C,T), inets_test_lib:csend(M,S,D,C,T)). --define(CLOSE(M,S), inets_test_lib:close(M,S)). - - -%% - Time macros - - --define(HOURS(N), inets_test_lib:hours(N)). --define(MINS(N), inets_test_lib:minutes(N)). --define(SECS(N), inets_test_lib:seconds(N)). - --define(WD_START(T), inets_test_lib:watchdog_start(T)). --define(WD_STOP(P), inets_test_lib:watchdog_stop(P)). - --define(SLEEP(MSEC), inets_test_lib:sleep(MSEC)). --define(M(), inets_test_lib:millis()). --define(MDIFF(A,B), inets_test_lib:millis_diff(A,B)). - - -%% - Process utility macros - - --define(FLUSH(), inets_test_lib:flush_mqueue()). --define(ETRAP_GET(), inets_test_lib:trap_exit()). --define(ETRAP_SET(O), inets_test_lib:trap_exit(O)). - - diff --git a/lib/inets/test/old_httpd_SUITE.erl b/lib/inets/test/old_httpd_SUITE.erl index 9902c1459e..172db53844 100644 --- a/lib/inets/test/old_httpd_SUITE.erl +++ b/lib/inets/test/old_httpd_SUITE.erl @@ -286,9 +286,7 @@ init_per_suite(Config) -> "~n Config: ~p" "~n", [Config]), - ?PRINT_SYSTEM_INFO([]), - - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), SuiteTopDir = filename:join(PrivDir, ?MODULE), case file:make_dir(SuiteTopDir) of ok -> @@ -314,7 +312,7 @@ init_per_suite(Config) -> %%-------------------------------------------------------------------- end_per_suite(_Config) -> - %% SuiteTopDir = ?config(suite_top_dir, Config), + %% SuiteTopDir = proplists:get_value(suite_top_dir, Config), %% inets_test_lib:del_dirs(SuiteTopDir), ok. @@ -346,8 +344,8 @@ init_per_testcase2(Case, Config) -> SslNormal = integer_to_list(?SSL_PORT) ++ ".conf", SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf", - DataDir = ?config(data_dir, Config), - SuiteTopDir = ?config(suite_top_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + SuiteTopDir = proplists:get_value(suite_top_dir, Config), %% tsp("init_per_testcase2 -> " %% "~n SuiteDir: ~p" @@ -499,7 +497,7 @@ init_per_testcase3(Case, Config) -> Dog = test_server:timetrap(inets_test_lib:minutes(10)), NewConfig = lists:keydelete(watchdog, 1, Config), - TcTopDir = ?config(tc_top_dir, Config), + TcTopDir = proplists:get_value(tc_top_dir, Config), CaseRest = case atom_to_list(Case) of @@ -581,16 +579,16 @@ init_per_testcase3(Case, Config) -> {skip, _} = Skip -> Skip; "mod_auth_" ++ _ -> - start_mnesia(?config(node, Config)), + start_mnesia(proplists:get_value(node, Config)), [{watchdog, Dog} | NewConfig]; "mod_htaccess" -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), Path = filename:join([ServerRoot, "htdocs"]), catch remove_htaccess(Path), - create_htaccess_data(Path, ?config(address, Config)), + create_htaccess_data(Path, proplists:get_value(address, Config)), [{watchdog, Dog} | NewConfig]; "range" -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), Path = filename:join([ServerRoot, "htdocs"]), create_range_data(Path), [{watchdog, Dog} | NewConfig]; @@ -613,7 +611,7 @@ init_per_testcase3(Case, Config) -> %% Description: Cleanup after each test case %%-------------------------------------------------------------------- end_per_testcase(Case, Config) -> - Dog = ?config(watchdog, Config), + Dog = proplists:get_value(watchdog, Config), test_server:timetrap_cancel(Dog), end_per_testcase2(Case, lists:keydelete(watchdog, 1, Config)), ok. @@ -641,7 +639,7 @@ ip_mod_alias(suite) -> []; ip_mod_alias(Config) when is_list(Config) -> httpd_mod:alias(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -651,7 +649,7 @@ ip_mod_actions(suite) -> []; ip_mod_actions(Config) when is_list(Config) -> httpd_mod:actions(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -660,9 +658,9 @@ ip_mod_security(doc) -> ip_mod_security(suite) -> []; ip_mod_security(Config) when is_list(Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), httpd_mod:security(ServerRoot, ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -672,7 +670,7 @@ ip_mod_auth(suite) -> []; ip_mod_auth(Config) when is_list(Config) -> httpd_mod:auth(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -681,9 +679,9 @@ ip_mod_auth_api(doc) -> ip_mod_auth_api(suite) -> []; ip_mod_auth_api(Config) when is_list(Config) -> - ServerRoot = ?config(server_root, Config), - Host = ?config(host, Config), - Node = ?config(node, Config), + ServerRoot = proplists:get_value(server_root, Config), + Host = proplists:get_value(host, Config), + Node = proplists:get_value(node, Config), httpd_mod:auth_api(ServerRoot, "", ip_comm, ?IP_PORT, Host, Node), httpd_mod:auth_api(ServerRoot, "dets_", ip_comm, ?IP_PORT, Host, Node), httpd_mod:auth_api(ServerRoot, "mnesia_", ip_comm, ?IP_PORT, Host, Node), @@ -695,7 +693,7 @@ ip_mod_auth_mnesia_api(suite) -> []; ip_mod_auth_mnesia_api(Config) when is_list(Config) -> httpd_mod:auth_mnesia_api(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_mod_htaccess(doc) -> @@ -704,7 +702,7 @@ ip_mod_htaccess(suite) -> []; ip_mod_htaccess(Config) when is_list(Config) -> httpd_mod:htaccess(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_mod_cgi(doc) -> @@ -713,7 +711,7 @@ ip_mod_cgi(suite) -> []; ip_mod_cgi(Config) when is_list(Config) -> httpd_mod:cgi(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_mod_esi(doc) -> @@ -722,7 +720,7 @@ ip_mod_esi(suite) -> []; ip_mod_esi(Config) when is_list(Config) -> httpd_mod:esi(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -732,7 +730,7 @@ ip_mod_get(suite) -> []; ip_mod_get(Config) when is_list(Config) -> httpd_mod:get(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -742,7 +740,7 @@ ip_mod_head(suite) -> []; ip_mod_head(Config) when is_list(Config) -> httpd_mod:head(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_mod_all(doc) -> @@ -751,7 +749,7 @@ ip_mod_all(suite) -> []; ip_mod_all(Config) when is_list(Config) -> httpd_mod:all(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_load_light(doc) -> @@ -759,8 +757,8 @@ ip_load_light(doc) -> ip_load_light(suite) -> []; ip_load_light(Config) when is_list(Config) -> - httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config), + httpd_load:load_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config), get_nof_clients(ip_comm, light)), ok. %%------------------------------------------------------------------------- @@ -769,8 +767,8 @@ ip_load_medium(doc) -> ip_load_medium(suite) -> []; ip_load_medium(Config) when is_list(Config) -> - httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config), + httpd_load:load_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config), get_nof_clients(ip_comm, medium)), ok. %%------------------------------------------------------------------------- @@ -779,8 +777,8 @@ ip_load_heavy(doc) -> ip_load_heavy(suite) -> []; ip_load_heavy(Config) when is_list(Config) -> - httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config), + httpd_load:load_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config), get_nof_clients(ip_comm, heavy)), ok. @@ -791,8 +789,8 @@ ip_dos_hostname(doc) -> ip_dos_hostname(suite) -> []; ip_dos_hostname(Config) when is_list(Config) -> - dos_hostname(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config), ?MAX_HEADER_SIZE), + dos_hostname(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config), ?MAX_HEADER_SIZE), ok. @@ -802,13 +800,7 @@ ip_time_test(doc) -> ip_time_test(suite) -> []; ip_time_test(Config) when is_list(Config) -> - %% <CONDITIONAL-SKIP> - Skippable = [win32], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - - httpd_time_test:t(ip_comm, ?config(host, Config), ?IP_PORT), + httpd_time_test:t(ip_comm, proplists:get_value(host, Config), ?IP_PORT), ok. %%------------------------------------------------------------------------- @@ -818,8 +810,8 @@ ip_block_503(doc) -> ip_block_503(suite) -> []; ip_block_503(Config) when is_list(Config) -> - httpd_block:block_503(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), + httpd_block:block_503(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_block_disturbing_idle(doc) -> @@ -829,8 +821,8 @@ ip_block_disturbing_idle(suite) -> []; ip_block_disturbing_idle(Config) when is_list(Config) -> httpd_block:block_disturbing_idle(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_block_non_disturbing_idle(doc) -> @@ -840,8 +832,8 @@ ip_block_non_disturbing_idle(suite) -> []; ip_block_non_disturbing_idle(Config) when is_list(Config) -> httpd_block:block_non_disturbing_idle(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_block_disturbing_active(doc) -> @@ -851,8 +843,8 @@ ip_block_disturbing_active(suite) -> []; ip_block_disturbing_active(Config) when is_list(Config) -> httpd_block:block_disturbing_active(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_block_non_disturbing_active(doc) -> @@ -862,8 +854,8 @@ ip_block_non_disturbing_active(suite) -> []; ip_block_non_disturbing_active(Config) when is_list(Config) -> httpd_block:block_non_disturbing_idle(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -877,9 +869,9 @@ ip_block_disturbing_active_timeout_not_released(Config) when is_list(Config) -> httpd_block:block_disturbing_active_timeout_not_released(ip_comm, ?IP_PORT, - ?config(host, + proplists:get_value(host, Config), - ?config(node, + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -893,9 +885,9 @@ ip_block_disturbing_active_timeout_released(Config) when is_list(Config) -> httpd_block:block_disturbing_active_timeout_released(ip_comm, ?IP_PORT, - ?config(host, + proplists:get_value(host, Config), - ?config(node, + proplists:get_value(node, Config)), ok. @@ -910,9 +902,9 @@ ip_block_non_disturbing_active_timeout_not_released(Config) httpd_block: block_non_disturbing_active_timeout_not_released(ip_comm, ?IP_PORT, - ?config(host, + proplists:get_value(host, Config), - ?config(node, + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -927,9 +919,9 @@ ip_block_non_disturbing_active_timeout_released(Config) httpd_block: block_non_disturbing_active_timeout_released(ip_comm, ?IP_PORT, - ?config(host, + proplists:get_value(host, Config), - ?config(node, + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -939,8 +931,8 @@ ip_block_disturbing_blocker_dies(suite) -> []; ip_block_disturbing_blocker_dies(Config) when is_list(Config) -> httpd_block:disturbing_blocker_dies(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_block_non_disturbing_blocker_dies(doc) -> @@ -949,8 +941,8 @@ ip_block_non_disturbing_blocker_dies(suite) -> []; ip_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> httpd_block:non_disturbing_blocker_dies(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_restart_no_block(doc) -> @@ -958,8 +950,8 @@ ip_restart_no_block(doc) -> ip_restart_no_block(suite) -> []; ip_restart_no_block(Config) when is_list(Config) -> - httpd_block:restart_no_block(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), + httpd_block:restart_no_block(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_restart_disturbing_block(doc) -> @@ -967,33 +959,9 @@ ip_restart_disturbing_block(doc) -> ip_restart_disturbing_block(suite) -> []; ip_restart_disturbing_block(Config) when is_list(Config) -> - %% <CONDITIONAL-SKIP> - Condition = - fun() -> - case os:type() of - {unix, linux} -> - HW = string:strip(os:cmd("uname -m"), right, $\n), - case HW of - "ppc" -> - case inet:gethostname() of - {ok, "peach"} -> - true; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end - end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - httpd_block:restart_disturbing_block(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -1002,33 +970,9 @@ ip_restart_non_disturbing_block(doc) -> ip_restart_non_disturbing_block(suite) -> []; ip_restart_non_disturbing_block(Config) when is_list(Config) -> - %% <CONDITIONAL-SKIP> - Condition = - fun() -> - case os:type() of - {unix, linux} -> - HW = string:strip(os:cmd("uname -m"), right, $\n), - case HW of - "ppc" -> - case inet:gethostname() of - {ok, "peach"} -> - true; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end - end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - httpd_block:restart_non_disturbing_block(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -1043,7 +987,7 @@ essl_mod_alias(Config) when is_list(Config) -> ssl_mod_alias(Tag, Config) -> httpd_mod:alias(Tag, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), + proplists:get_value(host, Config), proplists:get_value(node, Config)), ok. @@ -1060,8 +1004,8 @@ essl_mod_actions(Config) when is_list(Config) -> ssl_mod_actions(Tag, Config) -> httpd_mod:actions(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1075,12 +1019,12 @@ essl_mod_security(Config) when is_list(Config) -> ssl_mod_security(essl, Config). ssl_mod_security(Tag, Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), httpd_mod:security(ServerRoot, Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1096,8 +1040,8 @@ essl_mod_auth(Config) when is_list(Config) -> ssl_mod_auth(Tag, Config) -> httpd_mod:auth(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1112,9 +1056,9 @@ essl_mod_auth_api(Config) when is_list(Config) -> ssl_mod_auth_api(essl, Config). ssl_mod_auth_api(Tag, Config) -> - ServerRoot = ?config(server_root, Config), - Host = ?config(host, Config), - Node = ?config(node, Config), + ServerRoot = proplists:get_value(server_root, Config), + Host = proplists:get_value(host, Config), + Node = proplists:get_value(node, Config), httpd_mod:auth_api(ServerRoot, "", Tag, ?SSL_PORT, Host, Node), httpd_mod:auth_api(ServerRoot, "dets_", Tag, ?SSL_PORT, Host, Node), httpd_mod:auth_api(ServerRoot, "mnesia_", Tag, ?SSL_PORT, Host, Node), @@ -1134,8 +1078,8 @@ essl_mod_auth_mnesia_api(Config) when is_list(Config) -> ssl_mod_auth_mnesia_api(Tag, Config) -> httpd_mod:auth_mnesia_api(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1151,8 +1095,8 @@ essl_mod_htaccess(Config) when is_list(Config) -> ssl_mod_htaccess(Tag, Config) -> httpd_mod:htaccess(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1168,8 +1112,8 @@ essl_mod_cgi(Config) when is_list(Config) -> ssl_mod_cgi(Tag, Config) -> httpd_mod:cgi(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1185,8 +1129,8 @@ essl_mod_esi(Config) when is_list(Config) -> ssl_mod_esi(Tag, Config) -> httpd_mod:esi(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1202,8 +1146,8 @@ essl_mod_get(Config) when is_list(Config) -> ssl_mod_get(Tag, Config) -> httpd_mod:get(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1219,8 +1163,8 @@ essl_mod_head(Config) when is_list(Config) -> ssl_mod_head(Tag, Config) -> httpd_mod:head(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1236,8 +1180,8 @@ essl_mod_all(Config) when is_list(Config) -> ssl_mod_all(Tag, Config) -> httpd_mod:all(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1253,8 +1197,8 @@ essl_load_light(Config) when is_list(Config) -> ssl_load_light(Tag, Config) -> httpd_load:load_test(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config), + proplists:get_value(host, Config), + proplists:get_value(node, Config), get_nof_clients(ssl, light)), ok. @@ -1269,16 +1213,10 @@ essl_load_medium(Config) when is_list(Config) -> ssl_load_medium(essl, Config). ssl_load_medium(Tag, Config) -> - %% <CONDITIONAL-SKIP> - Skippable = [win32], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - httpd_load:load_test(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config), + proplists:get_value(host, Config), + proplists:get_value(node, Config), get_nof_clients(ssl, medium)), ok. @@ -1293,16 +1231,10 @@ essl_load_heavy(Config) when is_list(Config) -> ssl_load_heavy(essl, Config). ssl_load_heavy(Tag, Config) -> - %% <CONDITIONAL-SKIP> - Skippable = [win32], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - httpd_load:load_test(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config), + proplists:get_value(host, Config), + proplists:get_value(node, Config), get_nof_clients(ssl, heavy)), ok. @@ -1320,8 +1252,8 @@ essl_dos_hostname(Config) when is_list(Config) -> ssl_dos_hostname(Tag, Config) -> dos_hostname(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config), + proplists:get_value(host, Config), + proplists:get_value(node, Config), ?MAX_HEADER_SIZE), ok. @@ -1337,23 +1269,8 @@ essl_time_test(Config) when is_list(Config) -> ssl_time_test(essl, Config). ssl_time_test(Tag, Config) when is_list(Config) -> - %% <CONDITIONAL-SKIP> - FreeBSDVersionVerify = - fun() -> - case os:version() of - {7, 1, _} -> % We only have one such machine, so... - true; - _ -> - false - end - end, - Skippable = [win32, {unix, [{freebsd, FreeBSDVersionVerify}]}], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - httpd_time_test:t(Tag, - ?config(host, Config), + proplists:get_value(host, Config), ?SSL_PORT), ok. @@ -1372,8 +1289,8 @@ essl_block_503(Config) when is_list(Config) -> ssl_block_503(Tag, Config) -> httpd_block:block_503(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1391,8 +1308,8 @@ essl_block_disturbing_idle(Config) when is_list(Config) -> ssl_block_disturbing_idle(Tag, Config) -> httpd_block:block_disturbing_idle(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1410,8 +1327,8 @@ essl_block_non_disturbing_idle(Config) when is_list(Config) -> ssl_block_non_disturbing_idle(Tag, Config) -> httpd_block:block_non_disturbing_idle(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1429,8 +1346,8 @@ essl_block_disturbing_active(Config) when is_list(Config) -> ssl_block_disturbing_active(Tag, Config) -> httpd_block:block_disturbing_active(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1448,8 +1365,8 @@ essl_block_non_disturbing_active(Config) when is_list(Config) -> ssl_block_non_disturbing_active(Tag, Config) -> httpd_block:block_non_disturbing_idle(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1468,8 +1385,8 @@ essl_block_disturbing_active_timeout_not_released(Config) ssl_block_disturbing_active_timeout_not_released(Tag, Config) -> Port = ?SSL_PORT, - Host = ?config(host, Config), - Node = ?config(node, Config), + Host = proplists:get_value(host, Config), + Node = proplists:get_value(node, Config), httpd_block:block_disturbing_active_timeout_not_released(Tag, Port, Host, Node), ok. @@ -1490,8 +1407,8 @@ essl_block_disturbing_active_timeout_released(Config) ssl_block_disturbing_active_timeout_released(Tag, Config) -> Port = ?SSL_PORT, - Host = ?config(host, Config), - Node = ?config(node, Config), + Host = proplists:get_value(host, Config), + Node = proplists:get_value(node, Config), httpd_block:block_disturbing_active_timeout_released(Tag, Port, Host, @@ -1513,8 +1430,8 @@ essl_block_non_disturbing_active_timeout_not_released(Config) ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) -> Port = ?SSL_PORT, - Host = ?config(host, Config), - Node = ?config(node, Config), + Host = proplists:get_value(host, Config), + Node = proplists:get_value(node, Config), httpd_block:block_non_disturbing_active_timeout_not_released(Tag, Port, Host, @@ -1539,8 +1456,8 @@ essl_block_non_disturbing_active_timeout_released(Config) ssl_block_non_disturbing_active_timeout_released(Tag, Config) when is_list(Config) -> Port = ?SSL_PORT, - Host = ?config(host, Config), - Node = ?config(node, Config), + Host = proplists:get_value(host, Config), + Node = proplists:get_value(node, Config), httpd_block:block_non_disturbing_active_timeout_released(Tag, Port, Host, @@ -1562,8 +1479,8 @@ essl_block_disturbing_blocker_dies(Config) when is_list(Config) -> ssl_block_disturbing_blocker_dies(Tag, Config) -> httpd_block:disturbing_blocker_dies(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1579,8 +1496,8 @@ essl_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> ssl_block_non_disturbing_blocker_dies(Tag, Config) -> httpd_block:non_disturbing_blocker_dies(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1597,8 +1514,8 @@ essl_restart_no_block(Config) when is_list(Config) -> ssl_restart_no_block(Tag, Config) -> httpd_block:restart_no_block(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1613,43 +1530,9 @@ essl_restart_disturbing_block(Config) when is_list(Config) -> ssl_restart_disturbing_block(essl, Config). ssl_restart_disturbing_block(Tag, Config) -> - %% <CONDITIONAL-SKIP> - Condition = - fun() -> - case os:type() of - {unix, linux} -> - case ?OSCMD("uname -m") of - "ppc" -> - case file:read_file_info("/etc/fedora-release") of - {ok, _} -> - case ?OSCMD("awk '{print $2}' /etc/fedora-release") of - "release" -> - %% Fedora 7 and later - case ?OSCMD("awk '{print $3}' /etc/fedora-release") of - "7" -> - true; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end - end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - httpd_block:restart_disturbing_block(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1664,34 +1547,10 @@ essl_restart_non_disturbing_block(Config) when is_list(Config) -> ssl_restart_non_disturbing_block(essl, Config). ssl_restart_non_disturbing_block(Tag, Config) -> - %% <CONDITIONAL-SKIP> - Condition = - fun() -> - case os:type() of - {unix, linux} -> - HW = string:strip(os:cmd("uname -m"), right, $\n), - case HW of - "ppc" -> - case inet:gethostname() of - {ok, "peach"} -> - true; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end - end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - httpd_block:restart_non_disturbing_block(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. @@ -1701,8 +1560,8 @@ ip_host(doc) -> ip_host(suite)-> []; ip_host(Config) when is_list(Config) -> - httpd_1_1:host(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), + httpd_1_1:host(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_chunked(doc) -> @@ -1710,8 +1569,8 @@ ip_chunked(doc) -> ip_chunked(suite) -> []; ip_chunked(Config) when is_list(Config) -> - httpd_1_1:chunked(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), + httpd_1_1:chunked(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_expect(doc) -> @@ -1720,8 +1579,8 @@ ip_expect(doc) -> ip_expect(suite)-> []; ip_expect(Config) when is_list(Config) -> - httpd_1_1:expect(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), + httpd_1_1:expect(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_range(doc) -> @@ -1729,8 +1588,8 @@ ip_range(doc) -> ip_range(suite)-> []; ip_range(Config) when is_list(Config) -> - httpd_1_1:range(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), + httpd_1_1:range(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_if_test(doc) -> @@ -1738,10 +1597,10 @@ ip_if_test(doc) -> ip_if_test(suite) -> []; ip_if_test(Config) when is_list(Config) -> - ServerRoot = ?config(server_root, Config), + ServerRoot = proplists:get_value(server_root, Config), DocRoot = filename:join([ServerRoot, "htdocs"]), - httpd_1_1:if_test(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config), DocRoot), + httpd_1_1:if_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config), DocRoot), ok. %%------------------------------------------------------------------------- ip_http_trace(doc) -> @@ -1749,8 +1608,8 @@ ip_http_trace(doc) -> ip_http_trace(suite) -> []; ip_http_trace(Config) when is_list(Config) -> - httpd_1_1:http_trace(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), + httpd_1_1:http_trace(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- ip_http1_1_head(doc) -> @@ -1758,8 +1617,8 @@ ip_http1_1_head(doc) -> ip_http1_1_head(suite)-> []; ip_http1_1_head(Config) when is_list(Config) -> - httpd_1_1:head(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), + httpd_1_1:head(ip_comm, ?IP_PORT, proplists:get_value(host, Config), + proplists:get_value(node, Config)), ok. %%------------------------------------------------------------------------- @@ -1768,8 +1627,8 @@ ip_get_0_9(doc) -> ip_get_0_9(suite)-> []; ip_get_0_9(Config) when is_list(Config) -> - Host = ?config(host, Config), - Node = ?config(node, Config), + Host = proplists:get_value(host, Config), + Node = proplists:get_value(node, Config), ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, "GET / \r\n\r\n", [{statuscode, 200}, @@ -1791,8 +1650,8 @@ ip_head_1_0(doc) -> ip_head_1_0(suite)-> []; ip_head_1_0(Config) when is_list(Config) -> - Host = ?config(host, Config), - Node = ?config(node, Config), + Host = proplists:get_value(host, Config), + Node = proplists:get_value(node, Config), ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, "HEAD / HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), @@ -1804,8 +1663,8 @@ ip_get_1_0(doc) -> ip_get_1_0(suite)-> []; ip_get_1_0(Config) when is_list(Config) -> - Host = ?config(host, Config), - Node = ?config(node, Config), + Host = proplists:get_value(host, Config), + Node = proplists:get_value(node, Config), ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, "GET / HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), @@ -1817,8 +1676,8 @@ ip_post_1_0(doc) -> ip_post_1_0(suite)-> []; ip_post_1_0(Config) when is_list(Config) -> - Host = ?config(host, Config), - Node = ?config(node, Config), + Host = proplists:get_value(host, Config), + Node = proplists:get_value(node, Config), %% Test the post message formatin 1.0! Real post are testes elsewhere ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, "POST / HTTP/1.0\r\n\r\n " @@ -1832,7 +1691,7 @@ ip_mod_cgi_chunked_encoding_test(doc) -> ip_mod_cgi_chunked_encoding_test(suite)-> []; ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) -> - Host = ?config(host, Config), + Host = proplists:get_value(host, Config), Script = case test_server:os_type() of {win32, _} -> @@ -1846,7 +1705,7 @@ ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) -> ++ Host ++"\r\n\r\n"], httpd_1_1:mod_cgi_chunked_encoding_test(ip_comm, ?IP_PORT, Host, - ?config(node, Config), + proplists:get_value(node, Config), Requests), ok. @@ -1875,7 +1734,7 @@ ipv6_hostname(SocketType, Port, Config) when is_list(Config) -> "~n SocketType: ~p" "~n Port: ~p" "~n Config: ~p", [SocketType, Port, Config]), - Host = ?config(host, Config), + Host = proplists:get_value(host, Config), URI = "GET HTTP://" ++ Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n", tsp("ipv6_hostname -> Host: ~p", [Host]), @@ -1910,7 +1769,7 @@ ipv6_address(SocketType, Port, Config) when is_list(Config) -> "~n SocketType: ~p" "~n Port: ~p" "~n Config: ~p", [SocketType, Port, Config]), - Host = ?config(host, Config), + Host = proplists:get_value(host, Config), tsp("ipv6_address -> Host: ~p", [Host]), URI = "GET HTTP://" ++ Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n", @@ -1927,8 +1786,8 @@ ticket_5775(doc) -> ticket_5775(suite) -> []; ticket_5775(Config) -> - ok=httpd_test_lib:verify_request(ip_comm, ?config(host, Config), - ?IP_PORT, ?config(node, Config), + ok=httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config), + ?IP_PORT, proplists:get_value(node, Config), "GET /cgi-bin/erl/httpd_example:get_bin " "HTTP/1.0\r\n\r\n", [{statuscode, 200}, @@ -1939,9 +1798,9 @@ ticket_5865(doc) -> ticket_5865(suite) -> []; ticket_5865(Config) -> - ?SKIP(as_of_r15_behaviour_of_calendar_has_changed), - Host = ?config(host,Config), - ServerRoot = ?config(server_root, Config), + ct:skip(as_of_r15_behaviour_of_calendar_has_changed), + Host = proplists:get_value(host,Config), + ServerRoot = proplists:get_value(server_root, Config), DocRoot = filename:join([ServerRoot, "htdocs"]), File = filename:join([DocRoot,"last_modified.html"]), @@ -1957,7 +1816,7 @@ ticket_5865(Config) -> case file:write_file_info(File,FI#file_info{mtime=Bad_mtime}) of ok -> ok = httpd_test_lib:verify_request(ip_comm, Host, - ?IP_PORT, ?config(node, Config), + ?IP_PORT, proplists:get_value(node, Config), "GET /last_modified.html" " HTTP/1.1\r\nHost:" ++Host++"\r\n\r\n", @@ -1977,8 +1836,8 @@ ticket_5913(doc) -> ["Tests that a header without last-modified is handled"]; ticket_5913(suite) -> []; ticket_5913(Config) -> - ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config), - ?IP_PORT, ?config(node, Config), + ok = httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config), + ?IP_PORT, proplists:get_value(node, Config), "GET /cgi-bin/erl/httpd_example:get_bin " "HTTP/1.0\r\n\r\n", [{statuscode, 200}, @@ -1989,8 +1848,8 @@ ticket_6003(doc) -> ["Tests that a URI with a bad hexadecimal code is handled"]; ticket_6003(suite) -> []; ticket_6003(Config) -> - ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config), - ?IP_PORT, ?config(node, Config), + ok = httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config), + ?IP_PORT, proplists:get_value(node, Config), "GET http://www.erlang.org/%skalle " "HTTP/1.0\r\n\r\n", [{statuscode, 400}, @@ -2002,8 +1861,8 @@ ticket_7304(doc) -> ticket_7304(suite) -> []; ticket_7304(Config) -> - ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config), - ?IP_PORT, ?config(node, Config), + ok = httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config), + ?IP_PORT, proplists:get_value(node, Config), "GET / HTTP/1.0\r\n\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), @@ -2030,11 +1889,11 @@ dos_hostname(Type, Port, Host, Node, Max) -> %%-------------------------------------------------------------------- %% Other help functions create_config(Config, Access, FileName) -> - ServerRoot = ?config(server_root, Config), - TcTopDir = ?config(tc_top_dir, Config), - Port = ?config(port, Config), - Type = ?config(sock_type, Config), - Host = ?config(host, Config), + ServerRoot = proplists:get_value(server_root, Config), + TcTopDir = proplists:get_value(tc_top_dir, Config), + Port = proplists:get_value(port, Config), + Type = proplists:get_value(sock_type, Config), + Host = proplists:get_value(host, Config), Mods = io_lib:format("~p", [httpd_mod]), Funcs = io_lib:format("~p", [ssl_password_cb]), MaxHdrSz = io_lib:format("~p", [256]), @@ -2424,13 +2283,13 @@ create_range_data(Path) -> "12345678901234567890"])). create_ipv6_config(Config, FileName, Ipv6Address) -> - ServerRoot = ?config(server_root, Config), - TcTopDir = ?config(tc_top_dir, Config), - Port = ?config(port, Config), - SockType = ?config(sock_type, Config), + ServerRoot = proplists:get_value(server_root, Config), + TcTopDir = proplists:get_value(tc_top_dir, Config), + Port = proplists:get_value(port, Config), + SockType = proplists:get_value(sock_type, Config), Mods = io_lib:format("~p", [httpd_mod]), Funcs = io_lib:format("~p", [ssl_password_cb]), - Host = ?config(ipv6_host, Config), + Host = proplists:get_value(ipv6_host, Config), MaxHdrSz = io_lib:format("~p", [256]), MaxHdrAct = io_lib:format("~p", [close]), diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index bca04aa244..5ff167bcb3 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -11,7 +11,7 @@ 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 @@ -207,6 +207,10 @@ fe80::204:acff:fe17:bf38 <desc> <p>Returns a <c>hostent</c> record for the host with the specified hostname.</p> + <p>If resolver option <c>inet6</c> is <c>true</c>, + an IPv6 address is looked up. If that fails, + the IPv4 address is looked up and returned on + IPv6-mapped IPv4 format.</p> </desc> </func> @@ -1267,4 +1271,3 @@ inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code> </list> </section> </erlref> - diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index c1ae99ea24..713a9cf725 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -439,7 +439,12 @@ getstat(Socket,What) -> Hostent :: hostent(). gethostbyname(Name) -> - gethostbyname_tm(Name, inet, false). + case inet_db:res_option(inet6) of + true -> + gethostbyname_tm(Name, inet6, false); + false -> + gethostbyname_tm(Name, inet, false) + end. -spec gethostbyname(Hostname, Family) -> {ok, Hostent} | {error, posix()} when diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl index 64b28bb49b..f91d7ef7c3 100644 --- a/lib/kernel/src/inet_tcp_dist.erl +++ b/lib/kernel/src/inet_tcp_dist.erl @@ -73,7 +73,8 @@ gen_listen(Driver, Name) -> {ok, Socket} -> TcpAddress = get_tcp_address(Driver, Socket), {_,Port} = TcpAddress#net_address.address, - case erl_epmd:register_node(Name, Port) of + ErlEpmd = net_kernel:epmd_module(), + case ErlEpmd:register_node(Name, Port) of {ok, Creation} -> {ok, {Socket, TcpAddress, Creation}}; Error -> @@ -280,7 +281,8 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> case inet:getaddr(Address, AddressFamily) of {ok, Ip} -> Timer = dist_util:start_timer(SetupTime), - case erl_epmd:port_please(Name, Ip) of + ErlEpmd = net_kernel:epmd_module(), + case ErlEpmd:port_please(Name, Ip) of {port, TcpPort, Version} -> ?trace("port_please(~p) -> version ~p~n", [Node,Version]), diff --git a/lib/kernel/src/net_adm.erl b/lib/kernel/src/net_adm.erl index a38356c224..8ec275b88b 100644 --- a/lib/kernel/src/net_adm.erl +++ b/lib/kernel/src/net_adm.erl @@ -96,7 +96,8 @@ names() -> Reason :: address | file:posix(). names(Hostname) -> - erl_epmd:names(Hostname). + ErlEpmd = net_kernel:epmd_module(), + ErlEpmd:names(Hostname). -spec dns_hostname(Host) -> {ok, Name} | {error, Host} when Host :: atom() | string(), diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index e7b44a714c..a332e7966b 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -27,7 +27,7 @@ -export([get_arguments/1, get_argument/1, boot_var/1, restart/1, many_restarts/0, many_restarts/1, get_plain_arguments/1, - reboot/1, stop/1, get_status/1, script_id/1]). + reboot/1, stop_status/1, stop/1, get_status/1, script_id/1]). -export([boot1/1, boot2/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -48,7 +48,7 @@ suite() -> all() -> [get_arguments, get_argument, boot_var, many_restarts, - get_plain_arguments, restart, get_status, script_id, + get_plain_arguments, restart, stop_status, get_status, script_id, {group, boot}]. groups() -> @@ -465,6 +465,20 @@ reboot(Config) when is_list(Config) -> %% ------------------------------------------------ %% %% ------------------------------------------------ +stop_status(Config) when is_list(Config) -> + badarg = catch_stop([65,[66],67]), % flat strings only + badarg = catch_stop([65, 666, 67]), % only bytes in string + badarg = catch_stop(abort), % 'abort' not allowed + badarg = catch_stop(true), % other atoms not allowed + badarg = catch_stop(-1), % no negative statuses + ok. + +catch_stop(Status) -> + try init:stop(Status) catch error:badarg -> badarg end. + +%% ------------------------------------------------ +%% +%% ------------------------------------------------ stop(Config) when is_list(Config) -> Args = args(), {ok, Node} = start_node(init_test, Args), diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl index 73d170d1fa..c79f790973 100644 --- a/lib/mnesia/src/mnesia_index.erl +++ b/lib/mnesia/src/mnesia_index.erl @@ -513,11 +513,11 @@ db_put({dets, Ixt}, V) -> ok = dets:insert(Ixt, V). db_get({ram, _}=Ixt, IxKey) -> - Pat = [{{{IxKey, '$1'}}, [], [{{IxKey,'$1'}}]}], + Pat = [{{{IxKey, '$1'}}, [], [{element, 1, '$_'}]}], db_select(Ixt, Pat); db_get({{ext,_,_} = _Storage, {_,_,{_,Type}}} = Ixt, IxKey) -> Pat = case Type of - ordered -> [{{{IxKey, '$1'}}, [], [{{IxKey,'$1'}}]}]; + ordered -> [{{{IxKey, '$1'}}, [], [{element, 1, '$_'}]}]; bag -> [{{IxKey, '_'}, [], ['$_']}] end, db_select(Ixt, Pat); diff --git a/lib/mnesia/test/mnesia_dirty_access_test.erl b/lib/mnesia/test/mnesia_dirty_access_test.erl index c9df8ed353..6d970ac990 100644 --- a/lib/mnesia/test/mnesia_dirty_access_test.erl +++ b/lib/mnesia/test/mnesia_dirty_access_test.erl @@ -461,77 +461,81 @@ dirty_index_update_set_xets(Config) when is_list(Config) -> dirty_index_update_set(Config, ext_ets). dirty_index_update_set(Config, Storage) -> - [Node1] = Nodes = ?acquire_nodes(1, Config), - Tab = index_test, - ValPos = v1, + [Node1] = Nodes = ?acquire_nodes(1, Config), + Tab = index_test, + ValPos = v1, ValPos2 = v3, Def = [{attributes, [k, v1, v2, v3]}, {Storage, [Node1]}, - {index, [ValPos]}], - ?match({atomic, ok}, mnesia:create_table(Tab, Def)), - + {index, [ValPos]}], + ?match({atomic, ok}, mnesia:create_table(Tab, Def)), + Pat1 = {Tab, '$1', 2, '$2', '$3'}, - Pat2 = {Tab, '$1', '$2', '$3', '$4'}, - - Rec1 = {Tab, 1, 2, 3, 4}, + Pat2 = {Tab, '$1', '$2', '$3', '$4'}, + Pat3 = {Tab, '_', '_', '_', {4, 14}}, + + Rec1 = {Tab, 1, 2, 3, {4, 14}}, Rec2 = {Tab, 2, 2, 13, 14}, - Rec3 = {Tab, 1, 12, 13, 14}, - Rec4 = {Tab, 4, 2, 13, 14}, - + Rec3 = {Tab, 1, 12, 13, 14}, + Rec4 = {Tab, 4, 2, 13, 14}, + ?match([], mnesia:dirty_index_read(Tab, 2, ValPos)), ?match(ok, mnesia:dirty_write(Rec1)), ?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)), - + ?match(ok, mnesia:dirty_write(Rec2)), R1 = mnesia:dirty_index_read(Tab, 2, ValPos), ?match([Rec1, Rec2], lists:sort(R1)), - + ?match(ok, mnesia:dirty_write(Rec3)), R2 = mnesia:dirty_index_read(Tab, 2, ValPos), ?match([Rec2], lists:sort(R2)), ?match([Rec2], mnesia:dirty_index_match_object(Pat1, ValPos)), - - {atomic, R3} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end), + + {atomic, R3} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end), ?match([Rec3, Rec2], lists:sort(R3)), - + ?match(ok, mnesia:dirty_write(Rec4)), R4 = mnesia:dirty_index_read(Tab, 2, ValPos), ?match([Rec2, Rec4], lists:sort(R4)), - + ?match(ok, mnesia:dirty_delete({Tab, 4})), ?match([Rec2], mnesia:dirty_index_read(Tab, 2, ValPos)), - + ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)), ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)), ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)), - + R5 = mnesia:dirty_match_object(Pat2), ?match([Rec3, Rec2, Rec4], lists:sort(R5)), - + R6 = mnesia:dirty_index_read(Tab, 2, ValPos), - ?match([Rec2, Rec4], lists:sort(R6)), - ?match([], mnesia:dirty_index_read(Tab, 4, ValPos2)), + ?match([Rec2, Rec4], lists:sort(R6)), + ?match([], mnesia:dirty_index_read(Tab, {4,14}, ValPos2)), R7 = mnesia:dirty_index_read(Tab, 14, ValPos2), ?match([Rec3, Rec2, Rec4], lists:sort(R7)), ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)), R8 = mnesia:dirty_index_read(Tab, 2, ValPos), ?match([Rec1, Rec2, Rec4], lists:sort(R8)), - ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)), + ?match([Rec1], mnesia:dirty_index_read(Tab, {4,14}, ValPos2)), + ?match([Rec1], mnesia:dirty_match_object(Pat3)), + ?match([Rec1], mnesia:dirty_index_match_object(Pat3, ValPos2)), + R9 = mnesia:dirty_index_read(Tab, 14, ValPos2), ?match([Rec2, Rec4], lists:sort(R9)), ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec2) end)), R10 = mnesia:dirty_index_read(Tab, 2, ValPos), ?match([Rec1, Rec4], lists:sort(R10)), - ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)), + ?match([Rec1], mnesia:dirty_index_read(Tab, {4,14}, ValPos2)), ?match([Rec4], mnesia:dirty_index_read(Tab, 14, ValPos2)), ?match(ok, mnesia:dirty_delete({Tab, 4})), R11 = mnesia:dirty_index_read(Tab, 2, ValPos), ?match([Rec1], lists:sort(R11)), - ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)), + ?match([Rec1], mnesia:dirty_index_read(Tab, {4,14}, ValPos2)), ?match([], mnesia:dirty_index_read(Tab, 14, ValPos2)), ?verify_mnesia(Nodes, []). diff --git a/lib/observer/doc/src/observer_ug.xml b/lib/observer/doc/src/observer_ug.xml index ca354df864..6eb72f3e58 100644 --- a/lib/observer/doc/src/observer_ug.xml +++ b/lib/observer/doc/src/observer_ug.xml @@ -168,7 +168,7 @@ <item><p>The length of the message queue for the process.</p></item> </taglist> - <p>Option <em>Process info</em> opens a detailed information window on the selected process, + <p>Option <em>Process info</em> opens a detailed information window on the process under the mouse pointer, including the following:</p> <taglist> <tag>Process Information</tag> @@ -195,12 +195,53 @@ </p> </note> - <p>Option <em>Trace Processes</em> adds the selected process identifiers to tab + <p>Option <em>Trace selected processes</em> adds the selected process identifiers to tab <em>Trace Overview</em> plus the node that the processes reside on. </p> - <p>Option <em>Trace Named Processes</em> adds the registered name of the processes. This can be + <p>Option <em>Trace selected processes by name</em> adds the registered name of the processes. This can be useful when tracing is done on many nodes, as processes with that name are then traced on all traced nodes.</p> + <p>Option <em>Kill process</em> brutally kills the processes under + the mouse pointer by sending an exit signal with + reason <c>kill</c>.</p> + + </section> + + <section> + <title>Ports Tab</title> + <p>Tab <em>Ports</em> lists port information in columns. + For each port the following information is displayed: + </p> + <taglist> + <tag>Id</tag> + <item><p>The port identifier.</p></item> + <tag>Connected</tag> + <item><p>The process identifier for the process that owns the + port.</p></item> + <tag>Name</tag> + <item><p>The registered name of the port, if any.</p></item> + <tag>Controls</tag> + <item><p>The name of the command set by <seealso marker="erts:erlang#open_port-2"><c>erlang:open_port/2</c></seealso>.</p></item> + <tag>Slot</tag> + <item><p>The internal index of the port.</p></item> + </taglist> + + <p>Option <em>Port info</em> opens a detailed information window + for the port under the mouse pointer. In addition to the + information above, it also shows links and monitors.</p> + + <p>Option <em>Trace selected ports</em> adds the selected port + identifiers, and the nodes that the ports reside on, + to tab <em>Trace Overview</em>.</p> + + <p>Option <em>Trace selected ports by name</em> adds the + registered name of the port to tab <em>Trace Overview</em>. This + can be useful when tracing is done on many nodes, as ports with + that name are then traced on all traced nodes.</p> + + <p>Option <em>Close</em> + executes <seealso marker="erts:erlang#port_close-1"><c>erlang:port_close/1</c></seealso> + on the port under the mouse pointer.</p> </section> @@ -211,8 +252,11 @@ applications are not diplayed. Use menu <em>View</em> to view "system" ETS tables, unreadable ETS tables, or Mnesia tables. </p> - <p>Double-click to view the table content. To view table information, select the table - and activate menu <em>View > Table information</em>.</p> + <p>Double-click to view the table content, or right-click and + select option <em>Show Table Content</em>. To view table + information, select the table and activate menu <em>View > + Table information</em>, or right-click and select option <em>Table + info</em>.</p> <p>You can use <seealso marker="stdlib:re">regular expressions</seealso> and search for objects, and edit or delete them. </p> @@ -220,11 +264,12 @@ <section> <title>Trace Overview Tab</title> - <p>Tab <em>Trace Overview</em> handles tracing. Trace - by selecting the processes to be traced and how to trace - them. You can trace messages, function calls, and events, where - events are process-related events such as <c>spawn</c>, - <c>exit</c>, and many others. + <p>Tab <em>Trace Overview</em> handles tracing. Trace by selecting + the processes or ports to be traced and how to trace them. For + processes, you can trace messages, function calls, scheduling, + garbage collections, and process-related events such + as <c>spawn</c>, <c>exit</c>, and many others. For ports, you can + trace messages, scheduling and port-related events. </p> <p>To trace function calls, you also need to set up @@ -234,27 +279,51 @@ specifications can also be used to trigger more information in the trace messages. </p> - <note><p>Trace patterns only apply to the traced processes.</p></note> + + <p>You can also set match specifications on messages. By default, + if tracing messages, all messages sent and/or received by the + process or port are traced. Match specifications can be used to + reduce the number of traced messages and/or to trigger more + information in the trace messages.</p> + + <note><p>Trace patterns only apply to the traced processes and + ports.</p></note> <p> - Processes are added from the <em>Applications</em> or <em>Processes</em> tabs. - A special <em>new</em> identifier, meaning all processes spawned after trace - start, can be added with button <em>Add 'new' Process</em>. + Processes are added from the <em>Applications</em> + or <em>Processes</em> tabs. Ports are added from + the <em>Ports</em> tab. A special <em>new</em> identifier, + meaning all processes, or ports, started after trace start, can + be added with buttons <em>Add 'new' Processes</em> and <em>Add + 'new' Ports</em>, respecively. </p> <p> - When adding processes, a window with trace options is displayed. The chosen - options are set for the selected processes. - Process options can be changed by right-clicking a process. + When adding processes or ports, a window with trace options is + displayed. The chosen options are set for the selected + processes/ports. To change the options, right-click the process + or port and select <em>Edit process options</em>. To remove a + process or port from the list, right-click and select <em>Remove + process</em> or <em>Remove port</em>, respectively. </p> <p> - Processes added by process identifiers add the nodes these - processes reside on in the node list. More nodes can be added by clicking - button <em>Add Nodes</em>. + Processes and ports added by process/port identifiers add the + nodes these processes/ports reside on in the node list. More + nodes can be added by clicking button <em>Add Nodes</em>, or by + right-clicking in the <em>Nodes</em> list and select <em>Add + Nodes</em>. To remove nodes, select them, then right-click and + choose <em>Remove nodes</em>. </p> <p> If function calls are traced, trace patterns must be added by clicking button <em>Add Trace Pattern</em>. Select a module, function(s), and a match specification. - If no functions are selected, all functions in the module are traced. + If no functions are selected, all functions in the module are traced.</p> + <p> + Trace patterns can also be added for traced messages. Click + button <em>Add Trace Pattern</em> and select <em>Messages + sent</em> or <em>Messages received</em>, and a match + specification. + </p> + <p> A few basic match specifications are provided in the tool, and you can provide your own match specifications. The syntax of match specifications is described in the <seealso diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml index 2b637551db..94ecef24b4 100644 --- a/lib/observer/doc/src/ttb.xml +++ b/lib/observer/doc/src/ttb.xml @@ -229,27 +229,33 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>p(Procs,Flags) -> Return</name> - <fsummary>Set the specified trace flags on the specified processes.</fsummary> + <name>p(Item,Flags) -> Return</name> + <fsummary>Set the specified trace flags on the specified processes or ports.</fsummary> <type> - <v>Return = {ok,[{Procs,MatchDesc}]}</v> - <v>Procs = Process | [Process] | all | new | existing</v> - <v>Process = pid() | atom() | {global,atom()}</v> + <v>Return = {ok,[{Item,MatchDesc}]}</v> + <v>Items = Item | [Item]</v> + <v>Item = pid() | port() | RegName | {global,GlobalRegName} | + all | processes | ports | + existing | existing_processes | existing_ports | + new | new_processes | new_ports</v> + <v>RegName = atom()</v> + <v>GlobalRegName = term()</v> <v>Flags = Flag | [Flag]</v> </type> <desc> - <p>Sets the specified trace flags on the specified - processes. Flag <c>timestamp</c> is always turned on. + <p>Sets the specified trace flags on the specified processes + or ports. Flag <c>timestamp</c> is always turned on. </p> <p>See the Reference Manual for module <seealso marker="runtime_tools:dbg"><c>dbg</c></seealso> - and the possible trace flags. Parameter + for the possible trace flags. Parameter <c>MatchDesc</c> is the same as returned from <c>dbg:p/2</c>.</p> <p>Processes can be specified as registered names, globally - registered names, or process identifiers. If a registered name - is specified, the flags are set on processes with this name on all - active nodes.</p> + registered names, or process identifiers. Ports can be + specified as registered names or port identifiers. If a + registered name is specified, the flags are set on + processes/ports with this name on all active nodes.</p> <p>Issuing this command starts the timer for this trace if option <c>timer</c> is specified with <c>tracer/2</c>. </p> @@ -257,17 +263,23 @@ ttb:p(all, call).</input></pre> </func> <func> - <name>tp, tpl, ctp, ctpl, ctpg</name> + <name>tp, tpl, tpe, ctp, ctpl, ctpg, ctpe</name> <fsummary>Set and clear trace patterns.</fsummary> <desc> - <p>These functions are to be used with - trace flag <c>call</c> for setting and clearing trace - patterns. When trace flag <c>call</c> is set on a process, + <p>These functions are to be used with trace + flag <c>call</c>, <c>send</c>, and <c>'receive'</c> for + setting and clearing trace patterns.</p> + <p>When trace flag <c>call</c> is set on a process, function calls are traced on that process if a trace - pattern is set for the called function. Trace patterns - specify how to trace a function by using match - specifications. Match specifications are described in the - <seealso marker="erts:users_guide"><c>ERTS User's Guide</c></seealso>. + pattern is set for the called function.</p> + <p>The <c>send</c> and <c>'receive'</c> flags enable tracing + of all messages sent and received by the process/port. Trace + patterns set with <c>tpe</c> may limit traced messages based + on the message content, the sender, and/or the receiver.</p> + <p>Trace patterns specify how to trace a function or a message + by using match specifications. Match specifications are + described in the + <seealso marker="erts:match_spec"><c>ERTS User's Guide</c></seealso>. </p> <p>These functions are equivalent to the corresponding functions in module @@ -284,6 +296,8 @@ ttb:p(all, call).</input></pre> <item><p>Sets trace patterns on global function calls.</p></item> <tag><c>tpl</c></tag> <item><p>Sets trace patterns on local and global function calls.</p></item> + <tag><c>tpe</c></tag> + <item><p>Sets trace patterns on messages.</p></item> <tag><c>ctp</c></tag> <item><p>Clears trace patterns on local and global function calls.</p></item> @@ -291,13 +305,15 @@ ttb:p(all, call).</input></pre> <item><p>Clears trace patterns on local function calls.</p></item> <tag><c>ctpg</c></tag> <item><p>Clears trace patterns on global function calls.</p></item> + <tag><c>ctpe</c></tag> + <item><p>Clears trace patterns on messages.</p></item> </taglist> <p>With <c>tp</c> and <c>tpl</c>, one of the match specification shortcuts can be used (for example, <c>ttb:tp(foo_module, caller)</c>).</p> <p>The shortcuts are as follows:</p> <list type="bulleted"> <item><c>return</c> - for <c>[{'_',[],[{return_trace}]}]</c> - (report the return value)</item> + (report the return value from a traced function)</item> <item><c>caller</c> - for <c>[{'_',[],[{message,{caller}}]}]</c> (report the calling function)</item> <item><c>{codestr, Str}</c> - for <c>dbg:fun2ms/1</c> arguments diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile index 85dc5933c1..dd7831fa2b 100644 --- a/lib/observer/src/Makefile +++ b/lib/observer/src/Makefile @@ -67,6 +67,7 @@ MODULES= \ observer_html_lib \ observer_lib \ observer_perf_wx \ + observer_port_wx \ observer_pro_wx \ observer_procinfo \ observer_sys_wx \ diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src index 5ddf65fa59..3a5bd172e7 100644 --- a/lib/observer/src/observer.app.src +++ b/lib/observer/src/observer.app.src @@ -51,6 +51,7 @@ observer_html_lib, observer_lib, observer_perf_wx, + observer_port_wx, observer_pro_wx, observer_procinfo, observer_sys_wx, diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index cef83037d0..936b2783e2 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -221,21 +221,21 @@ handle_event(#wx{id=?ID_PROC_KILL, event=#wxCommand{type=command_menu_selected}} %%% Trace api handle_event(#wx{id=?ID_TRACE_PID, event=#wxCommand{type=command_menu_selected}}, State = #state{sel={Box,_}}) -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), [box_to_pid(Box)]), + observer_trace_wx:add_processes([box_to_pid(Box)]), {noreply, State}; handle_event(#wx{id=?ID_TRACE_NAME, event=#wxCommand{type=command_menu_selected}}, State = #state{sel={Box,_}}) -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), [box_to_reg(Box)]), + observer_trace_wx:add_processes([box_to_reg(Box)]), {noreply, State}; handle_event(#wx{id=?ID_TRACE_TREE_PIDS, event=#wxCommand{type=command_menu_selected}}, State = #state{sel=Sel}) -> Get = fun(Box) -> box_to_pid(Box) end, - observer_trace_wx:add_processes(observer_wx:get_tracer(), tree_map(Sel, Get)), + observer_trace_wx:add_processes(tree_map(Sel, Get)), {noreply, State}; handle_event(#wx{id=?ID_TRACE_TREE_NAMES, event=#wxCommand{type=command_menu_selected}}, State = #state{sel=Sel}) -> Get = fun(Box) -> box_to_reg(Box) end, - observer_trace_wx:add_processes(observer_wx:get_tracer(), tree_map(Sel, Get)), + observer_trace_wx:add_processes(tree_map(Sel, Get)), {noreply, State}; handle_event(Event, _State) -> diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl new file mode 100644 index 0000000000..3b788642cc --- /dev/null +++ b/lib/observer/src/observer_port_wx.erl @@ -0,0 +1,479 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-2014. 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(observer_port_wx). + +-export([start_link/2]). + +%% wx_object callbacks +-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, + handle_event/2, handle_sync_event/3, handle_cast/2]). + +-behaviour(wx_object). +-include_lib("wx/include/wx.hrl"). +-include("observer_defs.hrl"). + +-define(GRID, 300). +-define(ID_REFRESH, 301). +-define(ID_REFRESH_INTERVAL, 302). +-define(ID_PORT_INFO, 303). +-define(ID_PORT_INFO_SELECTED, 304). +-define(ID_TRACE_PORTS, 305). +-define(ID_TRACE_NAMES, 306). +-define(ID_TRACE_NEW, 307). +-define(ID_TRACE_ALL, 308). +-define(ID_CLOSE_PORT, 309). + +-define(TRACE_PORTS_STR, "Trace selected ports"). +-define(TRACE_NAMES_STR, "Trace selected ports, " + "if a process have a registered name " + "processes with same name will be traced on all nodes"). + +-record(port, + {id, + connected, + name, + controls, + slot, + id_str, + links, + monitors}). + +-record(opt, {sort_key=2, + sort_incr=true + }). + +-record(state, + { + parent, + grid, + panel, + node=node(), + opt=#opt{}, + right_clicked_port, + ports, + timer, + open_wins=[] + }). + +start_link(Notebook, Parent) -> + wx_object:start_link(?MODULE, [Notebook, Parent], []). + +init([Notebook, Parent]) -> + Panel = wxPanel:new(Notebook), + Sizer = wxBoxSizer:new(?wxVERTICAL), + Style = ?wxLC_REPORT bor ?wxLC_HRULES, + Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style}]), + wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL}, + {proportion, 1}, {border, 5}]), + wxWindow:setSizer(Panel, Sizer), + Li = wxListItem:new(), + AddListEntry = fun({Name, Align, DefSize}, Col) -> + wxListItem:setText(Li, Name), + wxListItem:setAlign(Li, Align), + wxListCtrl:insertColumn(Grid, Col, Li), + wxListCtrl:setColumnWidth(Grid, Col, DefSize), + Col + 1 + end, + ListItems = [{"Id", ?wxLIST_FORMAT_LEFT, 150}, + {"Connected", ?wxLIST_FORMAT_LEFT, 150}, + {"Name", ?wxLIST_FORMAT_LEFT, 150}, + {"Controls", ?wxLIST_FORMAT_LEFT, 200}, + {"Slot", ?wxLIST_FORMAT_RIGHT, 50}], + lists:foldl(AddListEntry, 0, ListItems), + wxListItem:destroy(Li), + + wxListCtrl:connect(Grid, command_list_item_right_click), + wxListCtrl:connect(Grid, command_list_item_activated), + wxListCtrl:connect(Grid, command_list_col_click), + wxListCtrl:connect(Grid, size, [{skip, true}]), + + wxWindow:setFocus(Grid), + {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer={false, 10}}}. + +handle_event(#wx{id=?ID_REFRESH}, + State = #state{node=Node, grid=Grid, opt=Opt}) -> + Ports0 = get_ports(Node), + Ports = update_grid(Grid, Opt, Ports0), + {noreply, State#state{ports=Ports}}; + +handle_event(#wx{obj=Obj, event=#wxClose{}}, #state{open_wins=Opened} = State) -> + NewOpened = + case lists:keytake(Obj,2,Opened) of + false -> Opened; + {value,_,Rest} -> Rest + end, + {noreply, State#state{open_wins=NewOpened}}; + +handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}}, + State = #state{node=Node, grid=Grid, + opt=Opt0=#opt{sort_key=Key, sort_incr=Bool}}) -> + Opt = case Col+2 of + Key -> Opt0#opt{sort_incr=not Bool}; + NewKey -> Opt0#opt{sort_key=NewKey} + end, + Ports0 = get_ports(Node), + Ports = update_grid(Grid, Opt, Ports0), + wxWindow:setFocus(Grid), + {noreply, State#state{opt=Opt, ports=Ports}}; + +handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) -> + observer_lib:set_listctrl_col_size(Grid, W), + {noreply, State}; + +handle_event(#wx{event=#wxList{type=command_list_item_activated, + itemIndex=Index}}, + State=#state{grid=Grid, ports=Ports, open_wins=Opened}) -> + Port = lists:nth(Index+1, Ports), + NewOpened = display_port_info(Grid, Port, Opened), + {noreply, State#state{open_wins=NewOpened}}; + +handle_event(#wx{event=#wxList{type=command_list_item_right_click, + itemIndex=Index}}, + State=#state{panel=Panel, ports=Ports}) -> + case Index of + -1 -> + {noreply, State}; + _ -> + Port = lists:nth(Index+1, Ports), + Menu = wxMenu:new(), + wxMenu:append(Menu, ?ID_PORT_INFO, + "Port info for " ++ erlang:port_to_list(Port#port.id)), + wxMenu:append(Menu, ?ID_TRACE_PORTS, + "Trace selected ports", + [{help, ?TRACE_PORTS_STR}]), + wxMenu:append(Menu, ?ID_TRACE_NAMES, + "Trace selected ports by name (all nodes)", + [{help, ?TRACE_NAMES_STR}]), + wxMenu:append(Menu, ?ID_CLOSE_PORT, + "Close " ++ erlang:port_to_list(Port#port.id)), + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu), + {noreply, State#state{right_clicked_port=Port}} + end; + +handle_event(#wx{id=?ID_PORT_INFO}, + State = #state{grid=Grid, right_clicked_port=Port, + open_wins=Opened}) -> + case Port of + undefined -> + {noreply, State}; + _ -> + NewOpened = display_port_info(Grid, Port, Opened), + {noreply, State#state{right_clicked_port=undefined, + open_wins=NewOpened}} + end; + +handle_event(#wx{id=?ID_PORT_INFO_SELECTED}, + State = #state{grid=Grid, ports=Ports, open_wins=Opened}) -> + case get_selected_items(Grid,Ports) of + [] -> + observer_wx:create_txt_dialog(State#state.panel, "No selected ports", + "Port Info", ?wxICON_EXCLAMATION), + {noreply, State}; + Selected -> + NewOpened = lists:foldl(fun(P,O) -> display_port_info(Grid, P, O) end, + Opened, Selected), + {noreply, State#state{open_wins = NewOpened}} + end; + +handle_event(#wx{id=?ID_CLOSE_PORT}, State = #state{right_clicked_port=Port}) -> + case Port of + undefined -> + {noreply, State}; + _ -> + erlang:port_close(Port#port.id), + {noreply, State#state{right_clicked_port=undefined}} + end; + +handle_event(#wx{id=?ID_TRACE_PORTS}, #state{grid=Grid, ports=Ports}=State) -> + case get_selected_items(Grid, Ports) of + [] -> + observer_wx:create_txt_dialog(State#state.panel, "No selected ports", + "Tracer", ?wxICON_EXCLAMATION); + Selected -> + SelectedIds = [Port#port.id || Port <- Selected], + observer_trace_wx:add_ports(SelectedIds) + end, + {noreply, State}; + +handle_event(#wx{id=?ID_TRACE_NAMES}, #state{grid=Grid, ports=Ports}=State) -> + case get_selected_items(Grid, Ports) of + [] -> + observer_wx:create_txt_dialog(State#state.panel, "No selected ports", + "Tracer", ?wxICON_EXCLAMATION); + Selected -> + IdsOrRegs = + [case Port#port.name of + [] -> Port#port.id; + Name -> Name + end || Port <- Selected], + observer_trace_wx:add_ports(IdsOrRegs) + end, + {noreply, State}; + +handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) -> + observer_trace_wx:add_ports([new_ports]), + {noreply, State}; + +handle_event(#wx{id=?ID_REFRESH_INTERVAL}, + State = #state{grid=Grid, timer=Timer0}) -> + Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60), + {noreply, State#state{timer=Timer}}; + +handle_event(#wx{event=#wxMouse{type=left_down}, userData=TargetPid}, State) -> + observer ! {open_link, TargetPid}, + {noreply, State}; + +handle_event(#wx{obj=Obj, event=#wxMouse{type=enter_window}}, State) -> + wxTextCtrl:setForegroundColour(Obj,{0,0,100,255}), + {noreply, State}; + +handle_event(#wx{obj=Obj, event=#wxMouse{type=leave_window}}, State) -> + wxTextCtrl:setForegroundColour(Obj,?wxBLUE), + {noreply, State}; + +handle_event(Event, _State) -> + error({unhandled_event, Event}). + +handle_sync_event(_Event, _Obj, _State) -> + ok. + +handle_call(Event, From, _State) -> + error({unhandled_call, Event, From}). + +handle_cast(Event, _State) -> + error({unhandled_cast, Event}). + +handle_info({portinfo_open, PortIdStr}, + State = #state{grid=Grid, ports=Ports, open_wins=Opened}) -> + Port = lists:keyfind(PortIdStr,#port.id_str,Ports), + NewOpened = display_port_info(Grid, Port, Opened), + {noreply, State#state{open_wins = NewOpened}}; + +handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt, + ports=OldPorts}) -> + case get_ports(Node) of + OldPorts -> + %% no change + {noreply, State}; + Ports0 -> + Ports = update_grid(Grid, Opt, Ports0), + {noreply, State#state{ports=Ports}} + end; + +handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt, + timer=Timer0}) -> + Ports0 = get_ports(Node), + Ports = update_grid(Grid, Opt, Ports0), + wxWindow:setFocus(Grid), + create_menus(Parent), + Timer = observer_lib:start_timer(Timer0), + {noreply, State#state{node=Node, ports=Ports, timer=Timer}}; + +handle_info(not_active, State = #state{timer = Timer0}) -> + Timer = observer_lib:stop_timer(Timer0), + {noreply, State#state{timer=Timer}}; + +handle_info({error, Error}, State) -> + handle_error(Error), + {noreply, State}; + +handle_info(_Event, State) -> + {noreply, State}. + +terminate(_Event, _State) -> + ok. + +code_change(_, _, State) -> + State. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +create_menus(Parent) -> + MenuEntries = + [{"View", + [#create_menu{id = ?ID_PORT_INFO_SELECTED, + text = "Port info for selected ports\tCtrl-I"}, + separator, + #create_menu{id = ?ID_REFRESH, text = "Refresh\tCtrl-R"}, + #create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh Interval..."} + ]}, + {"Trace", + [#create_menu{id=?ID_TRACE_PORTS, text="Trace selected ports"}, + #create_menu{id=?ID_TRACE_NAMES, text="Trace selected ports by name (all nodes)"}, + #create_menu{id=?ID_TRACE_NEW, text="Trace new ports"} + ]} + ], + observer_wx:create_menus(Parent, MenuEntries). + +get_ports(Node) -> + case get_ports2(Node) of + Error = {error, _} -> + self() ! Error, + []; + Res -> + Res + end. +get_ports2(Node) -> + case rpc:call(Node, observer_backend, get_port_list, []) of + {badrpc, Error} -> + {error, Error}; + Error = {error, _} -> + Error; + Result -> + [list_to_portrec(Port) || Port <- Result] + end. + +list_to_portrec(PL) -> + %% PortInfo: + %% {registered_name, RegisteredName :: atom()} | + %% {id, Index :: integer() >= 0} | + %% {connected, Pid :: pid()} | + %% {links, Pids :: [pid()]} | + %% {name, String :: string()} | + %% {input, Bytes :: integer() >= 0} | + %% {output, Bytes :: integer() >= 0} | + %% {os_pid, OsPid :: integer() >= 0 | undefined}, + PortId = proplists:get_value(port_id, PL), + #port{id = PortId, + id_str = erlang:port_to_list(PortId), + slot = proplists:get_value(id, PL), + connected = proplists:get_value(connected, PL), + links = proplists:get_value(links, PL, []), + name = proplists:get_value(registered_name, PL, []), + monitors = proplists:get_value(monitors, PL, []), + controls = proplists:get_value(name, PL)}. + +portrec_to_list(#port{id = Id, + slot = Slot, + connected = Connected, + links = Links, + name = Name, + monitors = Monitors, + controls = Controls}) -> + [{id,Id}, + {slot,Slot}, + {connected,Connected}, + {links,Links}, + {name,Name}, + {monitors,Monitors}, + {controls,Controls}]. + +display_port_info(Parent, PortRec, Opened) -> + PortIdStr = PortRec#port.id_str, + case lists:keyfind(PortIdStr,1,Opened) of + false -> + Frame = do_display_port_info(Parent, PortRec), + [{PortIdStr,Frame}|Opened]; + {_,Win} -> + wxFrame:raise(Win), + Opened + end. + +do_display_port_info(Parent0, PortRec) -> + Parent = observer_lib:get_wx_parent(Parent0), + Title = "Port Info: " ++ PortRec#port.id_str, + Frame = wxMiniFrame:new(Parent, ?wxID_ANY, Title, + [{style, ?wxSYSTEM_MENU bor ?wxCAPTION + bor ?wxCLOSE_BOX bor ?wxRESIZE_BORDER}]), + + Port = portrec_to_list(PortRec), + Fields0 = port_info_fields(Port), + {_FPanel, _Sizer, _UpFields} = observer_lib:display_info(Frame, Fields0), + wxFrame:center(Frame), + wxFrame:connect(Frame, close_window, [{skip, true}]), + wxFrame:show(Frame), + Frame. + + +port_info_fields(Port) -> + Struct = + [{"Overview", + [{"Name", name}, + {"Connected", {click,connected}}, + {"Slot", slot}, + {"Controls", controls}]}, + {scroll_boxes, + [{"Links",1,{click,links}}, + {"Monitors",1,{click,filter_monitor_info()}}]}], + observer_lib:fill_info(Struct, Port). + +filter_monitor_info() -> + fun(Data) -> + Ms = proplists:get_value(monitors, Data), + [Pid || {process, Pid} <- Ms] + end. + + +handle_error(Foo) -> + Str = io_lib:format("ERROR: ~s~n",[Foo]), + observer_lib:display_info_dialog(Str). + +update_grid(Grid, Opt, Ports) -> + wx:batch(fun() -> update_grid2(Grid, Opt, Ports) end). +update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) -> + wxListCtrl:deleteAllItems(Grid), + Update = + fun(#port{id = Id, + slot = Slot, + connected = Connected, + name = Name, + controls = Ctrl}, + Row) -> + _Item = wxListCtrl:insertItem(Grid, Row, ""), + if (Row rem 2) =:= 0 -> + wxListCtrl:setItemBackgroundColour(Grid, Row, ?BG_EVEN); + true -> ignore + end, + + lists:foreach(fun({Col, Val}) -> + wxListCtrl:setItem(Grid, Row, Col, + observer_lib:to_str(Val)) + end, + [{0,Id},{1,Connected},{2,Name},{3,Ctrl},{4,Slot}]), + Row + 1 + end, + PortInfo = case Dir of + false -> lists:reverse(lists:keysort(Sort, Ports)); + true -> lists:keysort(Sort, Ports) + end, + lists:foldl(Update, 0, PortInfo), + PortInfo. + + +get_selected_items(Grid, Data) -> + get_indecies(get_selected_items(Grid, -1, []), Data). +get_selected_items(Grid, Index, ItemAcc) -> + Item = wxListCtrl:getNextItem(Grid, Index, [{geometry, ?wxLIST_NEXT_ALL}, + {state, ?wxLIST_STATE_SELECTED}]), + case Item of + -1 -> + lists:reverse(ItemAcc); + _ -> + get_selected_items(Grid, Item, [Item | ItemAcc]) + end. + +get_indecies(Items, Data) -> + get_indecies(Items, 0, Data). +get_indecies([I|Rest], I, [H|T]) -> + [H|get_indecies(Rest, I+1, T)]; +get_indecies(Rest = [_|_], I, [_|T]) -> + get_indecies(Rest, I+1, T); +get_indecies(_, _, _) -> + []. diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index bd914cdf65..ee6829b847 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -83,6 +83,7 @@ timer, procinfo_menu_pids=[], sel={[], []}, + right_clicked_pid, holder}). start_link(Notebook, Parent) -> @@ -303,13 +304,14 @@ handle_event(#wx{id=?ID_REFRESH_INTERVAL}, Timer = observer_lib:interval_dialog(Panel, Timer0, 1, 5*60), {noreply, State#state{timer=Timer}}; -handle_event(#wx{id=?ID_KILL}, #state{sel={[_|Ids], [ToKill|Pids]}}=State) -> - exit(ToKill, kill), - {noreply, State#state{sel={Ids,Pids}}}; +handle_event(#wx{id=?ID_KILL}, #state{right_clicked_pid=Pid, sel=Sel0}=State) -> + exit(Pid, kill), + Sel = rm_selected(Pid,Sel0), + {noreply, State#state{sel=Sel}}; handle_event(#wx{id=?ID_PROC}, - #state{panel=Panel, sel={_, [Pid|_]},procinfo_menu_pids=Opened}=State) -> + #state{panel=Panel, right_clicked_pid=Pid, procinfo_menu_pids=Opened}=State) -> Opened2 = start_procinfo(Pid, Panel, Opened), {noreply, State#state{procinfo_menu_pids=Opened2}}; @@ -319,7 +321,7 @@ handle_event(#wx{id=?ID_TRACE_PIDS}, #state{sel={_, Pids}, panel=Panel}=State) observer_wx:create_txt_dialog(Panel, "No selected processes", "Tracer", ?wxICON_EXCLAMATION), {noreply, State}; Pids -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), Pids), + observer_trace_wx:add_processes(Pids), {noreply, State} end; @@ -330,12 +332,12 @@ handle_event(#wx{id=?ID_TRACE_NAMES}, #state{sel={SelIds,_Pids}, holder=Holder, {noreply, State}; _ -> PidsOrReg = call(Holder, {get_name_or_pid, self(), SelIds}), - observer_trace_wx:add_processes(observer_wx:get_tracer(), PidsOrReg), + observer_trace_wx:add_processes(PidsOrReg), {noreply, State} end; handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), [new]), + observer_trace_wx:add_processes([new_processes]), {noreply, State}; handle_event(#wx{event=#wxSize{size={W,_}}}, @@ -347,20 +349,26 @@ handle_event(#wx{event=#wxList{type=command_list_item_right_click, itemIndex=Row}}, #state{panel=Panel, holder=Holder}=State) -> - case call(Holder, {get_row, self(), Row, pid}) of - {error, undefined} -> - undefined; - {ok, _} -> - Menu = wxMenu:new(), - wxMenu:append(Menu, ?ID_PROC, "Process info"), - wxMenu:append(Menu, ?ID_TRACE_PIDS, "Trace processes", [{help, ?TRACE_PIDS_STR}]), - wxMenu:append(Menu, ?ID_TRACE_NAMES, "Trace named processes (all nodes)", - [{help, ?TRACE_NAMES_STR}]), - wxMenu:append(Menu, ?ID_KILL, "Kill Process"), - wxWindow:popupMenu(Panel, Menu), - wxMenu:destroy(Menu) - end, - {noreply, State}; + Pid = + case call(Holder, {get_row, self(), Row, pid}) of + {error, undefined} -> + undefined; + {ok, P} -> + Menu = wxMenu:new(), + wxMenu:append(Menu, ?ID_PROC, + "Process info for " ++ pid_to_list(P)), + wxMenu:append(Menu, ?ID_TRACE_PIDS, + "Trace selected processes", + [{help, ?TRACE_PIDS_STR}]), + wxMenu:append(Menu, ?ID_TRACE_NAMES, + "Trace selected processes by name (all nodes)", + [{help, ?TRACE_NAMES_STR}]), + wxMenu:append(Menu, ?ID_KILL, "Kill process " ++ pid_to_list(P)), + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu), + P + end, + {noreply, State#state{right_clicked_pid=Pid}}; handle_event(#wx{event=#wxList{type=command_list_item_focused, itemIndex=Row}}, @@ -432,6 +440,17 @@ set_focus([Old|_], [New|_], Grid) -> wxListCtrl:setItemState(Grid, Old, 0, ?wxLIST_STATE_FOCUSED), wxListCtrl:setItemState(Grid, New, 16#FFFF, ?wxLIST_STATE_FOCUSED). +rm_selected(Pid, {Ids, Pids}) -> + rm_selected(Pid, Ids, Pids, [], []). + +rm_selected(Pid, [_Id|Ids], [Pid|Pids], AccIds, AccPids) -> + {lists:reverse(AccIds)++Ids,lists:reverse(AccPids)++Pids}; +rm_selected(Pid, [Id|Ids], [OtherPid|Pids], AccIds, AccPids) -> + rm_selected(Pid, Ids, Pids, [Id|AccIds], [OtherPid|AccPids]); +rm_selected(_, [], [], AccIds, AccPids) -> + {lists:reverse(AccIds), lists:reverse(AccPids)}. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_table_holder(Parent, Attrs) -> diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index 9c0243e4a7..af90e2100c 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -19,7 +19,7 @@ -module(observer_trace_wx). --export([start_link/2, add_processes/2]). +-export([start_link/2, add_processes/1, add_ports/1]). -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_cast/2]). @@ -31,11 +31,15 @@ -define(SAVE_TRACEOPTS, 305). -define(LOAD_TRACEOPTS, 306). -define(TOGGLE_TRACE, 307). --define(ADD_NEW, 308). --define(ADD_TP, 309). --define(TRACE_OUTPUT, 310). --define(TRACE_DEFMS, 311). --define(TRACE_DEFPS, 312). +-define(ADD_NEW_PROCS, 308). +-define(ADD_NEW_PORTS, 309). +-define(ADD_TP, 310). +-define(TRACE_OUTPUT, 311). +-define(DEF_MS_FUNCS, 312). +-define(DEF_MS_SEND, 313). +-define(DEF_MS_RECV, 314). +-define(DEF_PROC_OPTS, 315). +-define(DEF_PORT_OPTS, 316). -define(NODES_WIN, 330). -define(ADD_NODES, 331). @@ -45,36 +49,53 @@ -define(EDIT_PROCS, 341). -define(REMOVE_PROCS, 342). --define(MODULES_WIN, 350). +-define(PORT_WIN, 350). +-define(EDIT_PORTS, 351). +-define(REMOVE_PORTS, 352). --define(FUNCS_WIN, 360). --define(EDIT_FUNCS_MS, 361). --define(REMOVE_FUNCS_MS, 362). +-define(MODULES_WIN, 360). +-define(REMOVE_MOD_MS, 361). --define(LOG_WIN, 370). --define(LOG_SAVE, 321). --define(LOG_CLEAR, 322). +-define(FUNCS_WIN, 370). +-define(EDIT_FUNCS_MS, 371). +-define(REMOVE_FUNCS_MS, 372). + +-define(LOG_WIN, 380). +-define(LOG_SAVE, 381). +-define(LOG_CLEAR, 382). + +-define(NO_NODES_HELP,"Right click to add nodes"). +-define(NODES_HELP,"Select nodes to see traced processes and ports"). +-define(NO_P_HELP,"Add items from Processes/Ports tab"). +-define(P_HELP,"Select nodes to see traced processes and ports"). +-define(NO_TP_HELP,"Add trace pattern with button below"). +-define(TP_HELP,"Select module to see trace patterns"). -record(state, {parent, panel, - n_view, p_view, m_view, f_view, %% The listCtrl's + n_view, proc_view, port_view, m_view, f_view, %% The listCtrl's logwin, %% The latest log window nodes = [], toggle_button, - tpids = [], %% #tpid - def_trace_opts = [], + tpids = [], % #titem + tports = [], % #titem + def_proc_flags = [], + def_port_flags = [], output = [], tpatterns = dict:new(), % Key =:= Module::atom, Value =:= {M, F, A, MatchSpec} match_specs = []}). % [ #match_spec{} ] --record(tpid, {pid, opts}). +-record(titem, {id, opts}). start_link(Notebook, ParentPid) -> wx_object:start_link(?MODULE, [Notebook, ParentPid], []). -add_processes(Tracer, Pids) when is_list(Pids) -> - wx_object:cast(Tracer, {add_processes, Pids}). +add_processes(Pids) when is_list(Pids) -> + wx_object:cast(observer_wx:get_tracer(), {add_processes, Pids}). + +add_ports(Ports) when is_list(Ports) -> + wx_object:cast(observer_wx:get_tracer(), {add_ports, Ports}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -87,11 +108,13 @@ create_window(Notebook, ParentPid) -> Sizer = wxBoxSizer:new(?wxVERTICAL), Splitter = wxSplitterWindow:new(Panel, [{size, wxWindow:getClientSize(Panel)}, {style, ?SASH_STYLE}]), - {NodeProcView, NodeView, ProcessView} = create_process_view(Splitter), + {NodeProcView, NodeView, ProcessView, PortView} = + create_proc_port_view(Splitter), {MatchSpecView,ModView,FuncView} = create_matchspec_view(Splitter), wxSplitterWindow:setSashGravity(Splitter, 0.5), wxSplitterWindow:setMinimumPaneSize(Splitter,50), - wxSplitterWindow:splitHorizontally(Splitter, NodeProcView, MatchSpecView), + wxSplitterWindow:splitHorizontally(Splitter, NodeProcView, MatchSpecView, + [{sashPosition,368}]), wxSizer:add(Sizer, Splitter, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}, {proportion, 1}]), %% Buttons Buttons = wxBoxSizer:new(?wxHORIZONTAL), @@ -99,7 +122,8 @@ create_window(Notebook, ParentPid) -> wxSizer:add(Buttons, ToggleButton, [{flag, ?wxALIGN_CENTER_VERTICAL}]), wxSizer:addSpacer(Buttons, 15), wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NODES, [{label, "Add Nodes"}])), - wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW, [{label, "Add 'new' Process"}])), + wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW_PROCS, [{label, "Add 'new' Processes"}])), + wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW_PORTS, [{label, "Add 'new' Ports"}])), wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_TP, [{label, "Add Trace Pattern"}])), wxMenu:connect(Panel, command_togglebutton_clicked, [{skip, true}]), wxMenu:connect(Panel, command_button_clicked, [{skip, true}]), @@ -107,24 +131,47 @@ create_window(Notebook, ParentPid) -> {border, 5}, {proportion,0}]), wxWindow:setSizer(Panel, Sizer), {Panel, #state{parent=ParentPid, panel=Panel, - n_view=NodeView, p_view=ProcessView, m_view=ModView, f_view=FuncView, + n_view=NodeView, proc_view=ProcessView, port_view=PortView, + m_view=ModView, f_view=FuncView, toggle_button = ToggleButton, match_specs=default_matchspecs()}}. default_matchspecs() -> - Ms = [{"Return Trace", [{'_', [], [{return_trace}]}], "fun(_) -> return_trace() end"}, - {"Exception Trace", [{'_', [], [{exception_trace}]}], "fun(_) -> exception_trace() end"}, - {"Message Caller", [{'_', [], [{message,{caller}}]}], "fun(_) -> message(caller()) end"}, - {"Message Dump", [{'_', [], [{message,{process_dump}}]}], "fun(_) -> message(process_dump()) end"}], + [{Key,default_matchspecs(Key)} || Key <- [funcs,send,'receive']]. +default_matchspecs(Key) -> + Ms = get_default_matchspecs(Key), [make_ms(Name,Term,FunStr) || {Name,Term,FunStr} <- Ms]. -create_process_view(Parent) -> +get_default_matchspecs(funcs) -> + [{"Return Trace", [{'_', [], [{return_trace}]}], + "fun(_) -> return_trace() end"}, + {"Exception Trace", [{'_', [], [{exception_trace}]}], + "fun(_) -> exception_trace() end"}, + {"Message Caller", [{'_', [], [{message,{caller}}]}], + "fun(_) -> message(caller()) end"}, + {"Message Dump", [{'_', [], [{message,{process_dump}}]}], + "fun(_) -> message(process_dump()) end"}]; +get_default_matchspecs(send) -> + [{"To local node", [{['$1','_'], [{'==',{node,'$1'},{node}}], []}], + "fun([Pid,_]) when node(Pid)==node() ->\n true\nend"}, + {"To remote node", [{['$1','_'], [{'=/=',{node,'$1'},{node}}], []}], + "fun([Pid,_]) when node(Pid)=/=node() ->\n true\nend"}]; +get_default_matchspecs('receive') -> + [{"From local node", [{['$1','_','_'], [{'==','$1',{node}}], []}], + "fun([Node,_,_]) when Node==node() ->\n true\nend"}, + {"From remote node", [{['$1','_','_'], [{'=/=','$1',{node}}], []}], + "fun([Node,_,_]) when Node=/=node() ->\n true\nend"}]. + + +create_proc_port_view(Parent) -> Panel = wxPanel:new(Parent), MainSz = wxBoxSizer:new(?wxHORIZONTAL), Style = ?wxLC_REPORT bor ?wxLC_HRULES, Splitter = wxSplitterWindow:new(Panel, [{style, ?SASH_STYLE}]), Nodes = wxListCtrl:new(Splitter, [{winid, ?NODES_WIN}, {style, Style}]), - Procs = wxListCtrl:new(Splitter, [{winid, ?PROC_WIN}, {style, Style}]), + ProcsPortsSplitter = wxSplitterWindow:new(Splitter, [{style, ?SASH_STYLE}]), + Procs = wxListCtrl:new(ProcsPortsSplitter, [{winid,?PROC_WIN},{style,Style}]), + Ports = wxListCtrl:new(ProcsPortsSplitter, [{winid,?PORT_WIN},{style,Style}]), Li = wxListItem:new(), wxListItem:setText(Li, "Nodes"), wxListCtrl:insertColumn(Nodes, 0, Li), @@ -136,31 +183,57 @@ create_process_view(Parent) -> wxListCtrl:setColumnWidth(Procs, Col, DefSize), Col + 1 end, - ListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120}, - {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}], - lists:foldl(AddProc, 0, ListItems), + ProcListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120}, + {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}], + lists:foldl(AddProc, 0, ProcListItems), + + AddPort = fun({Name, Align, DefSize}, Col) -> + wxListItem:setText(Li, Name), + wxListItem:setAlign(Li, Align), + wxListCtrl:insertColumn(Ports, Col, Li), + wxListCtrl:setColumnWidth(Ports, Col, DefSize), + Col + 1 + end, + PortListItems = [{"Port Id", ?wxLIST_FORMAT_CENTER, 120}, + {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}], + lists:foldl(AddPort, 0, PortListItems), + wxListItem:destroy(Li), wxSplitterWindow:setSashGravity(Splitter, 0.0), wxSplitterWindow:setMinimumPaneSize(Splitter,50), - wxSplitterWindow:splitVertically(Splitter, Nodes, Procs, [{sashPosition, 155}]), + wxSplitterWindow:splitVertically(Splitter, Nodes, ProcsPortsSplitter, + [{sashPosition, 155}]), wxSizer:add(MainSz, Splitter, [{flag, ?wxEXPAND}, {proportion, 1}]), + wxSplitterWindow:setSashGravity(ProcsPortsSplitter, 0.5), + wxSplitterWindow:setMinimumPaneSize(ProcsPortsSplitter,50), + wxSplitterWindow:splitHorizontally(ProcsPortsSplitter, Procs, Ports, + [{sashPosition, 182}]), + wxListCtrl:connect(Procs, command_list_item_right_click), + wxListCtrl:connect(Ports, command_list_item_right_click), wxListCtrl:connect(Nodes, command_list_item_right_click), + wxListCtrl:connect(Nodes, command_list_item_selected), wxListCtrl:connect(Procs, size, [{skip, true}]), + wxListCtrl:connect(Ports, size, [{skip, true}]), wxListCtrl:connect(Nodes, size, [{skip, true}]), + wxListCtrl:setToolTip(Nodes, ?NO_NODES_HELP), + wxListCtrl:setToolTip(Procs, ?NO_P_HELP), + wxListCtrl:setToolTip(Ports, ?NO_P_HELP), + wxPanel:setSizer(Panel, MainSz), wxWindow:setFocus(Procs), - {Panel, Nodes, Procs}. + {Panel, Nodes, Procs, Ports}. create_matchspec_view(Parent) -> Panel = wxPanel:new(Parent), MainSz = wxBoxSizer:new(?wxHORIZONTAL), Style = ?wxLC_REPORT bor ?wxLC_HRULES, Splitter = wxSplitterWindow:new(Panel, [{style, ?SASH_STYLE}]), - Modules = wxListCtrl:new(Splitter, [{winid, ?MODULES_WIN}, {style, Style}]), + Modules = wxListCtrl:new(Splitter, [{winid, ?MODULES_WIN}, + {style, Style bor ?wxLC_SINGLE_SEL}]), Funcs = wxListCtrl:new(Splitter, [{winid, ?FUNCS_WIN}, {style, Style}]), Li = wxListItem:new(), @@ -182,7 +255,9 @@ create_matchspec_view(Parent) -> wxListCtrl:connect(Modules, size, [{skip, true}]), wxListCtrl:connect(Funcs, size, [{skip, true}]), wxListCtrl:connect(Modules, command_list_item_selected), + wxListCtrl:connect(Modules, command_list_item_right_click), wxListCtrl:connect(Funcs, command_list_item_right_click), + wxListCtrl:setToolTip(Panel, ?NO_TP_HELP), wxPanel:setSizer(Panel, MainSz), {Panel, Modules, Funcs}. @@ -192,8 +267,11 @@ create_menues(Parent) -> #create_menu{id = ?SAVE_TRACEOPTS, text = "Save settings"}]}, {"Options", [#create_menu{id = ?TRACE_OUTPUT, text = "Output"}, - #create_menu{id = ?TRACE_DEFMS, text = "Match Specifications"}, - #create_menu{id = ?TRACE_DEFPS, text = "Default Process Options"}]} + #create_menu{id = ?DEF_MS_FUNCS, text = "Default Match Specifications for Functions"}, + #create_menu{id = ?DEF_MS_SEND, text = "Default Match Specifications for 'send'"}, + #create_menu{id = ?DEF_MS_RECV, text = "Default Match Specifications for 'receive'"}, + #create_menu{id = ?DEF_PROC_OPTS, text = "Default Process Options"}, + #create_menu{id = ?DEF_PORT_OPTS, text = "Default Port Options"}]} ], observer_wx:create_menus(Parent, Menus). @@ -206,11 +284,19 @@ handle_event(#wx{obj=Obj, event=#wxSize{size={W,_}}}, State) -> end, {noreply, State}; -handle_event(#wx{id=?ADD_NEW}, State = #state{panel=Parent, def_trace_opts=TraceOpts}) -> +handle_event(#wx{id=?ADD_NEW_PROCS}, State = #state{panel=Parent, def_proc_flags=TraceOpts}) -> try Opts = observer_traceoptions_wx:process_trace(Parent, TraceOpts), - Process = #tpid{pid=new, opts=Opts}, - {noreply, do_add_processes([Process], State#state{def_trace_opts=Opts})} + Process = #titem{id=new_processes, opts=Opts}, + {noreply, do_add_processes([Process], State#state{def_proc_flags=Opts})} + catch cancel -> {noreply, State} + end; + +handle_event(#wx{id=?ADD_NEW_PORTS}, State = #state{panel=Parent, def_port_flags=TraceOpts}) -> + try + Opts = observer_traceoptions_wx:port_trace(Parent, TraceOpts), + Port = #titem{id=new_ports, opts=Opts}, + {noreply, do_add_ports([Port], State#state{def_port_flags=Opts})} catch cancel -> {noreply, State} end; @@ -233,26 +319,36 @@ handle_event(#wx{id=?MODULES_WIN, event=#wxList{type=command_list_item_selected, update_functions_view(dict:fetch(Module, TPs), Fview), {noreply, State}; +handle_event(#wx{id=?NODES_WIN, + event=#wxList{type=command_list_item_selected}}, + State = #state{tpids=Tpids, tports=Tports, n_view=Nview, + proc_view=ProcView, port_view=PortView, nodes=Ns}) -> + Nodes = get_selected_items(Nview, Ns), + update_p_view(Tpids, ProcView, Nodes), + update_p_view(Tports, PortView, Nodes), + {noreply, State}; + handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 1}}, #state{panel = Panel, nodes = Nodes, tpids = TProcs, + tports = TPorts, tpatterns = TPs0, toggle_button = ToggleBtn, output = Opts } = State) -> try TPs = dict:to_list(TPs0), - (TProcs == []) andalso throw({error, "No processes traced"}), + (TProcs == []) andalso (TPorts == []) andalso throw({error, "No processes or ports traced"}), (Nodes == []) andalso throw({error, "No nodes traced"}), - HaveCallTrace = fun(#tpid{opts=Os}) -> lists:member(functions,Os) end, + HaveCallTrace = fun(#titem{opts=Os}) -> lists:member(functions,Os) end, WStr = "Call trace actived but no trace patterns used", (TPs == []) andalso lists:any(HaveCallTrace, TProcs) andalso observer_wx:create_txt_dialog(Panel, WStr, "Warning", ?wxICON_WARNING), {TTB, LogWin} = ttb_output_args(Panel, Opts), {ok, _} = ttb:tracer(Nodes, TTB), - setup_ttb(TPs, TProcs), + setup_ttb(TPs, TProcs, TPorts), wxToggleButton:setLabel(ToggleBtn, "Stop Trace"), {noreply, State#state{logwin=LogWin}} catch {error, Msg} -> @@ -302,7 +398,8 @@ handle_event(#wx{id=?LOG_SAVE, userData=TCtrl}, #state{panel=Panel} = State) -> handle_event(#wx{id = ?SAVE_TRACEOPTS}, #state{panel = Panel, - def_trace_opts = TraceOpts, + def_proc_flags = ProcFlags, + def_port_flags = PortFlags, match_specs = MatchSpecs, tpatterns = TracePatterns, output = Output @@ -312,7 +409,7 @@ handle_event(#wx{id = ?SAVE_TRACEOPTS}, ?wxID_OK -> Path = wxFileDialog:getPath(Dialog), write_file(Panel, Path, - TraceOpts, MatchSpecs, Output, + ProcFlags, PortFlags, MatchSpecs, Output, dict:to_list(TracePatterns) ); _ -> @@ -333,52 +430,159 @@ handle_event(#wx{id = ?LOAD_TRACEOPTS}, #state{panel = Panel} = State) -> wxDialog:destroy(Dialog), {noreply, State2}; -handle_event(#wx{id=Type, event=#wxList{type=command_list_item_right_click}}, - State = #state{panel=Panel}) -> - Menus = case Type of - ?PROC_WIN -> - [{?EDIT_PROCS, "Edit process options"}, - {?REMOVE_PROCS, "Remove processes"}]; - ?FUNCS_WIN -> - [{?EDIT_FUNCS_MS, "Edit matchspecs"}, - {?REMOVE_FUNCS_MS, "Remove trace patterns"}]; - ?NODES_WIN -> - [{?ADD_NODES, "Trace other nodes"}, - {?REMOVE_NODES, "Remove nodes"}] - end, - Menu = wxMenu:new(), - [wxMenu:append(Menu,Id,Str) || {Id,Str} <- Menus], - wxWindow:popupMenu(Panel, Menu), - wxMenu:destroy(Menu), +handle_event(#wx{id=?PROC_WIN, event=#wxList{type=command_list_item_right_click}}, + State = #state{panel=Panel, proc_view=LCtrl, tpids=Tpids, + n_view=Nview, nodes=Nodes}) -> + case get_visible_ps(Tpids, Nodes, Nview) of + [] -> + ok; + Visible -> + case get_selected_items(LCtrl, Visible) of + [] -> + ok; + _ -> + create_right_click_menu( + Panel, + [{?EDIT_PROCS, "Edit process options"}, + {?REMOVE_PROCS, "Remove processes"}]) + end + end, + {noreply, State}; + +handle_event(#wx{id=?PORT_WIN, event=#wxList{type=command_list_item_right_click}}, + State = #state{panel=Panel, port_view=LCtrl, tports=Tports, + n_view=Nview, nodes=Nodes}) -> + case get_visible_ps(Tports, Nodes, Nview) of + [] -> + ok; + Visible -> + case get_selected_items(LCtrl, Visible) of + [] -> + ok; + _ -> + create_right_click_menu( + Panel, + [{?EDIT_PORTS, "Edit port options"}, + {?REMOVE_PORTS, "Remove ports"}]) + end + end, + {noreply, State}; + +handle_event(#wx{id=?MODULES_WIN,event=#wxList{type=command_list_item_right_click}}, + State = #state{panel=Panel, m_view=Mview, tpatterns=TPs}) -> + case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of + [] -> + ok; + _ -> + create_right_click_menu( + Panel, + [{?REMOVE_MOD_MS, "Remove trace patterns"}]) + end, + {noreply,State}; + +handle_event(#wx{id=?FUNCS_WIN,event=#wxList{type=command_list_item_right_click}}, + State = #state{panel=Panel, m_view=Mview, f_view=Fview, + tpatterns=TPs}) -> + case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of + [] -> + ok; + [Module] -> + case get_selected_items(Fview, dict:fetch(Module, TPs)) of + [] -> + ok; + _ -> + create_right_click_menu( + Panel, + [{?EDIT_FUNCS_MS, "Edit matchspecs"}, + {?REMOVE_FUNCS_MS, "Remove trace patterns"}]) + end + end, + {noreply,State}; + +handle_event(#wx{id=?NODES_WIN,event=#wxList{type=command_list_item_right_click}}, + State = #state{panel=Panel, n_view=Nview, nodes=Nodes}) -> + Menu = + case get_selected_items(Nview, Nodes) of + [] -> + [{?ADD_NODES, "Add nodes"}]; + _ -> + [{?ADD_NODES, "Add nodes"}, + {?REMOVE_NODES, "Remove nodes"}] + end, + create_right_click_menu(Panel,Menu), {noreply, State}; -handle_event(#wx{id=?EDIT_PROCS}, #state{panel=Panel, tpids=Tpids, p_view=Ps} = State) -> +handle_event(#wx{id=?EDIT_PROCS}, #state{panel=Panel, tpids=Tpids, proc_view=Procs} = State) -> try - [#tpid{opts=DefOpts}|_] = Selected = get_selected_items(Ps, Tpids), + [#titem{opts=DefOpts}|_] = Selected = get_selected_items(Procs, Tpids), Opts = observer_traceoptions_wx:process_trace(Panel, DefOpts), - Changed = [Tpid#tpid{opts=Opts} || Tpid <- Selected], - {noreply, do_add_processes(Changed, State#state{def_trace_opts=Opts})} + Changed = [Tpid#titem{opts=Opts} || Tpid <- Selected], + {noreply, do_add_processes(Changed, State#state{def_proc_flags=Opts})} catch _:_ -> {noreply, State} end; -handle_event(#wx{id=?REMOVE_PROCS}, #state{tpids=Tpids, p_view=LCtrl} = State) -> +handle_event(#wx{id=?REMOVE_PROCS}, + #state{tpids=Tpids, proc_view=LCtrl, + n_view=Nview, nodes=Nodes} = State) -> Selected = get_selected_items(LCtrl, Tpids), Pids = Tpids -- Selected, - update_process_view(Pids, LCtrl), + update_p_view(Pids, LCtrl, Nodes, Nview), {noreply, State#state{tpids=Pids}}; -handle_event(#wx{id=?TRACE_DEFPS}, #state{panel=Panel, def_trace_opts=PO} = State) -> +handle_event(#wx{id=?EDIT_PORTS}, #state{panel=Panel, tports=Tports, port_view=Ports} = State) -> + try + [#titem{opts=DefOpts}|_] = Selected = get_selected_items(Ports, Tports), + Opts = observer_traceoptions_wx:port_trace(Panel, DefOpts), + Changed = [Tport#titem{opts=Opts} || Tport <- Selected], + {noreply, do_add_ports(Changed, State#state{def_port_flags=Opts})} + catch _:_ -> + {noreply, State} + end; + +handle_event(#wx{id=?REMOVE_PORTS}, + #state{tports=Tports, port_view=LCtrl, + n_view=Nview, nodes=Nodes} = State) -> + Selected = get_selected_items(LCtrl, Tports), + Ports = Tports -- Selected, + update_p_view(Ports, LCtrl, Nodes, Nview), + {noreply, State#state{tports=Ports}}; + +handle_event(#wx{id=?DEF_PROC_OPTS}, #state{panel=Panel, def_proc_flags=PO} = State) -> try Opts = observer_traceoptions_wx:process_trace(Panel, PO), - {noreply, State#state{def_trace_opts=Opts}} + {noreply, State#state{def_proc_flags=Opts}} catch _:_ -> {noreply, State} end; -handle_event(#wx{id=?TRACE_DEFMS}, #state{panel=Panel, match_specs=Ms} = State) -> +handle_event(#wx{id=?DEF_PORT_OPTS}, #state{panel=Panel, def_port_flags=PO} = State) -> + try + Opts = observer_traceoptions_wx:port_trace(Panel, PO), + {noreply, State#state{def_port_flags=Opts}} + catch _:_ -> + {noreply, State} + end; + +handle_event(#wx{id=?DEF_MS_FUNCS}, #state{panel=Panel, match_specs=Ms} = State) -> try %% Return selected MS and sends new MS's to us - observer_traceoptions_wx:select_matchspec(self(), Panel, Ms) + observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, funcs) + catch _:_ -> + cancel + end, + {noreply, State}; + +handle_event(#wx{id=?DEF_MS_SEND}, #state{panel=Panel, match_specs=Ms} = State) -> + try %% Return selected MS and sends new MS's to us + observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, send) + catch _:_ -> + cancel + end, + {noreply, State}; + +handle_event(#wx{id=?DEF_MS_RECV}, #state{panel=Panel, match_specs=Ms} = State) -> + try %% Return selected MS and sends new MS's to us + observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, 'receive') catch _:_ -> cancel end, @@ -389,12 +593,34 @@ handle_event(#wx{id=?EDIT_FUNCS_MS}, #state{panel=Panel, tpatterns=TPs, match_specs=Mss } = State) -> try - [Module] = get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))), - Selected = get_selected_items(LCtrl, dict:fetch(Module, TPs)), - Ms = observer_traceoptions_wx:select_matchspec(self(), Panel, Mss), - Changed = [TP#tpattern{ms=Ms} || TP <- Selected], - {noreply, do_add_patterns({Module, Changed}, State)} - catch _:_ -> + case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of + [] -> + throw({error,"No module selected"}); + [Module] -> + Selected = get_selected_items(LCtrl, dict:fetch(Module, TPs)), + Key = case Module of + 'Events' -> + SelectedEvents = + [Event || #tpattern{fa=Event} <- Selected], + E1 = hd(SelectedEvents), + case lists:all(fun(E) when E==E1 -> true; + (_) -> false + end, + SelectedEvents) of + true -> E1; + false -> throw({error,"Can not set match specs for multiple event types"}) + end; + _ -> funcs + end, + Ms = observer_traceoptions_wx:select_matchspec(self(), Panel, + Mss, Key), + Changed = [TP#tpattern{ms=Ms} || TP <- Selected], + {noreply, do_add_patterns({Module, Changed}, State)} + end + catch {error, Msg} -> + observer_wx:create_txt_dialog(Panel, Msg, "Error", ?wxICON_ERROR), + {noreply, State}; + cancel -> {noreply, State} end; @@ -417,6 +643,16 @@ handle_event(#wx{id=?REMOVE_FUNCS_MS}, #state{tpatterns=TPs0, f_view=LCtrl, m_vi {noreply, State#state{tpatterns=TPs}} end; +handle_event(#wx{id=?REMOVE_MOD_MS}, #state{tpatterns=TPs0, f_view=LCtrl, m_view=Mview} = State) -> + case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs0))) of + [] -> {noreply, State}; + [Module] -> + update_functions_view([], LCtrl), + TPs = dict:erase(Module, TPs0), + update_modules_view(lists:sort(dict:fetch_keys(TPs)), Module, Mview), + {noreply, State#state{tpatterns=TPs}} + end; + handle_event(#wx{id=?TRACE_OUTPUT}, #state{panel=Panel, output=Out0} = State) -> try Out = observer_traceoptions_wx:output(Panel, Out0), @@ -458,11 +694,20 @@ handle_call(Msg, From, _State) -> error({unhandled_call, Msg, From}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -handle_cast({add_processes, Pids}, State = #state{panel=Parent, def_trace_opts=TraceOpts}) -> +handle_cast({add_processes, Pids}, State = #state{panel=Parent, def_proc_flags=TraceOpts}) -> try Opts = observer_traceoptions_wx:process_trace(Parent, TraceOpts), - POpts = [#tpid{pid=Pid, opts=Opts} || Pid <- Pids], - S = do_add_processes(POpts, State#state{def_trace_opts=Opts}), + POpts = [#titem{id=Pid, opts=Opts} || Pid <- Pids], + S = do_add_processes(POpts, State#state{def_proc_flags=Opts}), + {noreply, S} + catch cancel -> + {noreply, State} + end; +handle_cast({add_ports, Ports}, State = #state{panel=Parent, def_port_flags=TraceOpts}) -> + try + Opts = observer_traceoptions_wx:port_trace(Parent, TraceOpts), + POpts = [#titem{id=Id, opts=Opts} || Id <- Ports], + S = do_add_ports(POpts, State#state{def_port_flags=Opts}), {noreply, S} catch cancel -> {noreply, State} @@ -510,45 +755,93 @@ do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_vi State#state{tpatterns=TPs} end. -do_add_processes(POpts, S0=#state{n_view=Nview, p_view=LCtrl, tpids=OldPids, nodes=Ns0}) -> - case merge_pids(POpts, OldPids) of - {OldPids, [], []} -> - S0; - {Pids, New, _Changed} -> - update_process_view(Pids, LCtrl), - Ns1 = lists:usort([node(Pid) || #tpid{pid=Pid} <- New, is_pid(Pid)]), +do_add_processes(POpts, S0=#state{n_view=Nview, proc_view=LCtrl, tpids=OldPids, nodes=OldNodes}) -> + CheckFun = fun(Pid) -> is_pid(Pid) end, + {Pids, Nodes} = do_add_pid_or_port(POpts, Nview, LCtrl, + OldPids, OldNodes, CheckFun), + S0#state{tpids=Pids, nodes=Nodes}. + +do_add_ports(POpts, S0=#state{n_view=Nview, port_view=LCtrl, tports=OldPorts, nodes=OldNodes}) -> + CheckFun = fun(Port) -> is_port(Port) end, + {Ports, Nodes} = do_add_pid_or_port(POpts, Nview, LCtrl, + OldPorts, OldNodes, CheckFun), + S0#state{tports=Ports, nodes=Nodes}. + +do_add_pid_or_port(POpts, Nview, LCtrl, OldPs, Ns0, Check) -> + case merge_trace_items(POpts, OldPs) of + {OldPs, [], []} -> + {OldPs,Ns0}; + {Ps, New, _Changed} -> + Ns1 = lists:usort([node(Id) || #titem{id=Id} <- New, Check(Id)]), Nodes = case ordsets:subtract(Ns1, Ns0) of + [] when Ns0==[] -> [observer_wx:get_active_node()]; [] -> Ns0; %% No new Nodes - NewNs -> - %% if dynamicly updates add trace patterns for new nodes - All = ordsets:union(NewNs, Ns0), - update_nodes_view(All, Nview), - All + NewNs -> ordsets:union(NewNs, Ns0) end, - S0#state{tpids=Pids, nodes=Nodes} + update_nodes_view(Nodes, Nview), + update_p_view(Ps, LCtrl, Nodes, Nview), + {Ps, Nodes} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -update_process_view(Pids, LCtrl) -> +get_visible_ps(PidsOrPorts, [Node], _Nview) -> + %% If only one node, treat this as selected + get_visible_ps(PidsOrPorts, [Node]); +get_visible_ps(PidsOrPorts, Nodes, Nview) -> + get_visible_ps(PidsOrPorts, get_selected_items(Nview, Nodes)). + +get_visible_ps(PidsOrPorts, Nodes) -> + %% Show pids/ports belonging to the selected nodes only (+ named pids/ports) + [P || P <- PidsOrPorts, + is_atom(P#titem.id) orelse + lists:member(node(P#titem.id),Nodes)]. + +update_p_view(PidsOrPorts, LCtrl, Nodes, Nview) -> + update_p_view(get_visible_ps(PidsOrPorts, Nodes, Nview), LCtrl). +update_p_view(PidsOrPorts, LCtrl, Nodes) -> + update_p_view(get_visible_ps(PidsOrPorts, Nodes), LCtrl). + +update_p_view(PidsOrPorts, LCtrl) -> + %% pid- or port-view wxListCtrl:deleteAllItems(LCtrl), - wx:foldl(fun(#tpid{pid=Pid, opts=Opts}, Row) -> + wx:foldl(fun(#titem{id=Id, opts=Opts}, Row) -> _Item = wxListCtrl:insertItem(LCtrl, Row, ""), ?EVEN(Row) andalso wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN), - wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Pid)), + wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Id)), wxListCtrl:setItem(LCtrl, Row, 1, observer_lib:to_str(Opts)), Row+1 - end, 0, Pids). + end, 0, PidsOrPorts), + case PidsOrPorts of + [] -> + wxListCtrl:setToolTip(LCtrl,?NO_P_HELP); + _ -> + wxListCtrl:setToolTip(LCtrl,?P_HELP) + end. update_nodes_view(Nodes, LCtrl) -> + Selected = + case Nodes of + [_] -> Nodes; + _ -> get_selected_items(LCtrl, Nodes) + end, wxListCtrl:deleteAllItems(LCtrl), wx:foldl(fun(Node, Row) -> _Item = wxListCtrl:insertItem(LCtrl, Row, ""), ?EVEN(Row) andalso wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN), wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Node)), + lists:member(Node,Selected) andalso % keep selection + wxListCtrl:setItemState(LCtrl, Row, 16#FFFF, + ?wxLIST_STATE_SELECTED), Row+1 - end, 0, Nodes). + end, 0, Nodes), + case Nodes of + [] -> + wxListCtrl:setToolTip(LCtrl,?NO_NODES_HELP); + _ -> + wxListCtrl:setToolTip(LCtrl,?NODES_HELP) + end. update_modules_view(Mods, Module, LCtrl) -> wxListCtrl:deleteAllItems(LCtrl), @@ -560,33 +853,51 @@ update_modules_view(Mods, Module, LCtrl) -> (Mod =:= Module) andalso wxListCtrl:setItemState(LCtrl, Row, 16#FFFF, ?wxLIST_STATE_SELECTED), Row+1 - end, 0, Mods). + end, 0, Mods), + Parent = wxListCtrl:getParent(LCtrl), + case Mods of + [] -> + wxListCtrl:setToolTip(Parent,?NO_TP_HELP); + _ -> + wxListCtrl:setToolTip(Parent,?TP_HELP) + end. update_functions_view(Funcs, LCtrl) -> wxListCtrl:deleteAllItems(LCtrl), - wx:foldl(fun(#tpattern{fa=FA, ms=#match_spec{str=Ms}}, Row) -> + wx:foldl(fun(#tpattern{m=M, fa=FA, ms=#match_spec{str=Ms}}, Row) -> _Item = wxListCtrl:insertItem(LCtrl, Row, ""), ?EVEN(Row) andalso wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN), - wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str({func,FA})), + FuncStr = + case M of + 'Events' -> + observer_lib:to_str(FA); + _ -> + observer_lib:to_str({func,FA}) + end, + wxListCtrl:setItem(LCtrl, Row, 0, FuncStr), wxListCtrl:setItem(LCtrl, Row, 1, Ms), Row+1 end, 0, Funcs). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -merge_pids([N1=#tpid{pid=new}|Ns], [N2=#tpid{pid=new}|Old]) -> - {Pids, New, Changed} = merge_pids_1(Ns,Old), - {[N1|Pids], New, [{N2,N2}|Changed]}; -merge_pids([N1=#tpid{pid=new}|Ns], Old) -> - {Pids, New, Changed} = merge_pids_1(Ns,Old), - {[N1|Pids], [N1|New], Changed}; -merge_pids(Ns, [N2=#tpid{pid=new}|Old]) -> - {Pids, New, Changed} = merge_pids_1(Ns,Old), - {[N2|Pids], New, Changed}; -merge_pids(New, Old) -> - merge_pids_1(New, Old). - -merge_pids_1(New, Old) -> - merge(lists:sort(New), Old, #tpid.pid, [], [], []). +%% Trace items are processes and ports +merge_trace_items([N1=#titem{id=NewP}|Ns], [N2=#titem{id=NewP}|Old]) + when NewP==new_processes; NewP==new_ports -> + {Ids, New, Changed} = merge_trace_items_1(Ns,Old), + {[N1|Ids], New, [{N2,N2}|Changed]}; +merge_trace_items([N1=#titem{id=NewP}|Ns], Old) + when NewP==new_processes; NewP==new_ports -> + {Ids, New, Changed} = merge_trace_items_1(Ns,Old), + {[N1|Ids], [N1|New], Changed}; +merge_trace_items(Ns, [N2=#titem{id=NewP}|Old]) + when NewP==new_processes; NewP==new_ports -> + {Ids, New, Changed} = merge_trace_items_1(Ns,Old), + {[N2|Ids], New, Changed}; +merge_trace_items(New, Old) -> + merge_trace_items_1(New, Old). + +merge_trace_items_1(New, Old) -> + merge(lists:sort(New), Old, #titem.id, [], [], []). merge_patterns(New, Old) -> merge(lists:sort(New), Old, #tpattern.fa, [], [], []). @@ -676,10 +987,12 @@ create_logwindow(Parent, true) -> wxFrame:show(LogWin), {LogWin, Text}. -setup_ttb(TPs, TPids) -> +setup_ttb(TPs, TPids, TPorts) -> _R1 = [setup_tps(FTP, []) || {_, FTP} <- TPs], - _R2 = [ttb:p(Pid, dbg_flags(Flags)) || #tpid{pid=Pid, opts=Flags} <- TPids], - [#tpid{pid=_Pid, opts=_Flags}|_] = TPids, + _R2 = [ttb:p(Pid, dbg_flags(proc,Flags)) || + #titem{id=Pid, opts=Flags} <- TPids], + _R3 = [ttb:p(Port, dbg_flags(port,Flags)) || + #titem{id=Port, opts=Flags} <- TPorts], ok. %% Sigh order is important @@ -695,20 +1008,24 @@ setup_tps([First|Rest], Prev) -> setup_tps([], Prev) -> [setup_tp(TP) || TP <- lists:reverse(Prev)]. +setup_tp(#tpattern{m='Events',fa=Event, ms=#match_spec{term=Ms}}) -> + ttb:tpe(Event,Ms); setup_tp(#tpattern{m=M,fa={F,A}, ms=#match_spec{term=Ms}}) -> ttb:tpl(M,F,A,Ms). -dbg_flags(Flags) -> - [dbg_flag(Flag) || Flag <- Flags]. +dbg_flags(Type,Flags) -> + [dbg_flag(Type,Flag) || Flag <- Flags]. -dbg_flag(send) -> s; -dbg_flag('receive') -> r; -dbg_flag(functions) -> c; -dbg_flag(on_spawn) -> sos; -dbg_flag(on_link) -> sol; -dbg_flag(on_first_spawn) -> sofs; -dbg_flag(on_first_link) -> sofl; -dbg_flag(events) -> p. +dbg_flag(_,send) -> s; +dbg_flag(_,'receive') -> r; +dbg_flag(proc,functions) -> c; +dbg_flag(proc,on_spawn) -> sos; +dbg_flag(proc,on_link) -> sol; +dbg_flag(proc,on_first_spawn) -> sofs; +dbg_flag(proc,on_first_link) -> sofl; +dbg_flag(proc,events) -> p; +dbg_flag(port,events) -> ports; +dbg_flag(_,Flag) -> Flag. textformat(Trace) when element(1, Trace) == trace_ts, tuple_size(Trace) >= 4 -> format_trace(Trace, tuple_size(Trace)-1, element(tuple_size(Trace),Trace)); @@ -784,23 +1101,28 @@ ftup(Trace, Index, Size) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -write_file(Frame, Filename, TraceOps, MatchSpecs, Output, TPs) -> - FormatMS = fun(#match_spec{name=Id, term=T, func=F}) -> - io_lib:format("[{name,\"~s\"}, {term, ~w}, {func, \"~s\"}]", - [Id, T, F]) - end, - FormatTP = fun({Module, FTPs}) -> - List = format_ftp(FTPs, FormatMS), - io_lib:format("{tp, ~w, [~s]}.~n",[Module, List]) +write_file(Frame, Filename, ProcFlags, PortFlags, MatchSpecs, Output, TPs) -> + MSToList = fun(#match_spec{name=Id, term=T, func=F}) -> + [{name,Id},{term,T},{func,F}] end, + MSTermList = [{ms,Key,[MSToList(MS) || MS <- MSs]} || + {Key,MSs} <- MatchSpecs], + TPToTuple = fun(#tpattern{fa={F,A}, ms=Ms}) -> + {F,A,MSToList(Ms)} + end, + ModuleTermList = [{tp, Module, [TPToTuple(FTP) || FTP <- FTPs]} || + {Module,FTPs} <- TPs], + Str = ["%%%\n%%% This file is generated by Observer\n", "%%%\n%%% DO NOT EDIT!\n%%%\n", - [["{ms, ", FormatMS(Ms), "}.\n"] || Ms <- MatchSpecs], - "{traceopts, ", io_lib:format("~w",[TraceOps]) ,"}.\n", - "{output, ", io_lib:format("~w",[Output]) ,"}.\n", - [FormatTP(TP) || TP <- TPs] + [io_lib:format("~p.~n",[MSTerm]) || MSTerm <- MSTermList], + io_lib:format("~p.~n",[{procflags,ProcFlags}]), + io_lib:format("~p.~n",[{portflags,PortFlags}]), + io_lib:format("~p.~n",[{output,Output}]), + [io_lib:format("~p.~n",[ModuleTerm]) || ModuleTerm <- ModuleTermList] ], + case file:write_file(Filename, list_to_binary(Str)) of ok -> success; @@ -809,38 +1131,66 @@ write_file(Frame, Filename, TraceOps, MatchSpecs, Output, TPs) -> observer_wx:create_txt_dialog(Frame, FailMsg, "Error", ?wxICON_ERROR) end. -format_ftp([#tpattern{fa={F,A}, ms=Ms}], FormatMS) -> - io_lib:format("{~w, ~w, ~s}", [F,A,FormatMS(Ms)]); -format_ftp([#tpattern{fa={F,A}, ms=Ms}|Rest], FormatMS) -> - [io_lib:format("{~w, ~w, ~s},~n ", [F,A,FormatMS(Ms)]), - format_ftp(Rest, FormatMS)]. - -read_settings(Filename, #state{match_specs=Ms0, def_trace_opts=TO0} = State) -> +read_settings(Filename, #state{match_specs=Ms0, def_proc_flags=ProcFs0, def_port_flags=PortFs0} = State) -> case file:consult(Filename) of {ok, Terms} -> - Ms = lists:usort(Ms0 ++ [parse_ms(MsList) || {ms, MsList} <- Terms]), - TOs = lists:usort(TO0 ++ proplists:get_value(traceopts, Terms, [])), + Ms = parse_ms(Terms, Ms0), + ProcFs1 = proplists:get_value(procflags, Terms, []) ++ + proplists:get_value(traceopts, Terms, []), % for backwards comp. + ProcFs = lists:usort(ProcFs0 ++ ProcFs1), + PortFs = lists:usort(PortFs0 ++ + proplists:get_value(portflags, Terms, [])), Out = proplists:get_value(output, Terms, []), lists:foldl(fun parse_tp/2, - State#state{match_specs=Ms, def_trace_opts=TOs, output=Out}, + State#state{match_specs=Ms, def_proc_flags=ProcFs, + def_port_flags=PortFs, output=Out}, Terms); {error, _} -> - observer_wx:create_txt_dialog(State#state.panel, "Could not load settings", + observer_wx:create_txt_dialog(State#state.panel, + "Could not load settings", "Error", ?wxICON_ERROR), State end. -parse_ms(Opts) -> - Name = proplists:get_value(name, Opts, "TracePattern"), - Term = proplists:get_value(term, Opts, [{'_',[],[ok]}]), - FunStr = proplists:get_value(term, Opts, "fun(_) -> ok end"), - make_ms(Name, Term, FunStr). +parse_ms(Terms, OldMSs) -> + MSs = + case [{Key,[make_ms(MS) || MS <- MSs]} || {ms,Key,MSs} <- Terms] of + [] -> + case [make_ms(MS) || {ms,MS} <- Terms] of + [] -> + []; + FuncMSs -> % for backwards compatibility + [{funcs,FuncMSs}] + end; + KeyMSs -> + KeyMSs + end, + parse_ms_1(MSs, dict:from_list(OldMSs)). + +parse_ms_1([{Key,MSs} | T], Dict) -> + parse_ms_1(T, dict:append_list(Key,MSs,Dict)); +parse_ms_1([],Dict) -> + [{Key,rm_dups(MSs,[])} || {Key,MSs} <- dict:to_list(Dict)]. + +rm_dups([H|T],Acc) -> + case lists:member(H,Acc) of + true -> + rm_dups(T,Acc); + false -> + rm_dups(T,[H|Acc]) + end; +rm_dups([],Acc) -> + lists:reverse(Acc). + +make_ms(MS) -> + [{func,FunStr},{name,Name},{term,Term}] = lists:keysort(1,MS), + make_ms(Name,Term,FunStr). make_ms(Name, Term, FunStr) -> #match_spec{name=Name, term=Term, str=io_lib:format("~w", Term), func = FunStr}. parse_tp({tp, Mod, FAs}, State) -> - Patterns = [#tpattern{m=Mod,fa={F,A}, ms=parse_ms(List)} || + Patterns = [#tpattern{m=Mod,fa={F,A}, ms=make_ms(List)} || {F,A,List} <- FAs], do_add_patterns({Mod, Patterns}, State); parse_tp(_, State) -> @@ -866,3 +1216,9 @@ get_indecies(Rest = [_|_], I, [_|T]) -> get_indecies(Rest, I+1, T); get_indecies(_, _, _) -> []. + +create_right_click_menu(Panel,Menus) -> + Menu = wxMenu:new(), + [wxMenu:append(Menu,Id,Str) || {Id,Str} <- Menus], + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu). diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index 9ba9b72b6f..285c298c4b 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -22,8 +22,8 @@ -include_lib("wx/include/wx.hrl"). -include("observer_defs.hrl"). --export([process_trace/2, trace_pattern/4, select_nodes/2, - output/2, select_matchspec/3]). +-export([process_trace/2, port_trace/2, trace_pattern/4, select_nodes/2, + output/2, select_matchspec/4]). process_trace(Parent, Default) -> Dialog = wxDialog:new(Parent, ?wxID_ANY, "Process Options", @@ -36,12 +36,20 @@ process_trace(Parent, Default) -> FuncBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace function call", []), check_box(FuncBox, lists:member(functions, Default)), + ArityBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace arity instead of arguments", []), + check_box(ArityBox, lists:member(functions, Default)), SendBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace send message", []), check_box(SendBox, lists:member(send, Default)), RecBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace receive message", []), check_box(RecBox, lists:member('receive', Default)), EventBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace process events", []), check_box(EventBox, lists:member(events, Default)), + SchedBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of processes", []), + check_box(SchedBox, lists:member(running_procs, Default)), + ExitBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of exiting processes", []), + check_box(ExitBox, lists:member(exiting, Default)), + GCBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace garbage collections", []), + check_box(GCBox, lists:member(garbage_collection, Default)), {SpawnBox, SpwnAllRadio, SpwnFirstRadio} = optionpage_top_right(Panel, RightSz, [{flag, ?wxBOTTOM},{border, 5}], "spawn"), @@ -57,7 +65,7 @@ process_trace(Parent, Default) -> {Radio, Opt} <- [{SpwnAllRadio, on_spawn}, {SpwnFirstRadio, on_first_spawn}, {LinkAllRadio, on_link}, {LinkFirstRadio, on_first_link}]], - [wxSizer:add(LeftSz, CheckBox, []) || CheckBox <- [FuncBox,SendBox,RecBox,EventBox]], + [wxSizer:add(LeftSz, CheckBox, []) || CheckBox <- [FuncBox,ArityBox,SendBox,RecBox,EventBox,SchedBox,ExitBox,GCBox]], wxSizer:add(LeftSz, 150, -1), wxSizer:add(PanelSz, LeftSz, [{flag, ?wxEXPAND}, {proportion,1}]), @@ -80,7 +88,9 @@ process_trace(Parent, Default) -> case wxDialog:showModal(Dialog) of ?wxID_OK -> All = [{SendBox, send}, {RecBox, 'receive'}, - {FuncBox, functions}, {EventBox, events}, + {FuncBox, functions}, {ArityBox, arity}, + {EventBox, events}, {SchedBox, running_procs}, + {ExitBox, exiting}, {GCBox, garbage_collection}, {{SpawnBox, SpwnAllRadio}, on_spawn}, {{SpawnBox,SpwnFirstRadio}, on_first_spawn}, {{LinkBox, LinkAllRadio}, on_link}, @@ -98,12 +108,57 @@ process_trace(Parent, Default) -> throw(cancel) end. +port_trace(Parent, Default) -> + Dialog = wxDialog:new(Parent, ?wxID_ANY, "Port Options", + [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}]), + Panel = wxPanel:new(Dialog), + MainSz = wxBoxSizer:new(?wxVERTICAL), + OptsSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Tracing options"}]), + + SendBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace send message", []), + check_box(SendBox, lists:member(send, Default)), + RecBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace receive message", []), + check_box(RecBox, lists:member('receive', Default)), + EventBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace port events", []), + check_box(EventBox, lists:member(events, Default)), + SchedBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of ports", []), + check_box(SchedBox, lists:member(running_ports, Default)), + + [wxSizer:add(OptsSz, CheckBox, []) || CheckBox <- [SendBox,RecBox,EventBox,SchedBox]], + wxSizer:add(OptsSz, 150, -1), + + wxPanel:setSizer(Panel, OptsSz), + wxSizer:add(MainSz, Panel, [{flag, ?wxEXPAND}, {proportion,1}]), + Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL), + wxSizer:add(MainSz, Buttons, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}]), + wxWindow:setSizerAndFit(Dialog, MainSz), + wxSizer:setSizeHints(MainSz, Dialog), + + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + All = [{SendBox, send}, {RecBox, 'receive'}, + {EventBox, events}, {SchedBox, running_ports}], + Opts = [Id || {Tick, Id} <- All, wxCheckBox:getValue(Tick)], + wxDialog:destroy(Dialog), + lists:reverse(Opts); + ?wxID_CANCEL -> + wxDialog:destroy(Dialog), + throw(cancel) + end. + trace_pattern(ParentPid, Parent, Node, MatchSpecs) -> try - Module = module_selector(Parent, Node), - MFAs = function_selector(Parent, Node, Module), - MatchSpec = select_matchspec(ParentPid, Parent, MatchSpecs), - {Module, [#tpattern{m=M,fa={F,A},ms=MatchSpec} || {M,F,A} <- MFAs]} + {Module,MFAs,MatchSpec} = + case module_selector(Parent, Node) of + {'$trace_event',Event} -> + MS = select_matchspec(ParentPid, Parent, MatchSpecs, Event), + {'Events',[{'Events',Event}],MS}; + Mod -> + MFAs0 = function_selector(Parent, Node, Mod), + MS = select_matchspec(ParentPid, Parent, MatchSpecs, funcs), + {Mod,MFAs0,MS} + end, + {Module, [#tpattern{m=M,fa=FA,ms=MatchSpec} || {M,FA} <- MFAs]} catch cancel -> cancel end. @@ -112,7 +167,7 @@ select_nodes(Parent, Nodes) -> check_selector(Parent, Choices). module_selector(Parent, Node) -> - Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module", + Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module or Event", [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}, {size, {400, 400}}]), Panel = wxPanel:new(Dialog), @@ -136,7 +191,9 @@ module_selector(Parent, Node) -> wxWindow:setFocus(TxtCtrl), %% init data Modules = get_modules(Node), - AllModules = [{atom_to_list(X), X} || X <- Modules], + Events = [{"Messages sent",{'$trace_event',send}}, + {"Messages received",{'$trace_event','receive'}}], + AllModules = Events ++ [{atom_to_list(X), X} || X <- Modules], filter_listbox_data("", AllModules, ListBox), wxTextCtrl:connect(TxtCtrl, command_text_updated, [{callback, fun(#wx{event=#wxCommand{cmdString=Input}}, _) -> @@ -174,9 +231,9 @@ function_selector(Parent, Node, Module) -> not(erl_internal:guard_bif(Name, Arity))]), ParsedChoices = parse_function_names(Choices), case check_selector(Parent, ParsedChoices) of - [] -> [{Module, '_', '_'}]; + [] -> [{Module, {'_', '_'}}]; FAs -> - [{Module, F, A} || {F,A} <- FAs] + [{Module, {F, A}} || {F,A} <- FAs] end. check_selector(Parent, ParsedChoices) -> @@ -268,7 +325,12 @@ get_checked(ListBox, Acc) -> lists:reverse(Acc) end. -select_matchspec(Pid, Parent, MatchSpecs) -> +select_matchspec(Pid, Parent, AllMatchSpecs, Key) -> + {MatchSpecs,RestMS} = + case lists:keytake(Key,1,AllMatchSpecs) of + {value,{Key,MSs0},Rest} -> {MSs0,Rest}; + false -> {[],AllMatchSpecs} + end, Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Match Specifications", [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}, {size, {400, 400}}]), @@ -313,8 +375,12 @@ select_matchspec(Pid, Parent, MatchSpecs) -> filter_listbox_data("", Choices, ListBox), Add = fun(_,_) -> - case edit_ms(TextCtrl, new, Parent) of - Ms = #match_spec{} -> add_and_select(-1, Ms, ListBox); + case edit_ms(TextCtrl, new, Dialog) of + Ms = #match_spec{} -> + add_and_select(-1, Ms, ListBox), + wxWindow:enable(OkButt), + wxWindow:enable(EditMsBtn), + wxWindow:enable(DelMsBtn); Else -> Else end end, @@ -323,8 +389,12 @@ select_matchspec(Pid, Parent, MatchSpecs) -> case SelId >= 0 of true -> #match_spec{name=Name} = wxListBox:getClientData(ListBox,SelId), - case edit_ms(TextCtrl, Name, Parent) of - Ms = #match_spec{} -> add_and_select(SelId, Ms, ListBox); + case edit_ms(TextCtrl, Name, Dialog) of + Ms = #match_spec{} -> + add_and_select(SelId, Ms, ListBox), + wxWindow:enable(OkButt), + wxWindow:enable(EditMsBtn), + wxWindow:enable(DelMsBtn); Else -> Else end; false -> @@ -367,7 +437,7 @@ select_matchspec(Pid, Parent, MatchSpecs) -> Count = wxListBox:getCount(ListBox), MSs = [wxListBox:getClientData(ListBox, Id) || Id <- lists:seq(0, Count-1)], - Pid ! {update_ms, MSs}, + Pid ! {update_ms, [{Key,MSs}|RestMS]}, MS = lists:nth(SelId+1, MSs), wxDialog:destroy(Dialog), MS; diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl index 1860f2f469..59f6443551 100644 --- a/lib/observer/src/observer_tv_wx.erl +++ b/lib/observer/src/observer_tv_wx.erl @@ -37,7 +37,8 @@ -define(ID_UNREADABLE, 405). -define(ID_SYSTEM_TABLES, 406). -define(ID_TABLE_INFO, 407). - +-define(ID_SHOW_TABLE, 408). + -record(opt, {type=ets, sys_hidden=true, unread_hidden=true, @@ -49,6 +50,7 @@ { parent, grid, + panel, node=node(), opt=#opt{}, selected, @@ -86,12 +88,13 @@ init([Notebook, Parent]) -> wxListItem:destroy(Li), wxListCtrl:connect(Grid, command_list_item_activated), + wxListCtrl:connect(Grid, command_list_item_right_click), wxListCtrl:connect(Grid, command_list_item_selected), wxListCtrl:connect(Grid, command_list_col_click), wxListCtrl:connect(Grid, size, [{skip, true}]), wxWindow:setFocus(Grid), - {Panel, #state{grid=Grid, parent=Parent, timer={false, 10}}}. + {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer={false, 10}}}. handle_event(#wx{id=?ID_REFRESH}, State = #state{node=Node, grid=Grid, opt=Opt}) -> @@ -145,6 +148,16 @@ handle_event(#wx{event=#wxList{type=command_list_item_activated, end, {noreply, State}; +handle_event(#wx{event=#wxList{type=command_list_item_right_click}}, + State=#state{panel=Panel}) -> + + Menu = wxMenu:new(), + wxMenu:append(Menu, ?ID_TABLE_INFO, "Table info"), + wxMenu:append(Menu, ?ID_SHOW_TABLE, "Show Table Content"), + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu), + {noreply, State}; + handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Index}}, State) -> {noreply, State#state{selected=Index}}; @@ -160,6 +173,22 @@ handle_event(#wx{id=?ID_TABLE_INFO}, {noreply, State} end; +handle_event(#wx{id=?ID_SHOW_TABLE}, + State=#state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs, selected=Sel}) -> + case Sel of + undefined -> + {noreply, State}; + R when is_integer(R) -> + Table = lists:nth(Sel+1, Tabs), + case Table#tab.protection of + private -> + self() ! {error, "Table has 'private' protection and can not be read"}; + _ -> + observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}]) + end, + {noreply, State} + end; + handle_event(#wx{id=?ID_REFRESH_INTERVAL}, State = #state{grid=Grid, timer=Timer0}) -> Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60), @@ -315,6 +344,7 @@ display_table_info(Parent0, Node, Source, Table) -> {_, Sizer, _} = observer_lib:display_info(Frame, [IdInfo,Settings,Memory]), wxSizer:setSizeHints(Sizer, Frame), + wxWindow:setMinSize(Frame, {300, -1}), wxFrame:center(Frame), wxFrame:show(Frame). diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index 30bd4043e4..301bb4b32f 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -21,8 +21,8 @@ -behaviour(wx_object). -export([start/0, stop/0]). --export([create_menus/2, get_attrib/1, get_tracer/0, set_status/1, - create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). +-export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0, + set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). -export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, handle_call/3, handle_info/2, check_page_title/1]). @@ -55,6 +55,7 @@ notebook, main_panel, pro_panel, + port_panel, tv_panel, sys_panel, trace_panel, @@ -90,6 +91,9 @@ set_status(What) -> get_tracer() -> wx_object:call(observer, get_tracer). +get_active_node() -> + wx_object:call(observer, get_active_node). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init(_Args) -> @@ -165,6 +169,10 @@ setup(#state{frame = Frame} = State) -> ProPanel = observer_pro_wx:start_link(Notebook, self()), wxNotebook:addPage(Notebook, ProPanel, "Processes", []), + %% Port Panel + PortPanel = observer_port_wx:start_link(Notebook, self()), + wxNotebook:addPage(Notebook, PortPanel, "Ports", []), + %% Table Viewer Panel TVPanel = observer_tv_wx:start_link(Notebook, self()), wxNotebook:addPage(Notebook, TVPanel, "Table Viewer", []), @@ -188,6 +196,7 @@ setup(#state{frame = Frame} = State) -> status_bar = StatusBar, sys_panel = SysPanel, pro_panel = ProPanel, + port_panel = PortPanel, tv_panel = TVPanel, trace_panel = TracePanel, app_panel = AppPanel, @@ -379,6 +388,9 @@ handle_call({get_attrib, Attrib}, _From, State) -> handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) -> {reply, TraceP, State}; +handle_call(get_active_node, _From, State=#state{node=Node}) -> + {reply, Node, State}; + handle_call(stop, From, State) -> stop_servers(State), {noreply, State#state{reply_to=From}}; @@ -406,16 +418,21 @@ handle_info({nodedown, Node}, create_txt_dialog(Frame, Msg, "Node down", ?wxICON_EXCLAMATION), {noreply, State3}; -handle_info({open_link, Pid0}, State = #state{pro_panel=ProcViewer, frame=Frame}) -> - Pid = case Pid0 of - [_|_] -> try list_to_pid(Pid0) catch _:_ -> Pid0 end; - _ -> Pid0 +handle_info({open_link, Id0}, State = #state{pro_panel=ProcViewer, + port_panel=PortViewer, + frame=Frame}) -> + Id = case Id0 of + [_|_] -> try list_to_pid(Id0) catch _:_ -> Id0 end; + _ -> Id0 end, %% Forward to process tab - case is_pid(Pid) of - true -> wx_object:get_pid(ProcViewer) ! {procinfo_open, Pid}; - false -> - Msg = io_lib:format("Information about ~p is not available or implemented",[Pid]), + case Id of + Pid when is_pid(Pid) -> + wx_object:get_pid(ProcViewer) ! {procinfo_open, Pid}; + "#Port" ++ _ = Port -> + wx_object:get_pid(PortViewer) ! {portinfo_open, Port}; + _ -> + Msg = io_lib:format("Information about ~p is not available or implemented",[Id]), Info = wxMessageDialog:new(Frame, Msg), wxMessageDialog:showModal(Info), wxMessageDialog:destroy(Info) @@ -541,10 +558,11 @@ check_page_title(Notebook) -> get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys, tv_panel=Tv, trace_panel=Trace, app_panel=App, - perf_panel=Perf, allc_panel=Alloc + perf_panel=Perf, allc_panel=Alloc, port_panel=Port }) -> Panel = case check_page_title(Notebook) of "Processes" -> Pro; + "Ports" -> Port; "System" -> Sys; "Table Viewer" -> Tv; ?TRACE_STR -> Trace; diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 4d6eb3ba8d..ac6c4572eb 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -25,7 +25,8 @@ -export([tracer/0,tracer/1,tracer/2,p/2,stop/0,stop/1,start_trace/4]). -export([get_et_handler/0]). -export([tp/2, tp/3, tp/4, ctp/0, ctp/1, ctp/2, ctp/3, tpl/2, tpl/3, tpl/4, - ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3]). + ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3, + tpe/2, ctpe/1]). -export([seq_trigger_ms/0,seq_trigger_ms/1]). -export([write_trace_info/2]). -export([write_config/2,write_config/3,run_config/1,run_config/2,list_config/1]). @@ -397,16 +398,16 @@ arg_list([A1|A],Acc) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Set trace flags on processes -p(Procs0,Flags0) -> +p(ProcsPorts0,Flags0) -> ensure_no_overloaded_nodes(), - store(p,[Procs0,Flags0]), - no_store_p(Procs0,Flags0). -no_store_p(Procs0,Flags0) -> + store(p,[ProcsPorts0,Flags0]), + no_store_p(ProcsPorts0,Flags0). +no_store_p(ProcsPorts0,Flags0) -> case transform_flags(to_list(Flags0)) of {error,Reason} -> {error,Reason}; Flags -> - Procs = procs(Procs0), + ProcsPorts = procs_ports(ProcsPorts0), case lists:foldl(fun(P,{PMatched,Ps}) -> case dbg:p(P,Flags) of {ok,Matched} -> {[{P,Matched}|PMatched],[P|Ps]}; @@ -414,7 +415,7 @@ no_store_p(Procs0,Flags0) -> display_warning(P,Reason), {PMatched,Ps} end - end,{[],[]},Procs) of + end,{[],[]},ProcsPorts) of {[],[]} -> {error, no_match}; {SuccMatched,Succ} -> no_store_write_trace_info(flags,{Succ,Flags}), @@ -429,20 +430,22 @@ transform_flags(Flags) -> dbg:transform_flags([timestamp | Flags]). -procs(Procs) when is_list(Procs) -> - lists:foldl(fun(P,Acc) -> proc(P)++Acc end,[],Procs); -procs(Proc) -> - proc(Proc). +procs_ports(Procs) when is_list(Procs) -> + lists:foldl(fun(P,Acc) -> proc_port(P)++Acc end,[],Procs); +procs_ports(Proc) -> + proc_port(Proc). -proc(Procs) when Procs=:=all; Procs=:=ports; Procs=:=processes; - Procs=:=existing; Procs=:=existing_ports; Procs=:=existing_processes; - Procs=:=new; Procs=:=new_ports; Procs=:=new_processes -> - [Procs]; -proc(Name) when is_atom(Name) -> +proc_port(P) when P=:=all; P=:=ports; P=:=processes; + P=:=existing; P=:=existing_ports; P=:=existing_processes; + P=:=new; P=:=new_ports; P=:=new_processes -> + [P]; +proc_port(Name) when is_atom(Name) -> [Name]; % can be registered on this node or other node -proc(Pid) when is_pid(Pid) -> +proc_port(Pid) when is_pid(Pid) -> [Pid]; -proc({global,Name}) -> +proc_port(Port) when is_port(Port) -> + [Port]; +proc_port({global,Name}) -> case global:whereis_name(Name) of Pid when is_pid(Pid) -> [Pid]; @@ -479,6 +482,11 @@ tpl(A,B,C,D) -> store(tpl,[A,B,C,ms(D)]), dbg:tpl(A,B,C,ms(D)). +tpe(A,B) -> + ensure_no_overloaded_nodes(), + store(tpe,[A,ms(B)]), + dbg:tpe(A,ms(B)). + ctp() -> store(ctp,[]), dbg:ctp(). @@ -518,6 +526,10 @@ ctpg(A,B,C) -> store(ctpg,[A,B,C]), dbg:ctpg(A,B,C). +ctpe(A) -> + store(ctpe,[A]), + dbg:ctpe(A). + ms(return) -> [{'_',[],[{return_trace}]}]; ms(caller) -> @@ -1298,6 +1310,9 @@ ip_to_file(Trace, {shell_only, Fun} = State) -> ip_to_file(Trace,{{file,File}, ShellOutput}) -> Fun = dbg:trace_port(file,File), %File can be a filename or a wrap spec Port = Fun(), + %% Just in case this is on the traced node, + %% make sure the port is not traced. + p(Port,clear), %% Store the port so it can be properly closed ?MODULE ! {ip_to_file_trace_port, Port, self()}, receive {?MODULE,ok} -> ok end, diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl index ae8111dcd6..4c882ad951 100644 --- a/lib/observer/test/observer_SUITE.erl +++ b/lib/observer/test/observer_SUITE.erl @@ -171,6 +171,7 @@ test_page("Applications" ++ _, _Window) -> test_page("Processes" ++ _, _Window) -> timer:sleep(500), %% Give it time to refresh Active = get_active(), + Active ! refresh_interval, ChangeSort = fun(N) -> FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}}, Active ! FakeEv, @@ -184,7 +185,23 @@ test_page("Processes" ++ _, _Window) -> timer:sleep(1000), %% Give it time to refresh ok; -test_page(_Title = "Table" ++ _, _Window) -> +test_page("Ports" ++ _, _Window) -> + timer:sleep(500), %% Give it time to refresh + Active = get_active(), + Active ! refresh_interval, + ChangeSort = fun(N) -> + FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}}, + Active ! FakeEv, + timer:sleep(200) + end, + [ChangeSort(N) || N <- lists:seq(1,4) ++ [0]], + Activate = #wx{event=#wxList{type=command_list_item_activated, + itemIndex=2}}, + Active ! Activate, + timer:sleep(1000), %% Give it time to refresh + ok; + +test_page("Table" ++ _, _Window) -> Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)], Table = lists:nth(3, Tables), ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]), @@ -208,6 +225,13 @@ test_page(_Title = "Table" ++ _, _Window) -> timer:sleep(1000), ok; +test_page("Trace Overview" ++ _, _Window) -> + timer:sleep(500), %% Give it time to refresh + Active = get_active(), + Active ! refresh_interval, + timer:sleep(1000), %% Give it time to refresh + ok; + test_page(Title, Window) -> io:format("Page ~p: ~p~n", [Title, Window]), %% Just let it display some info and hopefully it doesn't crash diff --git a/lib/runtime_tools/c_src/dyntrace.c b/lib/runtime_tools/c_src/dyntrace.c index e7a4a73373..3d2de9c21c 100644 --- a/lib/runtime_tools/c_src/dyntrace.c +++ b/lib/runtime_tools/c_src/dyntrace.c @@ -96,14 +96,14 @@ static ErlNifFunc nif_funcs[] = { {"user_trace_i4s4", 9, user_trace_i4s4}, {"user_trace_n", 10, user_trace_n}, #ifdef HAVE_USE_LTTNG - {"trace_procs", 6, trace_procs}, - {"trace_ports", 6, trace_ports}, - {"trace_running_procs", 6, trace_running_procs}, - {"trace_running_ports", 6, trace_running_ports}, - {"trace_call", 6, trace_call}, - {"trace_send", 6, trace_send}, - {"trace_receive", 6, trace_receive}, - {"trace_garbage_collection", 6, trace_garbage_collection}, + {"trace_procs", 5, trace_procs}, + {"trace_ports", 5, trace_ports}, + {"trace_running_procs", 5, trace_running_procs}, + {"trace_running_ports", 5, trace_running_ports}, + {"trace_call", 5, trace_call}, + {"trace_send", 5, trace_send}, + {"trace_receive", 5, trace_receive}, + {"trace_garbage_collection", 5, trace_garbage_collection}, {"enabled_procs", 3, enabled_procs}, {"enabled_ports", 3, enabled_ports}, {"enabled_running_procs", 3, enabled_running_procs}, @@ -114,8 +114,7 @@ static ErlNifFunc nif_funcs[] = { {"enabled_garbage_collection", 3, enabled_garbage_collection}, #endif {"enabled", 3, enabled}, - {"trace", 5, trace}, - {"trace", 6, trace} + {"trace", 5, trace} }; ERL_NIF_INIT(dyntrace, nif_funcs, load, NULL, NULL, NULL) @@ -123,6 +122,7 @@ ERL_NIF_INIT(dyntrace, nif_funcs, load, NULL, NULL, NULL) static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_extra; static ERL_NIF_TERM atom_not_available; static ERL_NIF_TERM atom_badarg; static ERL_NIF_TERM atom_ok; @@ -187,6 +187,7 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_true = enif_make_atom(env,"true"); atom_false = enif_make_atom(env,"false"); atom_error = enif_make_atom(env,"error"); + atom_extra = enif_make_atom(env,"extra"); atom_not_available = enif_make_atom(env,"not_available"); atom_badarg = enif_make_atom(env,"badarg"); atom_ok = enif_make_atom(env,"ok"); @@ -325,7 +326,7 @@ static ERL_NIF_TERM trace_garbage_collection(ErlNifEnv* env, int argc, const ERL int arity; unsigned long ohbsz, nhbsz, size; - ASSERT(argc == 6); + ASSERT(argc == 5); /* Assume gc info order does not change */ gci = argv[3]; @@ -428,11 +429,13 @@ static ERL_NIF_TERM trace_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv } } else if (argv[0] == atom_exception_from) { const ERL_NIF_TERM* tuple; + ERL_NIF_TERM extra; int arity; lttng_decl_mfabuf(mfa); char class[LTTNG_BUFFER_SZ]; - enif_get_tuple(env, argv[4], &arity, &tuple); + enif_get_map_value(env, argv[4], atom_extra, &extra); + enif_get_tuple(env, extra, &arity, &tuple); enif_snprintf(class, LTTNG_BUFFER_SZ, "%T", tuple[0]); if (enif_get_tuple(env, argv[3], &arity, &tuple)) { @@ -457,14 +460,17 @@ static ERL_NIF_TERM enabled_send(ErlNifEnv *env, int argc, const ERL_NIF_TERM ar static ERL_NIF_TERM trace_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + ERL_NIF_TERM extra; lttng_decl_procbuf(pid); lttng_pid_to_str(argv[2], pid); + enif_get_map_value(env, argv[4], atom_extra, &extra); + if (argv[0] == atom_send) { lttng_decl_procbuf(to); char msg[LTTNG_BUFFER_SZ]; - lttng_pid_to_str(argv[4], to); + lttng_pid_to_str(extra, to); enif_snprintf(msg, LTTNG_BUFFER_SZ, "%T", argv[3]); LTTNG3(message_send, pid, to, msg); @@ -472,7 +478,7 @@ static ERL_NIF_TERM trace_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv lttng_decl_procbuf(to); char msg[LTTNG_BUFFER_SZ]; - lttng_pid_to_str(argv[4], to); + lttng_pid_to_str(extra, to); enif_snprintf(msg, LTTNG_BUFFER_SZ, "%T", argv[3]); /* mark it as non existing ? */ @@ -537,6 +543,7 @@ static ERL_NIF_TERM trace_procs(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg /* spawn */ if (argv[0] == atom_spawn) { + ERL_NIF_TERM extra; char undef[] = "undefined"; const ERL_NIF_TERM* tuple; int arity; @@ -545,7 +552,9 @@ static ERL_NIF_TERM trace_procs(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg lttng_pid_to_str(argv[3], to); - if (enif_get_tuple(env, argv[4], &arity, &tuple)) { + enif_get_map_value(env, argv[4], atom_extra, &extra); + + if (enif_get_tuple(env, extra, &arity, &tuple)) { if (enif_is_list(env, tuple[2])) { enif_get_list_length(env, tuple[2], &len); } else { @@ -618,11 +627,13 @@ static ERL_NIF_TERM trace_ports(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg /* open and closed */ if (argv[0] == atom_open) { + ERL_NIF_TERM extra; char driver[LTTNG_BUFFER_SZ]; lttng_decl_procbuf(pid); lttng_pid_to_str(argv[3], pid); + enif_get_map_value(env, argv[4], atom_extra, &extra); - enif_snprintf(driver, LTTNG_BUFFER_SZ, "%T", argv[4]); + enif_snprintf(driver, LTTNG_BUFFER_SZ, "%T", extra); LTTNG3(port_open, pid, driver, port); } else if (argv[0] == atom_closed) { char reason[LTTNG_BUFFER_SZ]; diff --git a/lib/runtime_tools/doc/src/LTTng.xml b/lib/runtime_tools/doc/src/LTTng.xml index 06152c66d6..9b490a27a0 100644 --- a/lib/runtime_tools/doc/src/LTTng.xml +++ b/lib/runtime_tools/doc/src/LTTng.xml @@ -75,6 +75,10 @@ $ make </code> <item><c>parent : string</c> :: Process ID. Ex. <c>"<0.131.0>"</c></item> <item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>procs</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">process_spawn: { cpu_id = 3 }, { pid = "<0.131.0>", parent = "<0.130.0>", entry = "erlang:apply/2" }</code> @@ -84,6 +88,10 @@ $ make </code> <item><c>from : string</c> :: Process ID or Port ID. Ex. <c>"<0.131.0>"</c></item> <item><c>type : string</c> :: <c>"link" | "unlink"</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>procs</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">process_link: { cpu_id = 3 }, { from = "<0.130.0>", to = "<0.131.0>", type = "link" }</code> @@ -93,6 +101,10 @@ $ make </code> <item><c>pid : string</c> :: Process ID. Ex. <c>"<0.131.0>"</c></item> <item><c>reason : string</c> :: Exit reason. Ex. <c>"normal"</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>procs</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">process_exit: { cpu_id = 3 }, { pid = "<0.130.0>", reason = "normal" }</code> @@ -111,7 +123,10 @@ $ make </code> <item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item> <item><c>type : string</c> :: <c>"in" | "out" | "in_exiting" | "out_exiting" | "out_exited"</c></item> </list> - + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>running</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">process_scheduled: { cpu_id = 0 }, { pid = "<0.136.0>", entry = "erlang:apply/2", type = "in" }</code> @@ -122,7 +137,10 @@ $ make </code> <item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item> <item><c>port : string</c> :: Port ID. Ex. <c>"#Port<0.1031>"</c></item> </list> - + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>ports</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">port_open: { cpu_id = 5 }, { pid = "<0.131.0>", driver = "'/bin/sh -s unix:cmd'", port = "#Port<0.1887>" }</code> @@ -131,6 +149,10 @@ $ make </code> <item><c>port : string</c> :: Port ID. Ex. <c>"#Port<0.1031>"</c></item> <item><c>reason : string</c> :: Exit reason. Ex. <c>"normal"</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>ports</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">port_exit: { cpu_id = 5 }, { port = "#Port<0.1887>", reason = "normal" }</code> @@ -140,10 +162,18 @@ $ make </code> <item><c>from : string</c> :: Process ID. Ex. <c>"<0.131.0>"</c></item> <item><c>type : string</c> :: <c>"link" | "unlink"</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>ports</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">port_link: { cpu_id = 5 }, { from = "#Port<0.1887>", to = "<0.131.0>", type = "unlink" }</code> <p><em>port_scheduled</em></p> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>running</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <list type="bulleted"> <item><c>port : string</c> :: Port ID. Ex. <c>"#Port<0.1031>"</c></item> <item><c>entry : string</c> :: Callback. Ex. <c>"open"</c></item> @@ -152,13 +182,20 @@ $ make </code> <p>Example:</p> <code type="none">port_scheduled: { cpu_id = 5 }, { pid = "#Port<0.1905>", entry = "close", type = "out" }</code> - + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>running</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p><em>function_call</em></p> <list type="bulleted"> <item><c>pid : string</c> :: Process ID. Ex. <c>"<0.131.0>"</c></item> <item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item> <item><c>depth : integer</c> :: Stack depth. Ex. <c>0</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>call</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">function_call: { cpu_id = 5 }, { pid = "<0.145.0>", entry = "dyntrace_lttng_SUITE:'-t_call/1-fun-1-'/0", depth = 0 }</code> @@ -168,6 +205,10 @@ $ make </code> <item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item> <item><c>depth : integer</c> :: Stack depth. Ex. <c>0</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>call</c> or <c>return_to</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">function_return: { cpu_id = 5 }, { pid = "<0.145.0>", entry = "dyntrace_lttng_SUITE:waiter/0", depth = 0 }</code> @@ -177,6 +218,10 @@ $ make </code> <item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item> <item><c>class : string</c> :: Error reason. Ex. <c>"error"</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>call</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">function_exception: { cpu_id = 5 }, { pid = "<0.144.0>", entry = "t:call_exc/1", class = "error" }</code> @@ -186,6 +231,10 @@ $ make </code> <item><c>to : string</c> :: Process ID or Port ID. Ex. <c>"<0.131.0>"</c></item> <item><c>message : string</c> :: Message sent. Ex. <c>"{<0.162.0>,ok}"</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>send</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">message_send: { cpu_id = 3 }, { from = "#Port<0.1938>", to = "<0.160.0>", message = "{#Port<0.1938>,eof}" }</code> @@ -194,6 +243,10 @@ $ make </code> <item><c>to : string</c> :: Process ID or Port ID. Ex. <c>"<0.131.0>"</c></item> <item><c>message : string</c> :: Message received. Ex. <c>"{<0.162.0>,ok}"</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>'receive'</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">message_receive: { cpu_id = 7 }, { to = "<0.167.0>", message = "{<0.165.0>,ok}" }</code> @@ -204,6 +257,10 @@ $ make </code> <item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item> <item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>garbage_collection</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">gc_minor_start: { cpu_id = 0 }, { pid = "<0.172.0>", need = 0, heap = 610, old_heap = 0 }</code> @@ -214,6 +271,10 @@ $ make </code> <item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item> <item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>garbage_collection</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">gc_minor_end: { cpu_id = 0 }, { pid = "<0.172.0>", reclaimed = 120, heap = 1598, old_heap = 1598 }</code> @@ -224,6 +285,10 @@ $ make </code> <item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item> <item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>garbage_collection</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">gc_major_start: { cpu_id = 0 }, { pid = "<0.172.0>", need = 8, heap = 2586, old_heap = 1598 }</code> @@ -234,6 +299,10 @@ $ make </code> <item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item> <item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item> </list> + <p> + Available through + <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>garbage_collection</c> and <c>{tracer,dyntrace,[]}</c> as tracer module. + </p> <p>Example:</p> <code type="none">gc_major_end: { cpu_id = 0 }, { pid = "<0.172.0>", reclaimed = 240, heap = 4185, old_heap = 0 }</code> @@ -454,6 +523,73 @@ $ make </code> </section> <section> - <title>Examples</title> + <title>Example of process tracing</title> + <p>An example of process tracing of <c>os_mon</c> and friends.</p> + + <p>Clean start of lttng in a bash shell.</p> + + <pre>$ lttng create erlang-demo +Spawning a session daemon +Session erlang-demo created. +Traces will be written in /home/egil/lttng-traces/erlang-demo-20160526-165920</pre> + + <p>Start an Erlang node with lttng enabled.</p> + + <pre>$ erl +Erlang/OTP 19 [erts-8.0] [source-4d7b24d] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [lttng] + +Eshell V8.0 (abort with ^G) +1></pre> + + <p>Load the <c>dyntrace</c> module.</p> + + <pre>1> <input>l(dyntrace).</input> +{module,dyntrace}</pre> + + <p>All tracepoints via dyntrace are now visibile and can be listed through <c>lttng list -u</c>.</p> + + <p>Enable the process_register LTTng tracepoint for Erlang.</p> + + <pre>$ lttng enable-event -u com_ericsson_dyntrace:process_register +UST event com_ericsson_dyntrace:process_register created in channel channel0</pre> + + <p>Enable process tracing for new processes and use <c>dyntrace</c> as tracer backend.</p> + + <pre>2> <input>erlang:trace(new,true,[procs,{tracer,dyntrace,[]}]).</input> +0</pre> + + <p>Start LTTng tracing.</p> + + <pre>$ lttng start +Tracing started for session erlang-demo</pre> + + <p>Start the <c>os_mon</c> application in Erlang.</p> + + <pre>3> <input>application:ensure_all_started(os_mon).</input> +{ok,[sasl,os_mon]}</pre> + + <p>Stop LTTng tracing and view the result.</p> + + <pre>$ lttng stop +Tracing stopped for session erlang-demo +$ lttng view +[17:20:42.561168759] (+?.?????????) elxd1168lx9 com_ericsson_dyntrace:process_register: \ + { cpu_id = 5 }, { pid = "<0.66.0>", name = "sasl_sup", type = "register" } +[17:20:42.561215519] (+0.000046760) elxd1168lx9 com_ericsson_dyntrace:process_register: \ + { cpu_id = 5 }, { pid = "<0.67.0>", name = "sasl_safe_sup", type = "register" } +[17:20:42.562149024] (+0.000933505) elxd1168lx9 com_ericsson_dyntrace:process_register: \ + { cpu_id = 5 }, { pid = "<0.68.0>", name = "alarm_handler", type = "register" } +[17:20:42.571035803] (+0.008886779) elxd1168lx9 com_ericsson_dyntrace:process_register: \ + { cpu_id = 5 }, { pid = "<0.69.0>", name = "release_handler", type = "register" } +[17:20:42.574939868] (+0.003904065) elxd1168lx9 com_ericsson_dyntrace:process_register: \ + { cpu_id = 5 }, { pid = "<0.74.0>", name = "os_mon_sup", type = "register" } +[17:20:42.576818712] (+0.001878844) elxd1168lx9 com_ericsson_dyntrace:process_register: \ + { cpu_id = 5 }, { pid = "<0.75.0>", name = "disksup", type = "register" } +[17:20:42.580032013] (+0.003213301) elxd1168lx9 com_ericsson_dyntrace:process_register: \ + { cpu_id = 5 }, { pid = "<0.76.0>", name = "memsup", type = "register" } +[17:20:42.583046339] (+0.003014326) elxd1168lx9 com_ericsson_dyntrace:process_register: \ + { cpu_id = 5 }, { pid = "<0.78.0>", name = "cpu_sup", type = "register" } +[17:20:42.586206242] (+0.003159903) elxd1168lx9 com_ericsson_dyntrace:process_register: \ + { cpu_id = 5 }, { pid = "<0.82.0>", name = "timer_server", type = "register" }</pre> </section> </chapter> diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml index 49b11ddc3c..14a81b2293 100644 --- a/lib/runtime_tools/doc/src/dbg.xml +++ b/lib/runtime_tools/doc/src/dbg.xml @@ -189,9 +189,9 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ <tag><c>all</c></tag> <item>All processes and ports in the system as well as all processes and ports created hereafter are to be traced.</item> - <tag><c>all_processes</c></tag> + <tag><c>processes</c></tag> <item>All processes in the system as well as all processes created hereafter are to be traced.</item> - <tag><c>all_ports</c></tag> + <tag><c>ports</c></tag> <item>All ports in the system as well as all ports created hereafter are to be traced.</item> <tag><c>new</c></tag> <item>All processes and ports created after the call is are to be traced.</item> diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index 8cdb5a43e3..c0d4665bda 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -1155,7 +1155,7 @@ all() -> [send,'receive',call,procs,ports,garbage_collection,running, set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link, timestamp,monotonic_timestamp,strict_monotonic_timestamp, - arity,return_to,silent,running_procs,running_ports]. + arity,return_to,silent,running_procs,running_ports,exiting]. display_info([Node|Nodes]) -> io:format("~nNode ~w:~n",[Node]), @@ -1313,6 +1313,9 @@ tc_loop(Other, _Handler, _HData) -> gen_reader(ip, {Host, Portno}) -> case gen_tcp:connect(Host, Portno, [{active, false}, binary]) of {ok, Sock} -> + %% Just in case this is on the traced node, + %% make sure the port is not traced. + p(Sock,clear), mk_reader(fun ip_read/2, Sock); Error -> exit(Error) diff --git a/lib/runtime_tools/src/dyntrace.erl b/lib/runtime_tools/src/dyntrace.erl index 28e6d67d96..58c5a773c3 100644 --- a/lib/runtime_tools/src/dyntrace.erl +++ b/lib/runtime_tools/src/dyntrace.erl @@ -42,15 +42,14 @@ -export([put_tag/1, get_tag/0, get_tag_data/0, spread_tag/1, restore_tag/1]). -export([trace/5, - trace/6, - trace_procs/6, - trace_ports/6, - trace_running_procs/6, - trace_running_ports/6, - trace_call/6, - trace_send/6, - trace_receive/6, - trace_garbage_collection/6]). + trace_procs/5, + trace_ports/5, + trace_running_procs/5, + trace_running_ports/5, + trace_call/5, + trace_send/5, + trace_receive/5, + trace_garbage_collection/5]). -export([enabled_procs/3, enabled_ports/3, @@ -147,34 +146,31 @@ user_trace_i4s4(_, _, _, _, _, _, _, _, _) -> user_trace_n(_, _, _, _, _, _, _, _, _, _) -> erlang:nif_error(nif_not_loaded). -trace(_TracerState, _Label, _SeqTraceInfo, _, _Opts) -> +trace(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> erlang:nif_error(nif_not_loaded). -trace(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) -> +trace_procs(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> erlang:nif_error(nif_not_loaded). -trace_procs(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) -> +trace_ports(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> erlang:nif_error(nif_not_loaded). -trace_ports(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) -> +trace_running_procs(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> erlang:nif_error(nif_not_loaded). -trace_running_procs(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) -> +trace_running_ports(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> erlang:nif_error(nif_not_loaded). -trace_running_ports(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) -> +trace_call(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> erlang:nif_error(nif_not_loaded). -trace_call(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) -> +trace_send(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> erlang:nif_error(nif_not_loaded). -trace_send(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) -> +trace_receive(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> erlang:nif_error(nif_not_loaded). -trace_receive(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) -> - erlang:nif_error(nif_not_loaded). - -trace_garbage_collection(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) -> +trace_garbage_collection(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> erlang:nif_error(nif_not_loaded). enabled(_TraceTag, _TracerState, _Tracee) -> diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 66653c5b7f..cedb677178 100644 --- a/lib/runtime_tools/src/observer_backend.erl +++ b/lib/runtime_tools/src/observer_backend.erl @@ -23,7 +23,8 @@ -export([vsn/0]). %% observer stuff --export([sys_info/0, get_table/3, get_table_list/2, fetch_stats/2]). +-export([sys_info/0, get_port_list/0, + get_table/3, get_table_list/2, fetch_stats/2]). %% etop stuff -export([etop_collect/1]). @@ -139,6 +140,15 @@ get_mnesia_loop(Parent, {Match, Cont}) -> Parent ! {self(), Match}, get_mnesia_loop(Parent, mnesia:select(Cont)). +get_port_list() -> + [begin + [{port_id,P}|erlang:port_info(P)] ++ + case erlang:port_info(P,monitors) of + undefined -> []; + Monitors -> [Monitors] + end + end || P <- erlang:ports()]. + get_table_list(ets, Opts) -> HideUnread = proplists:get_value(unread_hidden, Opts, true), HideSys = proplists:get_value(sys_hidden, Opts, true), diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl index 5a3c885571..4b0864858c 100644 --- a/lib/runtime_tools/test/dbg_SUITE.erl +++ b/lib/runtime_tools/test/dbg_SUITE.erl @@ -30,7 +30,7 @@ erl_tracer/1, distributed_erl_tracer/1]). -export([tracee1/1, tracee2/1]). -export([dummy/0, exported/1]). --export([enabled/3, trace/6, load_nif/1]). +-export([enabled/3, trace/5, load_nif/1]). -include_lib("common_test/include/ct.hrl"). @@ -961,7 +961,7 @@ erl_tracer(Config) -> {ok, _} = dbg:p(self(), [c, timestamp]), {ok, _} = dbg:tp(?MODULE, dummy, []), ok = ?MODULE:dummy(), - [{Self, call, Self, Self, {?MODULE, dummy, []}, undefined, #{}}] = flush(), + [{Self, call, Self, Self, {?MODULE, dummy, []}, #{}}] = flush(), ok. %% Test that distributed erl_tracer modules work @@ -997,10 +997,10 @@ distributed_erl_tracer(Config) -> {ok, {?MODULE, RNifProxy}} = dbg:get_tracer(RNode), LCall = spawn_link(LNode, fun() -> ?MODULE:dummy() end), - [{LCall, call, LNifProxy, LCall, {?MODULE, dummy, []}, undefined, #{}}] = flush(), + [{LCall, call, LNifProxy, LCall, {?MODULE, dummy, []}, #{}}] = flush(), RCall = spawn_link(RNode, fun() -> ?MODULE:dummy() end), - [{RCall, call, RNifProxy, RCall, {?MODULE, dummy, []}, undefined, #{}}] = flush(), + [{RCall, call, RNifProxy, RCall, {?MODULE, dummy, []}, #{}}] = flush(), ok. @@ -1018,7 +1018,7 @@ load_nif(Config) -> enabled(_, _, _) -> erlang:nif_error(nif_not_loaded). -trace(_, _, _, _, _, _) -> +trace(_, _, _, _, _) -> erlang:nif_error(nif_not_loaded). %% diff --git a/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c b/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c index 45f14938c6..ecdee7e3a2 100644 --- a/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c +++ b/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c @@ -36,7 +36,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ErlNifFunc nif_funcs[] = { {"enabled", 3, enabled}, - {"trace", 6, trace} + {"trace", 5, trace} }; ERL_NIF_INIT(dbg_SUITE, nif_funcs, load, NULL, upgrade, unload) @@ -93,7 +93,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) int state_arity; ErlNifPid self, to; ERL_NIF_TERM *tuple, msg; - ASSERT(argc == 6); + ASSERT(argc == 5); tuple = enif_alloc(sizeof(ERL_NIF_TERM)*(argc+1)); memcpy(tuple+1,argv,sizeof(ERL_NIF_TERM)*argc); diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl index db80d4c9e3..562f040477 100644 --- a/lib/ssh/src/ssh_message.erl +++ b/lib/ssh/src/ssh_message.erl @@ -50,7 +50,15 @@ -define(Empint(X), (ssh_bits:mpint(X))/binary ). -define(Ebinary(X), ?STRING(X) ). --define(unicode_list(B), unicode:characters_to_list(B)). +ucl(B) -> + try unicode:characters_to_list(B) of + L when is_list(L) -> L; + {error,_Matched,Rest} -> throw({error,{bad_unicode,Rest}}) + catch + _:_ -> throw({error,bad_unicode}) + end. + +-define(unicode_list(B), ucl(B)). encode(#ssh_msg_global_request{ name = Name, diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl index 9910b8f1d7..6894f83547 100644 --- a/lib/ssh/test/ssh_algorithms_SUITE.erl +++ b/lib/ssh/test/ssh_algorithms_SUITE.erl @@ -28,7 +28,7 @@ %% Note: This directive should only be used in test suites. -compile(export_all). --define(TIMEOUT, 10000). +-define(TIMEOUT, 35000). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- @@ -70,10 +70,10 @@ two_way_tags() -> [cipher,mac,compression]. %%-------------------------------------------------------------------- init_per_suite(Config) -> - ct:log("os:getenv(\"HOME\") = ~p~n" - "init:get_argument(home) = ~p", - [os:getenv("HOME"), init:get_argument(home)]), - ct:log("~n~n" + ct:log("~n" + "Environment:~n============~n" + "os:getenv(\"HOME\") = ~p~n" + "init:get_argument(home) = ~p~n~n~n" "OS ssh:~n=======~n~p~n~n~n" "Erl ssh:~n========~n~p~n~n~n" "Installed ssh client:~n=====================~n~p~n~n~n" @@ -82,7 +82,9 @@ init_per_suite(Config) -> " -- Default dh group exchange parameters ({min,def,max}): ~p~n" " -- dh_default_groups: ~p~n" " -- Max num algorithms: ~p~n" - ,[os:cmd("ssh -V"), + ,[os:getenv("HOME"), + init:get_argument(home), + os:cmd("ssh -V"), ssh:default_algorithms(), ssh_test_lib:default_algorithms(sshc), ssh_test_lib:default_algorithms(sshd), @@ -136,14 +138,14 @@ end_per_group(_Alg, Config) -> -init_per_testcase(sshc_simple_exec, Config) -> +init_per_testcase(sshc_simple_exec_os_cmd, Config) -> start_pubkey_daemon([?config(pref_algs,Config)], Config); init_per_testcase(_TC, Config) -> Config. -end_per_testcase(sshc_simple_exec, Config) -> +end_per_testcase(sshc_simple_exec_os_cmd, Config) -> case ?config(srvr_pid,Config) of Pid when is_pid(Pid) -> ssh:stop_daemon(Pid), @@ -154,7 +156,6 @@ end_per_testcase(sshc_simple_exec, Config) -> end_per_testcase(_TC, Config) -> Config. - %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- @@ -221,18 +222,36 @@ interpolate(Is) -> %%-------------------------------------------------------------------- %% Use the ssh client of the OS to connect -sshc_simple_exec(Config) -> +sshc_simple_exec_os_cmd(Config) -> PrivDir = ?config(priv_dir, Config), KnownHosts = filename:join(PrivDir, "known_hosts"), {Host,Port} = ?config(srvr_addr, Config), - Cmd = lists:concat(["ssh -p ",Port, - " -C", - " -o UserKnownHostsFile=",KnownHosts, - " -o StrictHostKeyChecking=no", - " ",Host," 1+1."]), - ct:log("~p",[Cmd]), - OpenSsh = ssh_test_lib:open_port({spawn, Cmd}, [eof,exit_status]), - ssh_test_lib:rcv_expected({data,<<"2\n">>}, OpenSsh, ?TIMEOUT). + Parent = self(), + Client = spawn( + fun() -> + Cmd = lists:concat(["ssh -p ",Port, + " -C" + " -o UserKnownHostsFile=",KnownHosts, + " -o StrictHostKeyChecking=no" + " ",Host," 1+1."]), + Result = os:cmd(Cmd), + ct:log("~p~n = ~p",[Cmd, Result]), + Parent ! {result, self(), Result, "2"} + end), + receive + {result, Client, RawResult, Expect} -> + Lines = string:tokens(RawResult, "\r\n"), + case lists:any(fun(Line) -> Line==Expect end, + Lines) of + true -> + ok; + false -> + ct:log("Bad result: ~p~nExpected: ~p~nMangled result: ~p", [RawResult,Expect,Lines]), + {fail, "Bad result"} + end + after ?TIMEOUT -> + ct:fail("Did not receive answer") + end. %%-------------------------------------------------------------------- %% Connect to the ssh server of the OS @@ -299,7 +318,7 @@ specific_test_cases(Tag, Alg, SshcAlgos, SshdAlgos) -> true -> case ssh_test_lib:ssh_type() of openSSH -> - [sshc_simple_exec]; + [sshc_simple_exec_os_cmd]; _ -> [] end; diff --git a/lib/ssh/test/ssh_benchmark_SUITE.erl b/lib/ssh/test/ssh_benchmark_SUITE.erl index d9be1a32b7..5d8c94be73 100644 --- a/lib/ssh/test/ssh_benchmark_SUITE.erl +++ b/lib/ssh/test/ssh_benchmark_SUITE.erl @@ -184,7 +184,7 @@ openssh_client_shell(Config, Options) -> end, Times), ssh:stop_daemon(ServerPid), ok - after 10000 -> + after 60*1000 -> ssh:stop_daemon(ServerPid), exit(SlavePid, kill), {fail, timeout} @@ -215,6 +215,7 @@ openssh_client_sftp(Config, Options) -> {root, SftpSrcDir}])]}, {failfun, fun ssh_test_lib:failfun/2} | Options]), + ct:pal("ServerPid = ~p",[ServerPid]), ct:sleep(500), Cmd = lists:concat(["sftp", " -b -", @@ -231,7 +232,7 @@ openssh_client_sftp(Config, Options) -> end), receive {SlavePid, _ClientResponse} -> - ct:pal("ClientResponse = ~p",[_ClientResponse]), + ct:pal("ClientResponse = ~p~nServerPid = ~p",[_ClientResponse,ServerPid]), {ok, List} = get_trace_list(TracerPid), %%ct:pal("List=~p",[List]), Times = find_times(List, [channel_open_close]), @@ -260,7 +261,7 @@ openssh_client_sftp(Config, Options) -> end, Times), ssh:stop_daemon(ServerPid), ok - after 10000 -> + after 2*60*1000 -> ssh:stop_daemon(ServerPid), exit(SlavePid, kill), {fail, timeout} @@ -445,10 +446,18 @@ increment({Alg,Sz,T},[]) -> %%% API for the traceing %%% get_trace_list(TracerPid) -> + MonRef = monitor(process, TracerPid), TracerPid ! {get_trace_list,self()}, receive - {trace_list,L} -> {ok, pair_events(lists:reverse(L))} - after 5000 -> {error,no_reply} + {trace_list,L} -> + demonitor(MonRef), + {ok, pair_events(lists:reverse(L))}; + {'DOWN', MonRef, process, TracerPid, Info} -> + {error, {tracer_down,Info}} + + after 3*60*1000 -> + demonitor(MonRef), + {error,no_reply} end. erlang_trace() -> diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl index f6d7be41d6..26fe0935e1 100644 --- a/lib/ssh/test/ssh_sftp_SUITE.erl +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -1,4 +1,4 @@ -%% +% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2005-2016. All Rights Reserved. @@ -93,35 +93,35 @@ groups() -> init_per_group(not_unicode, Config) -> ct:comment("Begin ~p",[grps(Config)]), DataDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), [{user, "Alladin"}, {passwd, "Sesame"}, {data, <<"Hello world!">>}, - {filename, filename:join(PrivDir, "sftp.txt")}, - {testfile, filename:join(PrivDir, "test.txt")}, - {linktest, filename:join(PrivDir, "link_test.txt")}, - {tar_filename, filename:join(PrivDir, "sftp_tar_test.tar")}, - {tar_F1_txt, "f1.txt"}, + {filename, "sftp.txt"}, + {testfile, "test.txt"}, + {linktest, "link_test.txt"}, + {tar_filename, "sftp_tar_test.tar"}, + {tar_F1_txt, "f1.txt"}, {datadir_tar, filename:join(DataDir,"sftp_tar_test_data")} | Config]; init_per_group(unicode, Config) -> - case file:native_name_encoding() of - utf8 -> + case (file:native_name_encoding() == utf8) + andalso ("四" == [22235]) + of + true -> ct:comment("Begin ~p",[grps(Config)]), DataDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), NewConfig = [{user, "åke高兴"}, {passwd, "ärlig日本じん"}, {data, <<"foobar å 一二三四いちにさんち">>}, - {filename, filename:join(PrivDir, "sftp瑞点.txt")}, - {testfile, filename:join(PrivDir, "testハンス.txt")}, - {linktest, filename:join(PrivDir, "link_test語.txt")}, - {tar_filename, filename:join(PrivDir, "sftp_tar_test一二三.tar")}, - {tar_F1_txt, "F一.txt"}, - {tar_F3_txt, "f3.txt"}, - {tar_F4_txt, "g四.txt"}, + {filename, "sftp瑞点.txt"}, + {testfile, "testハンス.txt"}, + {linktest, "link_test語.txt"}, + {tar_filename, "sftp_tar_test一二三.tar"}, + {tar_F1_txt, "F一.txt"}, + {tar_F3_txt, "f3.txt"}, + {tar_F4_txt, "g四.txt"}, {datadir_tar, filename:join(DataDir,"sftp_tar_test_data_高兴")} | lists:foldl(fun(K,Cf) -> lists:keydelete(K,1,Cf) end, Config, @@ -228,8 +228,8 @@ init_per_testcase(sftp_nonexistent_subsystem, Config) -> ]), [{sftpd, Sftpd} | Config]; -init_per_testcase(version_option, Config) -> - prep(Config), +init_per_testcase(version_option, Config0) -> + Config = prepare(Config0), TmpConfig0 = lists:keydelete(watchdog, 1, Config), TmpConfig = lists:keydelete(sftp, 1, TmpConfig0), Dog = ct:timetrap(?default_timeout), @@ -246,8 +246,8 @@ init_per_testcase(version_option, Config) -> Sftp = {ChannelPid, Connection}, [{sftp,Sftp}, {watchdog, Dog} | TmpConfig]; -init_per_testcase(Case, Config0) -> - prep(Config0), +init_per_testcase(Case, Config00) -> + Config0 = prepare(Config00), Config1 = lists:keydelete(watchdog, 1, Config0), Config2 = lists:keydelete(sftp, 1, Config1), Dog = ct:timetrap(2 * ?default_timeout), @@ -279,7 +279,7 @@ init_per_testcase(Case, Config0) -> [{sftp, Sftp}, {watchdog, Dog} | Config2] end, - case catch ?config(remote_tar,Config) of + case catch proplists:get_value(remote_tar,Config) of %% The 'catch' is for the case of Config={skip,...} true -> %% Provide a ChannelPid independent of the sftp-channel already opened. @@ -329,7 +329,7 @@ open_close_file(Server, File, Mode) -> open_close_dir() -> [{doc, "Test API functions opendir/2 and close/2"}]. open_close_dir(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = ?config(sftp_priv_dir, Config), {Sftp, _} = ?config(sftp, Config), FileName = ?config(filename, Config), @@ -351,7 +351,7 @@ read_file(Config) when is_list(Config) -> read_dir() -> [{doc,"Test API function list_dir/2"}]. read_dir(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = ?config(sftp_priv_dir, Config), {Sftp, _} = ?config(sftp, Config), {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir), ct:log("sftp list dir: ~p~n", [Files]). @@ -415,7 +415,7 @@ sftp_read_big_file(Config) when is_list(Config) -> remove_file() -> [{doc,"Test API function delete/2"}]. remove_file(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = ?config(sftp_priv_dir, Config), FileName = ?config(filename, Config), {Sftp, _} = ?config(sftp, Config), @@ -429,7 +429,7 @@ remove_file(Config) when is_list(Config) -> rename_file() -> [{doc, "Test API function rename_file/2"}]. rename_file(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = ?config(sftp_priv_dir, Config), FileName = ?config(filename, Config), NewFileName = ?config(testfile, Config), @@ -449,7 +449,7 @@ rename_file(Config) when is_list(Config) -> mk_rm_dir() -> [{doc,"Test API functions make_dir/2, del_dir/2"}]. mk_rm_dir(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = ?config(sftp_priv_dir, Config), {Sftp, _} = ?config(sftp, Config), DirName = filename:join(PrivDir, "test"), @@ -945,7 +945,7 @@ aes_ctr_stream_crypto_tar(Config) -> %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- -prep(Config) -> +oldprep(Config) -> DataDir = ?config(data_dir, Config), TestFile = ?config(filename, Config), TestFile1 = ?config(testfile, Config), @@ -965,6 +965,36 @@ prep(Config) -> ok = file:write_file_info(TestFile, FileInfo#file_info{mode = Mode}). +prepare(Config0) -> + PrivDir = proplists:get_value(priv_dir, Config0), + Dir = filename:join(PrivDir, random_chars(10)), + file:make_dir(Dir), + Keys = [filename, + testfile, + linktest, + tar_filename], + Config1 = foldl_keydelete(Keys, Config0), + Config2 = lists:foldl(fun({Key,Name}, ConfAcc) -> + [{Key, filename:join(Dir,Name)} | ConfAcc] + end, + Config1, + lists:zip(Keys, [proplists:get_value(K,Config0) || K<-Keys])), + + DataDir = proplists:get_value(data_dir, Config2), + FilenameSrc = filename:join(DataDir, "sftp.txt"), + FilenameDst = proplists:get_value(filename, Config2), + {ok,_} = file:copy(FilenameSrc, FilenameDst), + [{sftp_priv_dir,Dir} | Config2]. + + +random_chars(N) -> [crypto:rand_uniform($a,$z) || _<-lists:duplicate(N,x)]. + +foldl_keydelete(Keys, L) -> + lists:foldl(fun(K,E) -> lists:keydelete(K,1,E) end, + L, + Keys). + + chk_tar(Items, Config) -> chk_tar(Items, Config, []). diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl index c6541461a1..a1291146e4 100644 --- a/lib/ssh/test/ssh_test_lib.erl +++ b/lib/ssh/test/ssh_test_lib.erl @@ -655,17 +655,48 @@ sshc(Tag) -> ). ssh_type() -> - case os:find_executable("ssh") of - false -> not_found; - _ -> - case os:cmd("ssh -V") of - "OpenSSH" ++ _ -> - openSSH; - Str -> - ct:log("ssh client ~p is unknown",[Str]), - unknown - end - end. + Parent = self(), + Pid = spawn(fun() -> + Parent ! {ssh_type,self(),ssh_type1()} + end), + MonitorRef = monitor(process, Pid), + receive + {ssh_type, Pid, Result} -> + demonitor(MonitorRef), + Result; + {'DOWN', MonitorRef, process, Pid, _Info} -> + ct:log("~p:~p Process DOWN",[?MODULE,?LINE]), + not_found + after + 10000 -> + ct:log("~p:~p Timeout",[?MODULE,?LINE]), + demonitor(MonitorRef), + not_found + end. + + +ssh_type1() -> + try + case os:find_executable("ssh") of + false -> + ct:log("~p:~p Executable \"ssh\" not found",[?MODULE,?LINE]), + not_found; + _ -> + case os:cmd("ssh -V") of + "OpenSSH" ++ _ -> + openSSH; + Str -> + ct:log("ssh client ~p is unknown",[Str]), + unknown + end + end + catch + Class:Exception -> + ct:log("~p:~p Exception ~p:~p",[?MODULE,?LINE,Class,Exception]), + not_found + end. + + algo_intersection([], _) -> []; algo_intersection(_, []) -> []; diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 154664d855..33ece8f769 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -417,10 +417,24 @@ fun(srp, Username :: string(), UserState :: term()) -> If set to <c>false</c>, it disables the block cipher padding check to be able to interoperate with legacy software.</p></item> - </taglist> - <warning><p>Using <c>{padding_check, boolean()}</c> makes TLS vulnerable to the Poodle attack.</p></warning> + + <tag><c>{beast_mitigation, one_n_minus_one | zero_n | disabled}</c></tag> + <item><p>Affects SSL-3.0 and TLS-1.0 connections only. Used to change the BEAST + mitigation strategy to interoperate with legacy software. + Defaults to <c>one_n_minus_one</c></p>. + + <p><c>one_n_minus_one</c> - Perform 1/n-1 BEAST mitigation.</p> + + <p><c>zero_n</c> - Perform 0/n BEAST mitigation.</p> + + <p><c>disabled</c> - Disable BEAST mitigation.</p></item> + + <warning><p>Using <c>{beast_mitigation, disabled}</c> makes SSL or TLS + vulnerable to the BEAST attack.</p></warning> + </taglist> + </section> <section> diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index daa06a40fc..e50ffdbfe6 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -94,7 +94,10 @@ to <c>ssl:connect/[2,3]</c> and <c>ssl:listen/2</c>.</p></item> <tag><c><![CDATA[session_lifetime = integer() <optional>]]></c></tag> - <item><p>Maximum lifetime of the session data in seconds.</p></item> + <item><p>Maximum lifetime of the session data in seconds. Defaults to 24 hours which is the maximum + recommended lifetime by <url href="http://www.ietf.org/rfc/5246rfc.txt">RFC 5246</url>. However + sessions may be invalidated earlier due to the maximum limitation of the session cache table. + </p></item> <tag><c><![CDATA[session_cb = atom() <optional>]]></c></tag> <item><p>Name of the session cache callback module that implements @@ -107,16 +110,24 @@ in the session cache callback module. Defaults to <c>[]</c>.</p></item> <tag><c><![CDATA[session_cache_client_max = integer() <optional>]]></c><br/> - <c><![CDATA[session_cache_server_max = integer() <optional>]]></c></tag> - <item><p>Limits the growth of the clients/servers session cache, - if the maximum number of sessions is reached, the current cache entries will - be invalidated regardless of their remaining lifetime. Defaults to 1000. - </p></item> + <item><p>Limits the growth of the clients session cache, that is + how many sessions towards servers that are cached to be used by + new client connections. If the maximum number of sessions is + reached, the current cache entries will be invalidated + regardless of their remaining lifetime. Defaults to + 1000.</p></item> + + <tag> <c><![CDATA[session_cache_server_max = integer() <optional>]]></c></tag> + <item><p>Limits the growth of the servers session cache, that is + how many client sessions are cached by the server. If the + maximum number of sessions is reached, the current cache entries + will be invalidated regardless of their remaining + lifetime. Defaults to 1000.</p></item> <tag><c><![CDATA[ssl_pem_cache_clean = integer() <optional>]]></c></tag> <item> <p> - Number of milliseconds between PEM cache validations. + Number of milliseconds between PEM cache validations. Defaults to 2 minutes. </p> <seealso marker="ssl#clear_pem_cache-0">ssl:clear_pem_cache/0</seealso> @@ -131,7 +142,7 @@ shutdown gracefully. Defaults to 5000 milliseconds. </p> </item> - + </tag> </taglist> </section> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 82d6faee42..60a61bc901 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -416,7 +416,8 @@ encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, {CbModule, DataTag, CloseTag, ErrorTag}) -> - ConnectionStates = ssl_record:init_connection_states(Role), + #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions, + ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation), SessionCacheCb = case application:get_env(ssl, session_cb) of {ok, Cb} when is_atom(Cb) -> diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index 3481e89af0..0da4b3587f 100644 --- a/lib/ssl/src/inet_tls_dist.erl +++ b/lib/ssl/src/inet_tls_dist.erl @@ -87,7 +87,8 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> case inet:getaddr(Address, Driver:family()) of {ok, Ip} -> Timer = dist_util:start_timer(SetupTime), - case erl_epmd:port_please(Name, Ip) of + ErlEpmd = net_kernel:epmd_module(), + case ErlEpmd:port_please(Name, Ip) of {port, TcpPort, Version} -> ?trace("port_please(~p) -> version ~p~n", [Node,Version]), diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 51732b4a59..5dc2e583a5 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -725,6 +725,7 @@ handle_options(Opts0, Role) -> server, Role), protocol = proplists:get_value(protocol, Opts, tls), padding_check = proplists:get_value(padding_check, Opts, true), + beast_mitigation = handle_option(beast_mitigation, Opts, one_n_minus_one), fallback = handle_option(fallback, Opts, proplists:get_value(fallback, Opts, default_option_role(client, @@ -746,7 +747,7 @@ handle_options(Opts0, Role) -> alpn_preferred_protocols, next_protocols_advertised, client_preferred_next_protocols, log_alert, server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache, - fallback, signature_algs], + fallback, signature_algs, beast_mitigation], SockOpts = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) @@ -986,6 +987,10 @@ validate_option(crl_check, Value) when (Value == best_effort) or (Value == peer) Value; validate_option(crl_cache, {Cb, {_Handle, Options}} = Value) when is_atom(Cb) and is_list(Options) -> Value; +validate_option(beast_mitigation, Value) when Value == one_n_minus_one orelse + Value == zero_n orelse + Value == disabled -> + Value; validate_option(Opt, Value) -> throw({error, {options, {Opt, Value}}}). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 076e663cd4..dddcbdeeda 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -133,6 +133,9 @@ %% the client? honor_cipher_order = false :: boolean(), padding_check = true :: boolean(), + %%Should we use 1/n-1 or 0/n splitting to mitigate BEAST, or disable + %%mitigation entirely? + beast_mitigation = one_n_minus_one :: one_n_minus_one | zero_n | disabled, fallback = false :: boolean(), crl_check :: boolean() | peer | best_effort, crl_cache, diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 866bfcef7e..0a086f5eeb 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -30,7 +30,7 @@ -include("ssl_alert.hrl"). %% Connection state handling --export([init_connection_states/1, +-export([init_connection_states/2, current_connection_state/2, pending_connection_state/2, activate_pending_connection_state/2, set_security_params/3, @@ -62,15 +62,16 @@ %%==================================================================== %%-------------------------------------------------------------------- --spec init_connection_states(client | server) -> #connection_states{}. +-spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled ) -> + #connection_states{}. %% %% Description: Creates a connection_states record with appropriate %% values for the initial SSL connection setup. %%-------------------------------------------------------------------- -init_connection_states(Role) -> +init_connection_states(Role, BeastMitigation) -> ConnectionEnd = record_protocol_role(Role), - Current = initial_connection_state(ConnectionEnd), - Pending = empty_connection_state(ConnectionEnd), + Current = initial_connection_state(ConnectionEnd, BeastMitigation), + Pending = empty_connection_state(ConnectionEnd, BeastMitigation), #connection_states{current_read = Current, pending_read = Pending, current_write = Current, @@ -119,9 +120,10 @@ activate_pending_connection_state(States = read) -> NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current), sequence_number = 0}, + BeastMitigation = Pending#connection_state.beast_mitigation, SecParams = Pending#connection_state.security_parameters, ConnectionEnd = SecParams#security_parameters.connection_end, - EmptyPending = empty_connection_state(ConnectionEnd), + EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation), SecureRenegotation = NewCurrent#connection_state.secure_renegotiation, NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation}, States#connection_states{current_read = NewCurrent, @@ -134,9 +136,10 @@ activate_pending_connection_state(States = write) -> NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current), sequence_number = 0}, + BeastMitigation = Pending#connection_state.beast_mitigation, SecParams = Pending#connection_state.security_parameters, ConnectionEnd = SecParams#security_parameters.connection_end, - EmptyPending = empty_connection_state(ConnectionEnd), + EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation), SecureRenegotation = NewCurrent#connection_state.secure_renegotiation, NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation}, States#connection_states{current_write = NewCurrent, @@ -314,12 +317,13 @@ set_pending_cipher_state(#connection_states{pending_read = Read, encode_handshake(Frag, Version, #connection_states{current_write = #connection_state{ + beast_mitigation = BeastMitigation, security_parameters = #security_parameters{bulk_cipher_algorithm = BCA}}} = ConnectionStates) -> case iolist_size(Frag) of N when N > ?MAX_PLAIN_TEXT_LENGTH -> - Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH, Version, BCA), + Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation), encode_iolist(?HANDSHAKE, Data, Version, ConnectionStates); _ -> encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates) @@ -352,10 +356,11 @@ encode_change_cipher_spec(Version, ConnectionStates) -> %%-------------------------------------------------------------------- encode_data(Frag, Version, #connection_states{current_write = #connection_state{ + beast_mitigation = BeastMitigation, security_parameters = #security_parameters{bulk_cipher_algorithm = BCA}}} = ConnectionStates) -> - Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA), + Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation), encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates). uncompress(?NULL, Data, CS) -> @@ -447,9 +452,10 @@ decipher_aead(Version, CipherFragment, %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -empty_connection_state(ConnectionEnd) -> +empty_connection_state(ConnectionEnd, BeastMitigation) -> SecParams = empty_security_params(ConnectionEnd), - #connection_state{security_parameters = SecParams}. + #connection_state{security_parameters = SecParams, + beast_mitigation = BeastMitigation}. empty_security_params(ConnectionEnd = ?CLIENT) -> #security_parameters{connection_end = ConnectionEnd, @@ -478,10 +484,11 @@ record_protocol_role(client) -> record_protocol_role(server) -> ?SERVER. -initial_connection_state(ConnectionEnd) -> +initial_connection_state(ConnectionEnd, BeastMitigation) -> #connection_state{security_parameters = initial_security_params(ConnectionEnd), - sequence_number = 0 + sequence_number = 0, + beast_mitigation = BeastMitigation }. initial_security_params(ConnectionEnd) -> @@ -506,11 +513,17 @@ encode_iolist(Type, Data, Version, ConnectionStates0) -> %% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are %% not vulnerable to this attack. -split_bin(<<FirstByte:8, Rest/binary>>, ChunkSize, Version, BCA) when +split_bin(<<FirstByte:8, Rest/binary>>, ChunkSize, Version, BCA, one_n_minus_one) when BCA =/= ?RC4 andalso ({3, 1} == Version orelse {3, 0} == Version) -> do_split_bin(Rest, ChunkSize, [[FirstByte]]); -split_bin(Bin, ChunkSize, _, _) -> +%% 0/n splitting countermeasure for clients that are incompatible with 1/n-1 +%% splitting. +split_bin(Bin, ChunkSize, Version, BCA, zero_n) when + BCA =/= ?RC4 andalso ({3, 1} == Version orelse + {3, 0} == Version) -> + do_split_bin(Bin, ChunkSize, [[<<>>]]); +split_bin(Bin, ChunkSize, _, _, _) -> do_split_bin(Bin, ChunkSize, []). do_split_bin(<<>>, _, Acc) -> diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index d34d144343..87fde35258 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -40,7 +40,9 @@ %% RFC 5746 secure_renegotiation, client_verify_data, - server_verify_data + server_verify_data, + %% How to do BEAST mitigation? + beast_mitigation }). -record(connection_states, { diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index 4651687fe6..2e308a15b7 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -116,7 +116,8 @@ handle_call({listen, Driver, Name}, _From, State) -> {ok, TcpAddress} = get_tcp_address(Socket), {ok, WorldTcpAddress} = get_tcp_address(World), {_,Port} = WorldTcpAddress#net_address.address, - case erl_epmd:register_node(Name, Port) of + ErlEpmd = net_kernel:epmd_module(), + case ErlEpmd:register_node(Name, Port) of {ok, Creation} -> {reply, {ok, {Socket, TcpAddress, Creation}}, State#state{listen={Socket, World}}}; diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 40f3eea527..91903b4a1f 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -496,7 +496,8 @@ decode_alerts(Bin) -> initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User, {CbModule, DataTag, CloseTag, ErrorTag}) -> - ConnectionStates = ssl_record:init_connection_states(Role), + #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions, + ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation), SessionCacheCb = case application:get_env(ssl, session_cb) of {ok, Cb} when is_atom(Cb) -> diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index d1162ab4a5..cd06b97ab2 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -236,7 +236,10 @@ error_handling_tests_tls()-> rizzo_tests() -> [rizzo, - no_rizzo_rc4]. + no_rizzo_rc4, + rizzo_one_n_minus_one, + rizzo_zero_n, + rizzo_disabled]. %%-------------------------------------------------------------------- init_per_suite(Config0) -> @@ -346,10 +349,27 @@ init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites; ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 30}), Config; + init_per_testcase(rizzo, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 40}), Config; + +init_per_testcase(rizzo_one_n_minus_one, Config) -> + ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), + ct:timetrap({seconds, 40}), + rizzo_add_mitigation_option(one_n_minus_one, Config); + +init_per_testcase(rizzo_zero_n, Config) -> + ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), + ct:timetrap({seconds, 40}), + rizzo_add_mitigation_option(zero_n, Config); + +init_per_testcase(rizzo_disabled, Config) -> + ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), + ct:timetrap({seconds, 40}), + rizzo_add_mitigation_option(disabled, Config); + init_per_testcase(prf, Config) -> ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), ct:timetrap({seconds, 40}), @@ -3488,6 +3508,36 @@ no_rizzo_rc4(Config) when is_list(Config) -> run_send_recv_rizzo(Ciphers, Config, Version, {?MODULE, send_recv_result_active_no_rizzo, []}). +rizzo_one_n_minus_one() -> + [{doc,"Test that the 1/n-1-split mitigation of Rizzo/Dungon attack can be explicitly selected"}]. + +rizzo_one_n_minus_one(Config) when is_list(Config) -> + Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128], + Prop = proplists:get_value(tc_group_properties, Config), + Version = proplists:get_value(name, Prop), + run_send_recv_rizzo(Ciphers, Config, Version, + {?MODULE, send_recv_result_active_rizzo, []}). + +rizzo_zero_n() -> + [{doc,"Test that the 0/n-split mitigation of Rizzo/Dungon attack can be explicitly selected"}]. + +rizzo_zero_n(Config) when is_list(Config) -> + Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128], + Prop = proplists:get_value(tc_group_properties, Config), + Version = proplists:get_value(name, Prop), + run_send_recv_rizzo(Ciphers, Config, Version, + {?MODULE, send_recv_result_active_no_rizzo, []}). + +rizzo_disabled() -> + [{doc,"Test that the mitigation of Rizzo/Dungon attack can be explicitly disabled"}]. + +rizzo_disabled(Config) when is_list(Config) -> + Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128], + Prop = proplists:get_value(tc_group_properties, Config), + Version = proplists:get_value(name, Prop), + run_send_recv_rizzo(Ciphers, Config, Version, + {?MODULE, send_recv_result_active_no_rizzo, []}). + %%-------------------------------------------------------------------- new_server_wants_peer_cert() -> [{doc, "Test that server configured to do client certification does" @@ -3960,6 +4010,18 @@ renegotiate_rejected(Socket) -> ssl:send(Socket, "Hello world"), ok. +rizzo_add_mitigation_option(Value, Config) -> + lists:foldl(fun(Opt, Acc) -> + case proplists:get_value(Opt, Acc) of + undefined -> Acc; + C -> + N = lists:keystore(beast_mitigation, 1, C, + {beast_mitigation, Value}), + lists:keystore(Opt, 1, Acc, {Opt, N}) + end + end, Config, + [client_opts, client_dsa_opts, server_opts, server_dsa_opts, + server_ecdsa_opts, server_ecdh_rsa_opts]). new_config(PrivDir, ServerOpts0) -> CaCertFile = proplists:get_value(cacertfile, ServerOpts0), diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index 7a17226e46..fe9df601eb 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -46,7 +46,7 @@ terminate/2,code_change/3]). -export([make_crypto_key/2, get_crypto_key/1]). %Utilities used by compiler --export_type([attrib_entry/0, compinfo_entry/0, labeled_entry/0]). +-export_type([attrib_entry/0, compinfo_entry/0, labeled_entry/0, label/0]). -import(lists, [append/1, delete/2, foreach/2, keysort/2, member/2, reverse/1, sort/1, splitwith/2]). diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl index ef54076ee3..a6ae398d03 100644 --- a/lib/stdlib/src/erl_compile.erl +++ b/lib/stdlib/src/erl_compile.erl @@ -60,6 +60,7 @@ compile_cmdline() -> _ -> my_halt(2) end. +-spec my_halt(_) -> no_return(). my_halt(Reason) -> erlang:halt(Reason). diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index b8ce311c35..f53b0e2246 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -906,6 +906,7 @@ anno(L) -> fatal(Str) -> throw(Str). +-spec my_halt(_) -> no_return(). my_halt(Reason) -> erlang:halt(Reason). diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl index 4a19603ec2..3dc1848550 100644 --- a/lib/stdlib/src/proc_lib.erl +++ b/lib/stdlib/src/proc_lib.erl @@ -479,18 +479,12 @@ trans_init(gen,init_it,[gen_server,_,_,supervisor_bridge,[Module|_],_]) -> {supervisor_bridge,Module,1}; trans_init(gen,init_it,[gen_server,_,_,_,supervisor_bridge,[Module|_],_]) -> {supervisor_bridge,Module,1}; -trans_init(gen,init_it,[GenMod,_,_,Module,_,_]) - when GenMod =:= gen_server; - GenMod =:= gen_statem; - GenMod =:= gen_fsm -> - {Module,init,1}; -trans_init(gen,init_it,[GenMod,_,_,_,Module|_]) - when GenMod =:= gen_server; - GenMod =:= gen_statem; - GenMod =:= gen_fsm -> - {Module,init,1}; trans_init(gen,init_it,[gen_event|_]) -> {gen_event,init_it,6}; +trans_init(gen,init_it,[_GenMod,_,_,Module,_,_]) when is_atom(Module) -> + {Module,init,1}; +trans_init(gen,init_it,[_GenMod,_,_,_,Module|_]) when is_atom(Module) -> + {Module,init,1}; trans_init(M, F, A) when is_atom(M), is_atom(F) -> {M,F,length(A)}. diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index 38b764541a..a594c66fa7 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -246,7 +246,7 @@ check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> check_childspecs(X) -> {error, {badarg, X}}. %%%----------------------------------------------------------------- -%%% Called by timer:apply_after from restart/2 +%%% Called by restart/2 -spec try_again_restart(SupRef, Child) -> ok when SupRef :: sup_ref(), Child :: child_id() | pid(). @@ -571,8 +571,8 @@ count_child(#child{pid = Pid, child_type = supervisor}, end. -%%% If a restart attempt failed, this message is sent via -%%% timer:apply_after(0,...) in order to give gen_server the chance to +%%% If a restart attempt failed, this message is cast +%%% from restart/2 in order to give gen_server the chance to %%% check it's inbox before trying again. -spec handle_cast({try_again_restart, child_id() | pid()}, state()) -> {'noreply', state()} | {stop, shutdown, state()}. @@ -790,16 +790,10 @@ restart(Child, State) -> Id = if ?is_simple(State) -> Child#child.pid; true -> Child#child.name end, - {ok, _TRef} = timer:apply_after(0, - ?MODULE, - try_again_restart, - [self(),Id]), + ok = try_again_restart(self(), Id), {ok,NState2}; {try_again, NState2, #child{name=ChName}} -> - {ok, _TRef} = timer:apply_after(0, - ?MODULE, - try_again_restart, - [self(),ChName]), + ok = try_again_restart(self(), ChName), {ok,NState2}; Other -> Other |