diff options
Diffstat (limited to 'lib/ssh/test/ssh_benchmark_SUITE.erl')
-rw-r--r-- | lib/ssh/test/ssh_benchmark_SUITE.erl | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/lib/ssh/test/ssh_benchmark_SUITE.erl b/lib/ssh/test/ssh_benchmark_SUITE.erl new file mode 100644 index 0000000000..0d7239c5b5 --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE.erl @@ -0,0 +1,295 @@ +%%%------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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% +%% +-module(ssh_benchmark_SUITE). +-compile(export_all). + +-include_lib("common_test/include/ct_event.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-include_lib("ssh/src/ssh.hrl"). +-include_lib("ssh/src/ssh_connect.hrl"). + +suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. +%%suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [{group, opensshc_erld} +%% {group, erlc_opensshd} + ]. + +groups() -> + [{opensshc_erld, [{repeat, 3}], [openssh_client_shell]}, + {erlc_opensshd, [{repeat, 3}], [erl_shell]} + ]. + + +init_per_suite(Config) -> + catch ssh:stop(), + catch crypto:stop(), + try + ok = crypto:start(), + ok = ssh:start(), + {ok,TracerPid} = erlang_trace(), + [{tracer_pid,TracerPid} | Config] + catch + C:E -> + {skip, io_lib:format("Couldn't start ~p:~p",[C,E])} + end. + +end_per_suite(_Config) -> + catch ssh:stop(), + catch crypto:stop(), + ok. + + + +init_per_group(opensshc_erld, Config) -> + case ssh_test_lib:ssh_type() of + openSSH -> + DataDir = ?config(data_dir, Config), + UserDir = ?config(priv_dir, Config), + ssh_test_lib:setup_dsa(DataDir, UserDir), + ssh_test_lib:setup_rsa(DataDir, UserDir), + ssh_test_lib:setup_ecdsa("256", DataDir, UserDir), + [{c_kexs, ssh_test_lib:sshc(kex)}, + {c_ciphers, ssh_test_lib:sshc(cipher)} + | Config]; + _ -> + {skip, "No OpenSsh client found"} + end; + +init_per_group(erlc_opensshd, _) -> + {skip, "Group erlc_opensshd not implemented"}; + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, _Config) -> + ok. + + +init_per_testcase(_Func, Conf) -> + Conf. + +end_per_testcase(_Func, _Conf) -> + ok. + +%%%================================================================ +openssh_client_shell(Config) -> + SystemDir = ?config(data_dir, Config), + UserDir = ?config(priv_dir, Config), + KnownHosts = filename:join(UserDir, "known_hosts"), + + {ok, TracerPid} = erlang_trace(), + {ServerPid, _Host, Port} = + ssh_test_lib:daemon([{system_dir, SystemDir}, + {public_key_alg, ssh_dsa}, + {failfun, fun ssh_test_lib:failfun/2}]), + ct:sleep(500), + + Data = lists:duplicate(100000, $a), + Cmd = lists:concat(["ssh -p ",Port, + " -o UserKnownHostsFile=", KnownHosts, + " -o \"StrictHostKeyChecking no\"", + " localhost '\"",Data,"\"'."]), +%% ct:pal("Cmd ="++Cmd), + + Parent = self(), + SlavePid = spawn(fun() -> + Parent ! {self(),os:cmd(Cmd)} + end), + receive + {SlavePid, ClientResponse} -> +%% ct:pal("ClientResponse = ~p",[ClientResponse]), + {ok, List} = get_trace_list(TracerPid), + Times = find_times(List), + Algs = proplists:get_value(algorithms, List, #alg{}), + ct:pal("List = ~p~n~nAlgorithms = ~p~n~nTimes = ~p",[List,Algs,Times]), + lists:foreach( + fun({Tag0,MicroSeconds,Unit}) -> + Tag = case Tag0 of + {A,B} -> lists:concat([A," ",B]); + _ when is_list(Tag0) -> lists:concat(Tag0); + _ when is_atom(Tag0) -> Tag0 + end, + DataName = + ["Erl server ",Tag,sp(algo(Tag,Algs))," [",Unit,"]"], + EventData = [{value, MicroSeconds}, + {suite, ?MODULE}, + {name, lists:concat(DataName)} + ], + ct:pal("ct_event:notify ~p",[EventData]), + ct_event:notify(#event{name = benchmark_data, + data = EventData}) + end, Times), + ssh:stop_daemon(ServerPid), + ok + after 10000 -> + ssh:stop_daemon(ServerPid), + exit(SlavePid, kill), + {fail, timeout} + end. + + +algo(kex, #alg{kex=Alg} ) -> Alg; +algo(_, _) -> "". + +sp("") -> ""; +sp(A) -> lists:concat([" ",A]). + +%%%================================================================ +find_times(L) -> + [{accept_to_hello, find_time([tcp_accept, + {send,hello}], L, [])/1000, + millisec}, + {kex, find_time([{send,hello}, + {send,ssh_msg_newkeys}], L, []), + microsec}, + {kex_to_auth, find_time([{send,ssh_msg_newkeys}, + {recv,ssh_msg_userauth_request}], L, []), + microsec}, + {auth, find_time([{recv,ssh_msg_userauth_request}, + {send,ssh_msg_userauth_success}], L, []), + microsec}, + {to_prompt, find_time([tcp_accept, + {recv,{ssh_msg_channel_request,"env"}}], L, []), + microsec} + + | alg_times([encrypt,decrypt], L) + ]. + + +find_time([Event|Events], [{Event,T}|TraceList], Ts) -> + %% Important that the first one found is used! + find_time(Events, TraceList, [T|Ts]); +find_time([], _, [T1,T0]) -> + now2micro_sec(now_diff(T1,T0)); +find_time(Events, [_|TraceList], Ts) -> + find_time(Events, TraceList, Ts); +find_time(_, [], _Ts) -> + throw({error,not_found}). + + + +alg_times(Ops, L) -> + OpAlgs = lists:usort([{Op,Alg} || Op <- Ops, + {{{Op,Alg},_,_},_} <- L]), + [begin + {[Op,"(",Alg,")"], + sum_times(OpAlg, L, 0, 0), + "microsec/kbyte" + } + end || {Op,Alg} = OpAlg <- OpAlgs]. + + +sum_times(T, [{{T,start,Id={_,Nbytes}},TS0}|Events], SumBytes, SumMicroSec) -> + TS1 = proplists:get_value({T,stop,Id}, Events), + sum_times(T, Events, SumBytes+Nbytes, SumMicroSec+now2micro_sec(now_diff(TS1,TS0))); +sum_times(T, [_|Events], SumBytes, SumMicroSec) -> + sum_times(T, Events, SumBytes, SumMicroSec); +sum_times(T, [], SumBytes, SumMicroSec) -> + round(1024*SumMicroSec / SumBytes). % Microseconds per 1k bytes. + +%%%---------------------------------------------------------------- +%%% +%%% API for the traceing +%%% +get_trace_list(TracerPid) -> + TracerPid ! {get_trace_list,self()}, + receive + {trace_list,L} -> {ok,lists:reverse(L)} + after 5000 -> {error,no_reply} + end. + +erlang_trace() -> + TracerPid = spawn(fun trace_loop/0), + 0 = erlang:trace(new, true, [call,timestamp,{tracer,TracerPid}]), + [init_trace(MFA, TP) + || {MFA,TP} <- [{{ssh_acceptor,handle_connection,5}, []}, + {{ssh_connection_handler,hello,2}, []}, + {{ssh_message,encode,1}, []}, + {{ssh_message,decode,1}, [{['_'], [], [{return_trace}]}]}, + {{ssh_transport,select_algorithm,3}, [{['_','_','_'], [], [{return_trace}]}]}, + {{ssh_transport,encrypt,2}, [{['_','_'], [], [{return_trace}]}]}, + {{ssh_transport,decrypt,2}, [{['_','_'], [], [{return_trace}]}]} + ]], + {ok, TracerPid}. + + +%%%---------------- +init_trace(MFA = {Module,_,_}, TP) -> + case code:is_loaded(Module) of + false -> code:load_file(Module); + _ -> ok + end, + erlang:trace_pattern(MFA, TP, [local]). + + +trace_loop() -> + trace_loop([]). + +trace_loop(L) -> + receive + {trace_ts, Pid, call, {M,F,Args}, TS} = Ev -> + cond_pal(Ev), + trace_loop(save_event(call, Pid, {M,F,Args}, TS, L)); + {trace_ts, Pid, return_from, {M,F,Arity}, Ret, TS} = Ev -> + cond_pal(Ev), + trace_loop(save_event(return_from, Pid, {M,F,Arity,Ret}, TS, L)); + {get_trace_list, From} -> + From ! {trace_list, L}, + trace_loop(L) + + ; Other -> io:format('~p got ~p~n',[self(),Other]), trace_loop(L) + end. + +%%cond_pal(Ev) -> ct:pal("~p",[Ev]). +cond_pal(Ev) -> ok. + + +save_event(_Type, _Pid, MFA, TimeStamp, L) -> + try + event_name(MFA) + of + {Tag, 'TS'} -> [{Tag,TimeStamp} | L]; + Val -> [Val | L] + catch + _:_ -> L + end. + +event_name({ssh_acceptor,handle_connection,_}) -> {tcp_accept, 'TS'}; +event_name({ssh_connection_handler,hello,[socket_control|_]}) -> {{send,hello}, 'TS'}; +event_name({ssh_connection_handler,hello,[{version_exchange,_}|_]}) -> {{recv,hello}, 'TS'}; +event_name({ssh_message,encode,[Msg]}) -> {{send,element(1,Msg)}, 'TS'}; +event_name({ssh_message,decode,1, + #ssh_msg_channel_request{request_type=ReqType}}) -> {{recv,{ssh_msg_channel_request,ReqType}}, 'TS'}; +event_name({ssh_message,decode,1,Return}) -> {{recv,element(1,Return)}, 'TS'}; +event_name({ssh_transport,select_algorithm,3,{ok,Algs}}) -> {algorithms,Algs}; +event_name({ssh_transport,encrypt,[S,Data]}) -> {{{encrypt,S#ssh.encrypt},start, {S#ssh.send_sequence,size(Data)}}, 'TS'}; +event_name({ssh_transport,encrypt,2,{S,Ret}}) -> {{{encrypt,S#ssh.encrypt},stop, {S#ssh.send_sequence,size(Ret) }}, 'TS'}; +event_name({ssh_transport,decrypt,[S,Data]}) -> {{{decrypt,S#ssh.decrypt},start, {S#ssh.recv_sequence,size(Data)}}, 'TS'}; +event_name({ssh_transport,decrypt,2,{S,Ret}}) -> {{{decrypt,S#ssh.decrypt},stop, {S#ssh.recv_sequence,size(Ret) }}, 'TS'}. + + +now2sec({A,B,C}) -> A*1000000 + B + C/1000000. + +now2micro_sec({A,B,C}) -> (A*1000000 + B)*1000000 + C. + +now_diff({A1,B1,C1}, {A0,B0,C0}) -> {A1-A0, B1-B0, C1-C0}. + |