%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2005-2017. 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(erts_debug_SUITE). -include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct_event.hrl"). -export([all/0, suite/0, groups/0, test_size/1,flat_size_big/1,df/1,term_type/1, instructions/1, stack_check/1, alloc_blocks_size/1, interpreter_size_bench/1]). -export([do_alloc_blocks_size/0]). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 2}}]. all() -> [test_size, flat_size_big, df, instructions, term_type, stack_check, alloc_blocks_size]. groups() -> [{interpreter_size_bench, [], [interpreter_size_bench]}]. interpreter_size_bench(_Config) -> Size = erts_debug:interpreter_size(), ct_event:notify(#event{name=benchmark_data, data=[{value,Size}]}), {comment,integer_to_list(Size)++" bytes"}. test_size(Config) when is_list(Config) -> ConsCell1 = id([a|b]), ConsCell2 = id(ConsCell1), ConsCellSz = 2, 0 = do_test_size([]), 0 = do_test_size(42), ConsCellSz = do_test_size(ConsCell1), 1 = do_test_size({}), 2 = do_test_size({[]}), 3 = do_test_size({a,b}), 7 = do_test_size({a,[b,c]}), 8 = do_test_size(#{b => 2,c => 3}), 4 = do_test_size(#{}), 32 = do_test_size(#{b => 2,c => 3,txt => "hello world"}), true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,256)])) >= map_size_lower_bound(256), true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,4096)])) >= map_size_lower_bound(4096), true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,254)])) >= map_size_lower_bound(254), true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,239)])) >= map_size_lower_bound(239), %% Test internal consistency of sizes, but without testing %% exact sizes. Const = id(42), AnotherConst = id(7), %% Fun environment size = 0 (the smallest fun possible) SimplestFun = fun() -> ok end, FunSz0 = do_test_size(SimplestFun), %% Fun environment size = 1 FunSz1 = do_test_size(fun() -> Const end), FunSz1 = FunSz0 + 1, %% Fun environment size = 2 FunSz2 = do_test_size(fun() -> Const+AnotherConst end), FunSz2 = FunSz1 + 1, FunSz1 = do_test_size(fun() -> ConsCell1 end) - do_test_size(ConsCell1), %% Test shared data structures. do_test_size([ConsCell1|ConsCell1], 3*ConsCellSz, 2*ConsCellSz), do_test_size(fun() -> {ConsCell1,ConsCell2} end, FunSz2 + 2*ConsCellSz, FunSz2 + ConsCellSz), do_test_size({SimplestFun,SimplestFun}, 2*FunSz0+do_test_size({a,b}), FunSz0+do_test_size({a,b})), M = id(#{ "atom" => first, i => 0}), do_test_size([M,M#{ "atom" := other },M#{i := 42}],54,32), ok. do_test_size(Term) -> Sz = erts_debug:flat_size(Term), Sz = erts_debug:size(Term). do_test_size(Term, FlatSz, Sz) -> FlatSz = erts_debug:flat_size(Term), Sz = erts_debug:size(Term). map_size_lower_bound(N) -> %% this est. is a bit lower that actual lower bound %% number of internal nodes T = (N - 1) div 15, %% total words 2 + 17 * T + 2 * N. flat_size_big(Config) when is_list(Config) -> %% Build a term whose external size only fits in a big num (on 32-bit CPU). flat_size_big_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF). flat_size_big_1(Term, Size0, Limit) when Size0 < Limit -> case erts_debug:flat_size(Term) of Size when is_integer(Size), Size0 < Size -> io:format("~p", [Size]), flat_size_big_1([Term|Term], Size, Limit) end; flat_size_big_1(_, _, _) -> ok. term_type(Config) when is_list(Config) -> Ts = [{fixnum, 1}, {fixnum, -1}, {bignum, 1 bsl 300}, {bignum, -(1 bsl 300)}, {hfloat, 0.0}, {hfloat, 0.0/-1}, {hfloat, 1.0/(1 bsl 302)}, {hfloat, 1.0*(1 bsl 302)}, {hfloat, -1.0/(1 bsl 302)}, {hfloat, -1.0*(1 bsl 302)}, {hfloat, 3.1416}, {hfloat, 1.0e18}, {hfloat, -3.1416}, {hfloat, -1.0e18}, {heap_binary, <<1,2,3>>}, {refc_binary, <<0:(8*80)>>}, {sub_binary, <<5:7>>}, {flatmap, #{ a => 1}}, {hashmap, maps:from_list([{I,I}||I <- lists:seq(1,76)])}, {list, [1,2,3]}, {nil, []}, {tuple, {1,2,3}}, {tuple, {}}, {export, fun lists:sort/1}, {'fun', fun() -> ok end}, {pid, self()}, {atom, atom}], lists:foreach(fun({E,Val}) -> R = erts_internal:term_type(Val), io:format("expecting term type ~w, got ~w (~p)~n", [E,R,Val]), E = R end, Ts), ok. df(Config) when is_list(Config) -> P0 = pps(), PrivDir = proplists:get_value(priv_dir, Config), ok = file:set_cwd(PrivDir), AllLoaded = [M || {M,_} <- code:all_loaded()], {Pid,Ref} = spawn_monitor(fun() -> df_smoke(AllLoaded) end), receive {'DOWN',Ref,process,Pid,Status} -> normal = Status after 20*1000 -> %% Not finished (i.e. a slow computer). Stop now. Pid ! stop, receive {'DOWN',Ref,process,Pid,Status} -> normal = Status, io:format("...") end end, io:nl(), _ = [_ = file:delete(atom_to_list(M) ++ ".dis") || M <- AllLoaded], true = (P0 == pps()), ok. stack_check(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state,true), %% Recurses on the C stack until stacklimit is reached. That %% is, tests that the stack limit functionality works (used %% by PCRE). VM will crash if it doesn't work... Size = erts_debug:get_internal_state(stack_check), erts_debug:set_internal_state(available_internal_state,false), {comment, "Stack size: "++integer_to_list(Size)++" bytes"}. df_smoke([M|Ms]) -> io:format("~p", [M]), erts_debug:df(M), receive stop -> ok after 0 -> df_smoke(Ms) end; df_smoke([]) -> ok. pps() -> {erlang:ports()}. instructions(Config) when is_list(Config) -> Is = erts_debug:instructions(), _ = [list_to_atom(I) || I <- Is], ok. alloc_blocks_size(Config) when is_list(Config) -> F = fun(Args) -> Node = start_slave(Args), ok = rpc:call(Node, ?MODULE, do_alloc_blocks_size, []), true = test_server:stop_node(Node) end, F("+Meamax"), F("+Meamin"), F(""), ok. do_alloc_blocks_size() -> _ = erts_debug:alloc_blocks_size(binary_alloc), ok. start_slave(Args) -> Name = ?MODULE_STRING ++ "_slave", Pa = filename:dirname(code:which(?MODULE)), {ok, Node} = test_server:start_node(list_to_atom(Name), slave, [{args, "-pa " ++ Pa ++ " " ++ Args}]), Node. id(I) -> I.