1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2005-2017. 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
24-export([all/0, suite/0,
25	 test_size/1,flat_size_big/1,df/1,term_type/1,
26	 instructions/1, stack_check/1]).
27
28suite() ->
29    [{ct_hooks,[ts_install_cth]},
30     {timetrap, {minutes, 2}}].
31
32all() ->
33    [test_size, flat_size_big, df, instructions, term_type,
34     stack_check].
35
36test_size(Config) when is_list(Config) ->
37    ConsCell1 = id([a|b]),
38    ConsCell2 = id(ConsCell1),
39    ConsCellSz = 2,
40
41    0 = do_test_size([]),
42    0 = do_test_size(42),
43    ConsCellSz = do_test_size(ConsCell1),
44    1 = do_test_size({}),
45    2 = do_test_size({[]}),
46    3 = do_test_size({a,b}),
47    7 = do_test_size({a,[b,c]}),
48    8 = do_test_size(#{b => 2,c => 3}),
49    4 = do_test_size(#{}),
50    32 = do_test_size(#{b => 2,c => 3,txt => "hello world"}),
51
52    true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,256)])) >= map_size_lower_bound(256),
53    true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,4096)])) >= map_size_lower_bound(4096),
54    true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,254)])) >= map_size_lower_bound(254),
55    true = do_test_size(maps:from_list([{I,I}||I<-lists:seq(1,239)])) >= map_size_lower_bound(239),
56
57    %% Test internal consistency of sizes, but without testing
58    %% exact sizes.
59    Const = id(42),
60    AnotherConst = id(7),
61
62    %% Fun environment size = 0 (the smallest fun possible)
63    SimplestFun = fun() -> ok end,
64    FunSz0 = do_test_size(SimplestFun),
65
66    %% Fun environment size = 1
67    FunSz1 = do_test_size(fun() -> Const end),
68    FunSz1 = FunSz0 + 1,
69
70    %% Fun environment size = 2
71    FunSz2 = do_test_size(fun() -> Const+AnotherConst end),
72    FunSz2 = FunSz1 + 1,
73
74    FunSz1 = do_test_size(fun() -> ConsCell1 end) - do_test_size(ConsCell1),
75
76    %% Test shared data structures.
77    do_test_size([ConsCell1|ConsCell1],
78        	 3*ConsCellSz,
79        	 2*ConsCellSz),
80    do_test_size(fun() -> {ConsCell1,ConsCell2} end,
81        	 FunSz2 + 2*ConsCellSz,
82        	 FunSz2 + ConsCellSz),
83    do_test_size({SimplestFun,SimplestFun},
84        	 2*FunSz0+do_test_size({a,b}),
85        	 FunSz0+do_test_size({a,b})),
86
87    M = id(#{ "atom" => first, i => 0}),
88    do_test_size([M,M#{ "atom" := other },M#{i := 42}],54,32),
89    ok.
90
91do_test_size(Term) ->
92    Sz = erts_debug:flat_size(Term),
93    Sz = erts_debug:size(Term).
94
95do_test_size(Term, FlatSz, Sz) ->
96    FlatSz = erts_debug:flat_size(Term),
97    Sz = erts_debug:size(Term).
98
99map_size_lower_bound(N) ->
100    %% this est. is a bit lower that actual lower bound
101    %% number of internal nodes
102    T = (N - 1) div 15,
103    %% total words
104    2 + 17 * T + 2 * N.
105
106flat_size_big(Config) when is_list(Config) ->
107    %% Build a term whose external size only fits in a big num (on 32-bit CPU).
108    flat_size_big_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF).
109
110flat_size_big_1(Term, Size0, Limit) when Size0 < Limit ->
111    case erts_debug:flat_size(Term) of
112	Size when is_integer(Size), Size0 < Size ->
113	    io:format("~p", [Size]),
114	    flat_size_big_1([Term|Term], Size, Limit)
115    end;
116flat_size_big_1(_, _, _) -> ok.
117
118
119term_type(Config) when is_list(Config) ->
120    Ts = [{fixnum, 1},
121          {fixnum, -1},
122          {bignum, 1 bsl 300},
123          {bignum, -(1 bsl 300)},
124          {hfloat, 0.0},
125          {hfloat, 0.0/-1},
126          {hfloat, 1.0/(1 bsl 302)},
127          {hfloat, 1.0*(1 bsl 302)},
128          {hfloat, -1.0/(1 bsl 302)},
129          {hfloat, -1.0*(1 bsl 302)},
130          {hfloat, 3.1416},
131          {hfloat, 1.0e18},
132          {hfloat, -3.1416},
133          {hfloat, -1.0e18},
134
135          {heap_binary, <<1,2,3>>},
136          {refc_binary, <<0:(8*80)>>},
137          {sub_binary,  <<5:7>>},
138
139          {flatmap, #{ a => 1}},
140          {hashmap, maps:from_list([{I,I}||I <- lists:seq(1,76)])},
141
142          {list, [1,2,3]},
143          {nil, []},
144          {tuple, {1,2,3}},
145          {tuple, {}},
146
147          {export, fun lists:sort/1},
148          {'fun', fun() -> ok end},
149          {pid, self()},
150          {atom, atom}],
151    lists:foreach(fun({E,Val}) ->
152                          R = erts_internal:term_type(Val),
153                          io:format("expecting term type ~w, got ~w (~p)~n", [E,R,Val]),
154                          E = R
155                  end, Ts),
156    ok.
157
158
159df(Config) when is_list(Config) ->
160    P0 = pps(),
161    PrivDir = proplists:get_value(priv_dir, Config),
162    ok = file:set_cwd(PrivDir),
163
164    AllLoaded = [M || {M,_} <- code:all_loaded()],
165    {Pid,Ref} = spawn_monitor(fun() -> df_smoke(AllLoaded) end),
166    receive
167	{'DOWN',Ref,process,Pid,Status} ->
168	    normal = Status
169    after 20*1000 ->
170	    %% Not finished (i.e. a slow computer). Stop now.
171	    Pid ! stop,
172	    receive
173		{'DOWN',Ref,process,Pid,Status} ->
174		    normal = Status,
175		    io:format("...")
176	    end
177    end,
178    io:nl(),
179    _ = [_ = file:delete(atom_to_list(M) ++ ".dis") ||
180	    M <- AllLoaded],
181
182    true = (P0 == pps()),
183    ok.
184
185stack_check(Config) when is_list(Config) ->
186    erts_debug:set_internal_state(available_internal_state,true),
187    %% Recurses on the C stack until stacklimit is reached. That
188    %% is, tests that the stack limit functionality works (used
189    %% by PCRE). VM will crash if it doesn't work...
190    Size = erts_debug:get_internal_state(stack_check),
191    erts_debug:set_internal_state(available_internal_state,false),
192    {comment, "Stack size: "++integer_to_list(Size)++" bytes"}.
193
194df_smoke([M|Ms]) ->
195    io:format("~p", [M]),
196    erts_debug:df(M),
197    receive
198	stop ->
199	    ok
200    after 0 ->
201	    df_smoke(Ms)
202    end;
203df_smoke([]) -> ok.
204
205pps() ->
206    {erlang:ports()}.
207
208instructions(Config) when is_list(Config) ->
209    Is = erts_debug:instructions(),
210    _ = [list_to_atom(I) || I <- Is],
211    ok.
212
213id(I) ->
214    I.
215