1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 1997-2013. All Rights Reserved.
    5: %% 
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %% 
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %% 
   17: %% %CopyrightEnd%
   18: 
   19: -module(efile_SUITE).
   20: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   21: 	 init_per_group/2,end_per_group/2]).
   22: -export([iter_max_files/1, async_dist/1]).
   23: 
   24: -export([do_iter_max_files/2, do_async_dist/1]).
   25: 
   26: -include_lib("test_server/include/test_server.hrl").
   27: 
   28: suite() -> [{ct_hooks,[ts_install_cth]}].
   29: 
   30: all() -> 
   31:     [iter_max_files, async_dist].
   32: 
   33: groups() -> 
   34:     [].
   35: 
   36: init_per_suite(Config) ->
   37:     Config.
   38: 
   39: end_per_suite(_Config) ->
   40:     ok.
   41: 
   42: init_per_group(_GroupName, Config) ->
   43:     Config.
   44: 
   45: end_per_group(_GroupName, Config) ->
   46:     Config.
   47: 
   48: do_async_dist(Dir) ->
   49:     X = 100,
   50:     AT = erlang:system_info(thread_pool_size),
   51:     Keys = file_keys(Dir,AT*X,[],[]),
   52:     Tab = ets:new(x,[ordered_set]),
   53:     [ ets:insert(Tab,{N,0}) || N <- lists:seq(0,AT-1) ],
   54:     [ ets:update_counter(Tab,(N rem AT),1) || N <- Keys ],
   55:     Res = [ V || {_,V} <- ets:tab2list(Tab) ],
   56:     ets:delete(Tab),
   57:     {Res, sdev(Res)/X}.
   58: 
   59: sdev(List) ->
   60:     Len = length(List),
   61:     Mean = lists:sum(List)/Len,
   62:     math:sqrt(lists:sum([ (X - Mean) * (X - Mean) || X <- List ]) / Len).
   63: 
   64: file_keys(_,0,FdList,FnList) ->
   65:     [ file:close(FD) || FD <- FdList ],
   66:     [ file:delete(FN) || FN <- FnList ],
   67:     [];
   68: file_keys(Dir,Num,FdList,FnList) ->
   69:     Name = "dummy"++integer_to_list(Num),
   70:     FN = filename:join([Dir,Name]),
   71:     case file:open(FN,[write,raw]) of
   72: 	{ok,FD} ->
   73: 	    {file_descriptor,prim_file,{Port,_}} = FD,
   74: 	    <<X:32/integer-big>> = 
   75: 		iolist_to_binary(erlang:port_control(Port,$K,[])),
   76: 	    [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])];
   77: 	{error,_} ->
   78: 	    % Try freeing up FD's if there are any
   79: 	    case FdList of 
   80: 		[] ->
   81: 		    exit({cannot_open_file,FN});
   82: 		_ ->
   83: 		    [ file:close(FD) || FD <- FdList ],
   84: 		    [ file:delete(F) || F <- FnList ],
   85: 		    file_keys(Dir,Num,[],[])
   86: 	    end
   87:     end.
   88: 
   89: async_dist(doc) ->
   90:     "Check that the distribution of files over async threads is fair";
   91: async_dist(Config) when is_list(Config) ->
   92:     DataDir = ?config(data_dir,Config),
   93:     TestFile = filename:join(DataDir, "existing_file"),
   94:     Dir = filename:dirname(code:which(?MODULE)),
   95:     AsyncSizes = [7,10,100,255,256,64,63,65],
   96:     Max = 0.5,
   97: 
   98:     lists:foreach(fun(Size) ->
   99: 			  {ok,Node} = 
  100: 			      test_server:start_node
  101: 				(test_iter_max_files,slave,
  102: 				 [{args,
  103: 				   "+A "++integer_to_list(Size)++
  104: 				   " -pa " ++ Dir}]),
  105: 			  {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist,
  106: 						[DataDir]),
  107: 			  test_server:stop_node(Node),
  108: 			  if
  109: 			      SD > Max ->
  110: 				  io:format("Bad async queue distribution for "
  111: 					    "~p async threads:~n"
  112: 					    "    Standard deviation is ~p~n"
  113: 					    "    Key distribution:~n    ~lp~n",
  114: 					    [Size,SD,Distr]),
  115: 				  exit({bad_async_dist,Size,SD,Distr});
  116: 			      true ->
  117: 				  io:format("OK async queue distribution for "
  118: 					    "~p async threads:~n"
  119: 					    "    Standard deviation is ~p~n"
  120: 					    "    Key distribution:~n    ~lp~n",
  121: 					    [Size,SD,Distr]),
  122: 				  ok
  123: 			  end
  124: 		  end, AsyncSizes),
  125:     ok.
  126: 
  127: %%
  128: %% Open as many files as possible. Do this several times and check 
  129: %% that we get the same number of files every time.
  130: %%
  131: 
  132: iter_max_files(suite) -> [];
  133: iter_max_files(Config) when is_list(Config) ->
  134:     DataDir = ?config(data_dir,Config),
  135:     TestFile = filename:join(DataDir, "existing_file"),
  136:     N = 10,
  137:     %% Run on a different node in order to set the max ports
  138:     Dir = filename:dirname(code:which(?MODULE)),
  139:     {ok,Node} = test_server:start_node(test_iter_max_files,slave,
  140: 				       [{args,"+Q 1524 -pa " ++ Dir}]),
  141:     L = rpc:call(Node,?MODULE,do_iter_max_files,[N, TestFile]),
  142:     test_server:stop_node(Node),
  143:     io:format("Number of files opened in each test:~n~w\n", [L]),
  144:     all_equal(L),
  145:     Head = hd(L),
  146:     if  Head >= 2 -> ok;
  147: 	true -> ?line test_server:fail(too_few_files)
  148:     end,
  149:     {comment, "Max files: " ++ integer_to_list(hd(L))}.
  150: 
  151: do_iter_max_files(N, Name) when N > 0 -> 
  152:     ?line [max_files(Name)| do_iter_max_files(N-1, Name)];
  153: do_iter_max_files(_, _) ->
  154:     [].
  155: 
  156: all_equal([E, E| T]) ->
  157:     ?line all_equal([E| T]);
  158: all_equal([_]) ->
  159:     ok;
  160: all_equal([]) ->
  161:     ok.
  162:     
  163: max_files(Name) ->
  164:     ?line Fds = open_files(Name),
  165:     ?line N = length(Fds),
  166:     ?line close_files(Fds),
  167:     N.
  168: 
  169: close_files([Fd| Fds]) ->
  170:     ?line file:close(Fd),
  171:     ?line close_files(Fds);
  172: close_files([]) ->
  173:     ok.
  174: 
  175: open_files(Name) ->
  176:     ?line case file:open(Name, [read,raw]) of
  177: 	      {ok, Fd} ->
  178: 		  [Fd| open_files(Name)];
  179: 	      {error, _Reason} ->
  180: %		  io:format("Error reason: ~p", [_Reason]),
  181: 		  []
  182: 	  end.