aboutsummaryrefslogblamecommitdiffstats
path: root/lib/ssh/test/ssh_benchmark_SUITE.erl
blob: 0d7239c5b5cc7e4bde8a1ca380310208c5cf05c4 (plain) (tree)






































































































































































































































































































                                                                                                                            
%%%-------------------------------------------------------------------
%% %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}.