aboutsummaryrefslogblamecommitdiffstats
path: root/lib/ssh/test/ssh_benchmark_SUITE.erl
blob: 0a116541166e410534289e2372d3757e784f3735 (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_transport.hrl").
-include_lib("ssh/src/ssh_connect.hrl").
-include_lib("ssh/src/ssh_userauth.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 = find_algs(List),
	    ct:pal("Algorithms = ~p~n~nTimes = ~p",[Algs,Times]),
	    lists:foreach(
	      fun({Tag,Value,Unit}) ->
		      EventData =
			  case Tag of
			      {A,B} when A==encrypt ; A==decrypt -> 
				  [{value, Value},
				   {suite, ?MODULE}, 
				   {name, mk_name(["Cipher ",A," ",B," [",Unit,"]"])}
				  ];
			      kex ->
				  [{value, Value},
				   {suite, ?MODULE}, 
				   {name, mk_name(["Erl server kex ",Algs#alg.kex," [",Unit,"]"])}
				  ];
			      _ when is_atom(Tag) ->
				  [{value, Value},
				   {suite, ?MODULE}, 
				   {name, mk_name(["Erl server ",Tag," [",Unit,"]"])}
				  ]
			  end,
		      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.


%%%================================================================
mk_name(Name) -> [char(C) || C <- lists:concat(Name)].

char($-) -> $_;
char(C) -> C.

%%%----------------------------------------------------------------
find_times(L) ->
    Xs =  [accept_to_hello, kex, kex_to_auth, auth, to_prompt],
    [find_time(X,L) || X <- Xs]	++
	crypto_algs_times_sizes([encrypt,decrypt], L).

-record(call, {
	  mfa,
	  pid,
	  t_call,
	  t_return,
	  args,
	  result
	 }).

%%%----------------
-define(send(M), fun(C=#call{mfa = {ssh_message,encode,1},
			     args = [M]}) ->
			 C#call.t_return
		 end).

-define(recv(M), fun(C=#call{mfa = {ssh_message,decode,1},
			     result = M}) ->
			    C#call.t_call
		 end).

find_time(accept_to_hello, L) ->
    [T0,T1] = find([fun(C=#call{mfa = {ssh_acceptor,handle_connection,5}}) ->
			    C#call.t_call
		    end,
		    fun(C=#call{mfa = {ssh_connection_handler,hello,_},
				args = [socket_control|_]}) ->
			    C#call.t_return
		    end
		   ], L, []),
    {accept_to_hello, now2micro_sec(now_diff(T1,T0)), microsec};
find_time(kex, L) ->
    [T0,T1] = find([fun(C=#call{mfa = {ssh_connection_handler,hello,_},
				args = [socket_control|_]}) ->
			    C#call.t_call
		    end,
		    ?send(#ssh_msg_newkeys{})
		    ], L, []),
    {kex, now2micro_sec(now_diff(T1,T0)), microsec};
find_time(kex_to_auth, L) ->
    [T0,T1] = find([?send(#ssh_msg_newkeys{}),
		    ?recv(#ssh_msg_userauth_request{})
		   ], L, []),
    {kex_to_auth, now2micro_sec(now_diff(T1,T0)), microsec};
find_time(auth, L) ->
    [T0,T1] = find([?recv(#ssh_msg_userauth_request{}),
		    ?send(#ssh_msg_userauth_success{})
		   ], L, []),
    {auth, now2micro_sec(now_diff(T1,T0)), microsec};
find_time(to_prompt, L) ->
    [T0,T1] = find([fun(C=#call{mfa = {ssh_acceptor,handle_connection,5}}) ->
			    C#call.t_call
		    end,
		    ?recv(#ssh_msg_channel_request{request_type="env"})
		   ], L, []),
    {to_prompt, now2micro_sec(now_diff(T1,T0)), microsec}.


find([F|Fs], [C|Cs], Acc) when is_function(F,1) ->
    try
	F(C)
    of
	T -> find(Fs, Cs, [T|Acc])
    catch
	_:_ -> find([F|Fs], Cs, Acc)
    end;
find([], _, Acc) ->
    lists:reverse(Acc).


find_algs(L) ->
    {value,#call{result={ok,Algs}}} =
	lists:keysearch({ssh_transport,select_algorithm,3}, #call.mfa, L),
    Algs.

%%%----------------
crypto_algs_times_sizes(EncDecs, L) ->
    Raw = [{_Algorithm = case EncDec of
			     encrypt -> {encrypt,S#ssh.encrypt};
			     decrypt -> {decrypt,S#ssh.decrypt}
			 end, 
	    size(Data),
	    now2micro_sec(now_diff(T1, T0))
	   }
	   || EncDec <- EncDecs,
	      #call{mfa = {ssh_transport,ED,2},
		    args = [S,Data],
		    t_call = T0,
		    t_return = T1} <- L,
	      ED == EncDec
	  ],
    [{Alg, round(1024*Time/Size), "microsec per kbyte"}  % Microseconds per 1k bytes.
     || {Alg,Size,Time} <- lists:foldl(fun increment/2, [], Raw)].

increment({Alg,Sz,T}, [{Alg,SumSz,SumT}|Acc]) ->
    [{Alg,SumSz+Sz,SumT+T} | Acc];
increment(Spec, [X|Acc]) ->
    [X | increment(Spec,Acc)]; % Not so many Alg, 2 or 3
increment({Alg,Sz,T},[]) ->
    [{Alg,Sz,T}].

%%%----------------------------------------------------------------
%%%
%%% API for the traceing
%%% 
get_trace_list(TracerPid) ->
    TracerPid ! {get_trace_list,self()},
    receive
	{trace_list,L} -> {ok, pair_events(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))
     || MFA <- [{ssh_acceptor,handle_connection,5},
		{ssh_connection_handler,hello,2},
		{ssh_message,encode,1},
		{ssh_message,decode,1},
		{ssh_transport,select_algorithm,3},
		{ssh_transport,encrypt,2},
		{ssh_transport,decrypt,2}
	       ]],
    {ok, TracerPid}.

tp({_M,_F,Arity}) ->
    [{lists:duplicate(Arity,'_'), [], [{return_trace}]}].

%%%----------------------------------------------------------------
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
	{get_trace_list, From} ->
	    From ! {trace_list, L},
	    trace_loop(L);
	Ev ->
	    trace_loop([Ev|L])
    end.

pair_events(L) ->
    pair_events(L, []).

pair_events([{trace_ts,Pid,call,{M,F,Args},TS0} | L], Acc) ->
    Arity = length(Args),
    {ReturnValue,TS1} = find_return(Pid, {M,F,Arity}, L),
    pair_events(L, [#call{mfa = {M,F,Arity},
			  pid = Pid,
			  t_call = TS0,
			  t_return = TS1,
			  args = Args,
			  result = ReturnValue} | Acc]);
pair_events([_|L], Acc) ->
    pair_events(L, Acc);
pair_events([], Acc) ->
    lists:reverse(Acc).


find_return(Pid, MFA, 
	    [{trace_ts, Pid, return_from, MFA, ReturnValue, TS}|_]) ->
    {ReturnValue, TS};
find_return(Pid, MFA, [_|L]) ->
    find_return(Pid, MFA, L);
find_return(_, _, []) ->
    {undefined, undefined}.

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