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.