1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2005-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(erts_debug_SUITE). 22-include_lib("common_test/include/ct.hrl"). 23-include_lib("common_test/include/ct_event.hrl"). 24 25-export([all/0, suite/0, groups/0, 26 test_size/1,flat_size_big/1,df/1,term_type/1, 27 instructions/1, stack_check/1, alloc_blocks_size/1, 28 interpreter_size_bench/1]). 29 30-export([do_alloc_blocks_size/0]). 31 32suite() -> 33 [{ct_hooks,[ts_install_cth]}, 34 {timetrap, {minutes, 2}}]. 35 36all() -> 37 [test_size, flat_size_big, df, instructions, term_type, 38 stack_check, alloc_blocks_size]. 39 40groups() -> 41 [{interpreter_size_bench, [], [interpreter_size_bench]}]. 42 43interpreter_size_bench(_Config) -> 44 Size = erts_debug:interpreter_size(), 45 ct_event:notify(#event{name=benchmark_data, 46 data=[{value,Size}]}), 47 {comment,integer_to_list(Size)++" bytes"}. 48 49%% White box testing of term heap sizes 50test_size(Config) when is_list(Config) -> 51 ConsCell1 = id([a|b]), 52 ConsCell2 = id(ConsCell1), 53 ConsCellSz = 2, 54 55 0 = do_test_size([]), 56 0 = do_test_size(42), 57 ConsCellSz = do_test_size(ConsCell1), 58 1 = do_test_size({}), 59 2 = do_test_size({[]}), 60 3 = do_test_size({a,b}), 61 7 = do_test_size({a,[b,c]}), 62 8 = do_test_size(#{b => 2,c => 3}), 63 4 = do_test_size(#{}), 64 32 = do_test_size(#{b => 2,c => 3,txt => "hello world"}), 65 66 true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,256)])) >= map_size_lower_bound(256), 67 true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,4096)])) >= map_size_lower_bound(4096), 68 true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,254)])) >= map_size_lower_bound(254), 69 true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,239)])) >= map_size_lower_bound(239), 70 71 Const = id(42), 72 AnotherConst = id(7), 73 74 %% Fun environment size = 0 (the smallest fun possible) 75 SimplestFun = fun() -> ok end, 76 FunSz0 = 6, 77 FunSz0 = do_test_size(SimplestFun), 78 79 %% Fun environment size = 1 80 FunSz1 = do_test_size(fun() -> Const end), 81 FunSz1 = FunSz0 + 1, 82 83 %% Fun environment size = 2 84 FunSz2 = do_test_size(fun() -> Const+AnotherConst end), 85 FunSz2 = FunSz1 + 1, 86 87 FunSz1 = do_test_size(fun() -> ConsCell1 end) - do_test_size(ConsCell1), 88 89 2 = do_test_size(fun lists:sort/1), 90 91 Arch = 8 * erlang:system_info({wordsize, external}), 92 case {Arch, do_test_size(mk_ext_pid({a@b, 1}, 17, 42))} of 93 {32, 5} -> ok; 94 {64, 4} -> ok 95 end, 96 case {Arch, do_test_size(mk_ext_port({a@b, 1}, 1742))} of 97 {32, 5} -> ok; 98 {64, 4} -> ok 99 end, 100 case {Arch, do_test_size(make_ref())} of 101 {32, 4} -> ok; 102 {64, 3} -> ok 103 end, 104 case {Arch, do_test_size(mk_ext_ref({a@b, 1}, [42,43,44]))} of 105 {32, 6} -> ok; 106 {64, 5} -> ok 107 end, 108 3 = do_test_size(atomics:new(1,[])), % Magic ref 109 110 3 = do_test_size(<<1,2,3>>), % ErlHeapBin 111 case {Arch, do_test_size(<<0:(8*64)>>)} of % ERL_ONHEAP_BIN_LIMIT 112 {32, 18} -> ok; 113 {64, 10} -> ok 114 end, 115 6 = do_test_size(<<0:(8*65)>>), % ProcBin 116 8 = do_test_size(<<5:7>>), % ErlSubBin + ErlHeapBin 117 11 = do_test_size(<<0:(8*80+1)>>), % ErlSubBin + ProcBin 118 119 %% Test shared data structures. 120 do_test_size([ConsCell1|ConsCell1], 121 3*ConsCellSz, 122 2*ConsCellSz), 123 do_test_size(fun() -> {ConsCell1,ConsCell2} end, 124 FunSz2 + 2*ConsCellSz, 125 FunSz2 + ConsCellSz), 126 do_test_size({SimplestFun,SimplestFun}, 127 2*FunSz0+do_test_size({a,b}), 128 FunSz0+do_test_size({a,b})), 129 130 M = id(#{ "atom" => first, i => 0}), 131 do_test_size([M,M#{ "atom" := other },M#{i := 42}],54,32), 132 ok. 133 134do_test_size(Term) -> 135 Sz = erts_debug:flat_size(Term), 136 Sz = erts_debug:size(Term). 137 138do_test_size(Term, FlatSz, Sz) -> 139 FlatSz = erts_debug:flat_size(Term), 140 Sz = erts_debug:size(Term). 141 142map_size_lower_bound(N) -> 143 %% this est. is a bit lower that actual lower bound 144 %% number of internal nodes 145 T = (N - 1) div 15, 146 %% total words 147 2 + 17 * T + 2 * N. 148 149flat_size_big(Config) when is_list(Config) -> 150 %% Build a term whose external size only fits in a big num (on 32-bit CPU). 151 flat_size_big_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF). 152 153flat_size_big_1(Term, Size0, Limit) when Size0 < Limit -> 154 case erts_debug:flat_size(Term) of 155 Size when is_integer(Size), Size0 < Size -> 156 io:format("~p", [Size]), 157 flat_size_big_1([Term|Term], Size, Limit) 158 end; 159flat_size_big_1(_, _, _) -> ok. 160 161 162term_type(Config) when is_list(Config) -> 163 Ts = [{fixnum, 1}, 164 {fixnum, -1}, 165 {bignum, 1 bsl 300}, 166 {bignum, -(1 bsl 300)}, 167 {hfloat, 0.0}, 168 {hfloat, 0.0/-1}, 169 {hfloat, 1.0/(1 bsl 302)}, 170 {hfloat, 1.0*(1 bsl 302)}, 171 {hfloat, -1.0/(1 bsl 302)}, 172 {hfloat, -1.0*(1 bsl 302)}, 173 {hfloat, 3.1416}, 174 {hfloat, 1.0e18}, 175 {hfloat, -3.1416}, 176 {hfloat, -1.0e18}, 177 178 {heap_binary, <<1,2,3>>}, 179 {refc_binary, <<0:(8*80)>>}, 180 {sub_binary, <<5:7>>}, 181 182 {flatmap, #{ a => 1}}, 183 {hashmap, maps:from_list([{I,I}||I <- lists:seq(1,76)])}, 184 185 {list, [1,2,3]}, 186 {nil, []}, 187 {tuple, {1,2,3}}, 188 {tuple, {}}, 189 190 {export, fun lists:sort/1}, 191 {'fun', fun() -> ok end}, 192 {pid, self()}, 193 {atom, atom}], 194 lists:foreach(fun({E,Val}) -> 195 R = erts_internal:term_type(Val), 196 io:format("expecting term type ~w, got ~w (~p)~n", [E,R,Val]), 197 E = R 198 end, Ts), 199 ok. 200 201 202df(Config) when is_list(Config) -> 203 P0 = pps(), 204 PrivDir = proplists:get_value(priv_dir, Config), 205 ok = file:set_cwd(PrivDir), 206 207 AllLoaded = [M || {M,_} <- code:all_loaded()], 208 {Pid,Ref} = spawn_monitor(fun() -> df_smoke(AllLoaded) end), 209 receive 210 {'DOWN',Ref,process,Pid,Status} -> 211 normal = Status 212 after 20*1000 -> 213 %% Not finished (i.e. a slow computer). Stop now. 214 Pid ! stop, 215 receive 216 {'DOWN',Ref,process,Pid,Status} -> 217 normal = Status, 218 io:format("...") 219 end 220 end, 221 io:nl(), 222 _ = [_ = file:delete(atom_to_list(M) ++ ".dis") || 223 M <- AllLoaded], 224 225 true = (P0 == pps()), 226 ok. 227 228stack_check(Config) when is_list(Config) -> 229 erts_debug:set_internal_state(available_internal_state,true), 230 %% Recurses on the C stack until stacklimit is reached. That 231 %% is, tests that the stack limit functionality works (used 232 %% by PCRE). VM will crash if it doesn't work... 233 Size = erts_debug:get_internal_state(stack_check), 234 erts_debug:set_internal_state(available_internal_state,false), 235 true = (is_integer(Size) and (Size > 0)), 236 {comment, "Stack size: "++integer_to_list(Size)++" bytes"}. 237 238df_smoke([M|Ms]) -> 239 io:format("~p", [M]), 240 erts_debug:df(M), 241 receive 242 stop -> 243 ok 244 after 0 -> 245 df_smoke(Ms) 246 end; 247df_smoke([]) -> ok. 248 249pps() -> 250 {erlang:ports()}. 251 252instructions(Config) when is_list(Config) -> 253 Is = erts_debug:instructions(), 254 _ = [list_to_atom(I) || I <- Is], 255 ok. 256 257alloc_blocks_size(Config) when is_list(Config) -> 258 F = fun(Args) -> 259 Node = start_slave(Args), 260 ok = rpc:call(Node, ?MODULE, do_alloc_blocks_size, []), 261 true = test_server:stop_node(Node) 262 end, 263 case test_server:is_asan() of 264 false -> F("+Meamax"); 265 true -> skip 266 end, 267 F("+Meamin"), 268 F(""), 269 ok. 270 271do_alloc_blocks_size() -> 272 _ = erts_debug:alloc_blocks_size(binary_alloc), 273 ok. 274 275start_slave(Args) -> 276 Name = ?MODULE_STRING ++ "_slave", 277 Pa = filename:dirname(code:which(?MODULE)), 278 {ok, Node} = test_server:start_node(list_to_atom(Name), 279 slave, 280 [{args, "-pa " ++ Pa ++ " " ++ Args}]), 281 Node. 282 283id(I) -> 284 I. 285 286mk_ext_pid({NodeName, Creation}, Number, Serial) -> 287 erts_test_utils:mk_ext_pid({NodeName, Creation}, Number, Serial). 288 289mk_ext_port({NodeName, Creation}, Number) -> 290 erts_test_utils:mk_ext_port({NodeName, Creation}, Number). 291 292mk_ext_ref({NodeName, Creation}, Numbers) -> 293 erts_test_utils:mk_ext_ref({NodeName, Creation}, Numbers). 294