1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1999-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(code_SUITE).
22-export([all/0, suite/0, init_per_suite/1, end_per_suite/1,
23         versions/1,new_binary_types/1,
24         bad_beam_file/1,
25         literal_leak/1,
26         call_purged_fun_code_gone/1,
27         call_purged_fun_code_reload/1,
28         call_purged_fun_code_there/1,
29         multi_proc_purge/1, t_check_old_code/1,
30         external_fun/1,get_chunk/1,module_md5/1,
31         constant_pools/1,constant_refc_binaries/1,
32         fake_literals/1,
33         false_dependency/1,coverage/1,fun_confusion/1,
34         t_copy_literals/1, t_copy_literals_frags/1,
35         erl_544/1, max_heap_size/1,
36	 check_process_code_signal_order/1,
37	 check_process_code_dirty_exec_proc/1]).
38
39-define(line_trace, 1).
40-include_lib("common_test/include/ct.hrl").
41
42suite() -> [{ct_hooks,[ts_install_cth]}].
43
44all() ->
45    [versions, new_binary_types,
46     bad_beam_file, literal_leak,
47     call_purged_fun_code_gone,
48     call_purged_fun_code_reload, call_purged_fun_code_there,
49     multi_proc_purge, t_check_old_code, external_fun, get_chunk,
50     module_md5,
51     constant_pools, constant_refc_binaries, fake_literals,
52     false_dependency,
53     coverage, fun_confusion, t_copy_literals, t_copy_literals_frags,
54     erl_544, max_heap_size, check_process_code_signal_order,
55     check_process_code_dirty_exec_proc].
56
57init_per_suite(Config) ->
58    erts_debug:set_internal_state(available_internal_state, true),
59    Config.
60
61end_per_suite(_Config) ->
62    catch erts_debug:set_internal_state(available_internal_state, false),
63    ok.
64
65%% Make sure that only two versions of a module can be loaded.
66versions(Config) when is_list(Config) ->
67    V1 = compile_version(1, Config),
68    V2 = compile_version(2, Config),
69    V3 = compile_version(3, Config),
70
71    {ok,P1,1} = load_version(V1, 1),
72    {ok,P2,2} = load_version(V2, 2),
73    {error,not_purged} = load_version(V2, 2),
74    {error,not_purged} = load_version(V3, 3),
75
76    1 = check_version(P1),
77    2 = check_version(P2),
78    2 = versions:version(),
79
80    %% Kill processes, unload code.
81    _ = monitor(process, P1),
82    _ = monitor(process, P2),
83    P1 ! P2 ! done,
84    receive
85        {'DOWN',_,process,P1,normal} -> ok
86    end,
87    receive
88        {'DOWN',_,process,P2,normal} -> ok
89    end,
90    true = erlang:purge_module(versions),
91    true = erlang:delete_module(versions),
92    true = erlang:purge_module(versions),
93    ok.
94
95compile_version(Version, Config) ->
96    Data = proplists:get_value(data_dir, Config),
97    File = filename:join(Data, "versions"),
98    {ok,versions,Bin} = compile:file(File, [{d,'VERSION',Version},
99                                            binary,report]),
100    Bin.
101
102load_version(Code, Ver) ->
103    case erlang:load_module(versions, Code) of
104        {module,versions} ->
105            Pid = spawn_link(versions, loop, []),
106            Ver = versions:version(),
107            Ver = check_version(Pid),
108            {ok,Pid,Ver};
109        Error ->
110            Error
111    end.
112
113check_version(Pid) ->
114    Pid ! {self(),version},
115    receive
116        {Pid,version,Version} ->
117            Version
118    end.
119
120new_binary_types(Config) when is_list(Config) ->
121    Data = proplists:get_value(data_dir, Config),
122    File = filename:join(Data, "my_code_test"),
123    {ok,my_code_test,Bin} = compile:file(File, [binary]),
124    {module,my_code_test} = erlang:load_module(my_code_test,
125                                               make_sub_binary(Bin)),
126    true = erlang:delete_module(my_code_test),
127    true = erlang:purge_module(my_code_test),
128
129    {module,my_code_test} = erlang:load_module(my_code_test,
130                                               make_unaligned_sub_binary(Bin)),
131    true = erlang:delete_module(my_code_test),
132    true = erlang:purge_module(my_code_test),
133
134    %% Try heap binaries and bad binaries.
135    {error,badfile} = erlang:load_module(my_code_test, <<1,2>>),
136    {error,badfile} = erlang:load_module(my_code_test,
137                                         make_sub_binary(<<1,2>>)),
138    {error,badfile} = erlang:load_module(my_code_test,
139                                         make_unaligned_sub_binary(<<1,2>>)),
140    {'EXIT',{badarg,_}} = (catch erlang:load_module(my_code_test,
141                                                    bit_sized_binary(Bin))),
142    ok.
143
144%% Ensure that the loader doesn't crash or leak memory when attempting
145%% to load bad BEAM files. We depend on valgrind to notice leaks.
146bad_beam_file(_Config) ->
147    Mod = ?FUNCTION_NAME,
148
149    BadBeam1 = bad_beam_file_1(Mod),
150    {error,badfile} = code:load_binary(Mod, atom_to_list(Mod), BadBeam1),
151
152    BadBeam2 = bad_beam_file_2(Mod),
153    {error,badfile} = code:load_binary(Mod, atom_to_list(Mod), BadBeam2),
154
155    ok.
156
157%% Build a BEAM file with an invalid instruction in the code chunk.
158bad_beam_file_1(Mod) ->
159    Exp = [{term,0}],
160    Attr = [],
161    Fs = [{function,term,0,3,
162           [{label,1},
163            {line,[]},
164            {func_info,{atom,Mod},{atom,term},0},
165            {label,2},
166            {move,nil,nil},                     %Illegal destination.
167            return]}],
168    Asm = {Mod,Exp,Attr,Fs,3},
169
170    %% Bypass beam_validator.
171    {ok,BadBeam} = beam_asm:module(Asm, [], [], []),
172    BadBeam.
173
174%% Build a BEAM file with an invalid attributes chunk.
175bad_beam_file_2(Mod) ->
176    Asm = {Mod,[],[],[],1},
177    {ok,BadBeam0} = beam_asm:module(Asm, [], [], []),
178    {ok, Mod, Chunks0} = beam_lib:all_chunks(BadBeam0),
179    Chunks1 = lists:keydelete("Attr", 1, Chunks0),
180    Chunks = [{"Attr",<<"bad_attribute_chunk">>} | Chunks1],
181    {ok,BadBeam} = beam_lib:build_module(Chunks),
182    BadBeam.
183
184%% Ensure that literal areas don't leak when erlang:prepare_loading/2
185%% is not followed by erlang:finish_loading/1.
186literal_leak(_Config) ->
187    Mod = ?FUNCTION_NAME,
188    HugeLiteral = binary_to_list(<<0:(1024*1024)/unit:8>>),
189    Exp = [{term,0}],
190    Attr = [],
191    Fs = [{function,term,0,3,
192           [{label,1},
193            {line,[]},
194            {func_info,{atom,Mod},{atom,term},0},
195            {label,2},
196            {move,{literal,HugeLiteral},{x,0}},
197            return]}],
198    Asm = {Mod,Exp,Attr,Fs,3},
199    {ok,Beam} = beam_asm:module(Asm, [], [], []),
200
201    %% valgrind cannot help us find leak of literals because literal
202    %% areas are allocated using mmap().
203    %%
204    %% Instead we will prepare for loading a BEAM file with a 16Mb
205    %% literal N times so that the reserved literal memory range will
206    %% overflow.
207    %%
208    %% Setting N to 64 is sufficient for overflowing a 1 GB literal
209    %% area (which is the size at the time of writing). Just to be
210    %% sure, we will go a little bit higher...
211
212    N = 128,
213    _ = [begin
214             _ = erlang:prepare_loading(Mod, Beam),
215             _ = erlang:garbage_collect()
216         end || _ <- lists:seq(1, N)],
217    ok.
218
219call_purged_fun_code_gone(Config) when is_list(Config) ->
220    Priv = proplists:get_value(priv_dir, Config),
221    Data = proplists:get_value(data_dir, Config),
222    call_purged_fun_test(Priv, Data, code_gone),
223    ok.
224
225call_purged_fun_code_reload(Config) when is_list(Config) ->
226    Priv = proplists:get_value(priv_dir, Config),
227    Data = proplists:get_value(data_dir, Config),
228    Path = code:get_path(),
229    true = code:add_path(Priv),
230    try
231	call_purged_fun_test(Priv, Data, code_reload)
232    after
233	code:set_path(Path)
234    end,
235    ok.
236
237call_purged_fun_code_there(Config) when is_list(Config) ->
238    Priv = proplists:get_value(priv_dir, Config),
239    Data = proplists:get_value(data_dir, Config),
240    call_purged_fun_test(Priv, Data, code_there),
241    ok.
242
243call_purged_fun_test(Priv, Data, Type) ->
244    SrcFile = filename:join(Data, "call_purged_fun_tester.erl"),
245    ObjFile = filename:join(Priv, "call_purged_fun_tester.beam"),
246    {ok,Mod,Code} = compile:file(SrcFile, [binary, report]),
247    {module,Mod} = code:load_binary(Mod, ObjFile, Code),
248
249    call_purged_fun_tester:do(Priv, Data, Type, []).
250
251
252multi_proc_purge(Config) when is_list(Config) ->
253    %%
254    %% Make sure purge requests aren't lost when
255    %% purger process is working.
256    %%
257    Priv = proplists:get_value(priv_dir, Config),
258    Data = proplists:get_value(data_dir, Config),
259    File1 = filename:join(Data, "my_code_test"),
260    File2 = filename:join(Data, "my_code_test2"),
261
262    {ok,my_code_test} = c:c(File1, [{outdir,Priv}]),
263    {ok,my_code_test2} = c:c(File2, [{outdir,Priv}]),
264    erlang:delete_module(my_code_test),
265    erlang:delete_module(my_code_test2),
266
267    Self = self(),
268
269    Fun1 = fun () ->
270		   erts_code_purger:purge(my_code_test),
271		   Self ! {self(), done}
272	   end,
273    Fun2 = fun () ->
274		   erts_code_purger:soft_purge(my_code_test2),
275		   Self ! {self(), done}
276	   end,
277    Fun3 = fun () ->
278		   erts_code_purger:purge('__nonexisting_module__'),
279		   Self ! {self(), done}
280	   end,
281    Fun4 = fun () ->
282		   erts_code_purger:soft_purge('__another_nonexisting_module__'),
283		   Self ! {self(), done}
284	   end,
285
286    Pid1 = spawn_link(Fun1),
287    Pid2 = spawn_link(Fun2),
288    Pid3 = spawn_link(Fun3),
289    Pid4 = spawn_link(Fun4),
290    Pid5 = spawn_link(Fun1),
291    Pid6 = spawn_link(Fun2),
292    Pid7 = spawn_link(Fun3),
293    receive after 50 -> ok end,
294    Pid8 = spawn_link(Fun4),
295    Pid9 = spawn_link(Fun1),
296    Pid10 = spawn_link(Fun2),
297    Pid11 = spawn_link(Fun3),
298    Pid12 = spawn_link(Fun4),
299    Pid13 = spawn_link(Fun1),
300    receive after 50 -> ok end,
301    Pid14 = spawn_link(Fun2),
302    Pid15 = spawn_link(Fun3),
303    Pid16 = spawn_link(Fun4),
304
305    lists:foreach(fun (P) -> receive {P, done} -> ok end end,
306		  [Pid1, Pid2, Pid3, Pid4, Pid5, Pid6, Pid7, Pid8,
307		   Pid9, Pid10, Pid11, Pid12, Pid13, Pid14, Pid15, Pid16]),
308    ok.
309
310%% Test the erlang:check_old_code/1 BIF.
311t_check_old_code(Config) when is_list(Config) ->
312    Data = proplists:get_value(data_dir, Config),
313    File = filename:join(Data, "my_code_test"),
314
315    catch erlang:purge_module(my_code_test),
316    catch erlang:delete_module(my_code_test),
317    catch erlang:purge_module(my_code_test),
318
319    false = erlang:check_old_code(my_code_test),
320
321    {ok,my_code_test,Code} = compile:file(File, [binary]),
322    {module,my_code_test} = code:load_binary(my_code_test, File, Code),
323
324    false = erlang:check_old_code(my_code_test),
325    {module,my_code_test} = code:load_binary(my_code_test, File, Code),
326    true = erlang:check_old_code(my_code_test),
327
328    true = erlang:purge_module(my_code_test),
329    true = erlang:delete_module(my_code_test),
330    true = erlang:purge_module(my_code_test),
331
332    {'EXIT',_} = (catch erlang:check_old_code([])),
333
334    ok.
335
336external_fun(Config) when is_list(Config) ->
337    false = erlang:function_exported(another_code_test, x, 1),
338    AnotherCodeTest = id(another_code_test),
339    ExtFun = fun AnotherCodeTest:x/1,
340    {'EXIT',{undef,_}} = (catch ExtFun(answer)),
341    false = erlang:function_exported(another_code_test, x, 1),
342    false = lists:member(another_code_test, erlang:loaded()),
343    Data = proplists:get_value(data_dir, Config),
344    File = filename:join(Data, "another_code_test"),
345    {ok,another_code_test,Code} = compile:file(File, [binary,report]),
346    {module,another_code_test} = erlang:load_module(another_code_test, Code),
347    42 = ExtFun(answer),
348    ok.
349
350get_chunk(Config) when is_list(Config) ->
351    Data = proplists:get_value(data_dir, Config),
352    File = filename:join(Data, "my_code_test"),
353    {ok,my_code_test,Code} = compile:file(File, [binary]),
354
355    %% Should work.
356    Chunk = get_chunk_ok("AtU8", Code),
357    Chunk = get_chunk_ok("AtU8", make_sub_binary(Code)),
358    Chunk = get_chunk_ok("AtU8", make_unaligned_sub_binary(Code)),
359
360    %% Should fail.
361    {'EXIT',{badarg,_}} = (catch code:get_chunk(bit_sized_binary(Code), "AtU8")),
362    {'EXIT',{badarg,_}} = (catch code:get_chunk(Code, "bad chunk id")),
363
364    %% Invalid beam code or missing chunk should return 'undefined'.
365    undefined = code:get_chunk(<<"not a beam module">>, "AtU8"),
366    undefined = code:get_chunk(Code, "XXXX"),
367
368    ok.
369
370get_chunk_ok(Chunk, Code) ->
371    case code:get_chunk(Code, Chunk) of
372        Bin when is_binary(Bin) -> Bin
373    end.
374
375module_md5(Config) when is_list(Config) ->
376    Data = proplists:get_value(data_dir, Config),
377    File = filename:join(Data, "my_code_test"),
378    {ok,my_code_test,Code} = compile:file(File, [binary]),
379
380    %% Should work.
381    Chunk = module_md5_ok(Code),
382    Chunk = module_md5_ok(make_sub_binary(Code)),
383    Chunk = module_md5_ok(make_unaligned_sub_binary(Code)),
384
385    %% Should fail.
386    {'EXIT',{badarg,_}} = (catch code:module_md5(bit_sized_binary(Code))),
387
388    %% Invalid beam code should return 'undefined'.
389    undefined = code:module_md5(<<"not a beam module">>),
390    ok.
391
392module_md5_ok(Code) ->
393    case code:module_md5(Code) of
394        Bin when is_binary(Bin), size(Bin) =:= 16 -> Bin
395    end.
396
397
398constant_pools(Config) when is_list(Config) ->
399    Data = proplists:get_value(data_dir, Config),
400    File = filename:join(Data, "literals"),
401    {ok,literals,Code} = compile:file(File, [report,binary]),
402    {module,literals} = erlang:load_module(literals,
403                                           make_sub_binary(Code)),
404
405    %% Initialize.
406    A = literals:a(),
407    B = literals:b(),
408    C = literals:huge_bignum(),
409    D = literals:funs(),
410    process_flag(trap_exit, true),
411    Self = self(),
412
413    %% Have a process WITHOUT old heap that references the literals
414    %% in the 'literals' module.
415    NoOldHeap = spawn_link(fun() -> no_old_heap(Self) end),
416    receive go -> ok end,
417    true = erlang:delete_module(literals),
418    false = erlang:check_process_code(NoOldHeap, literals),
419    erlang:check_process_code(self(), literals),
420    true = erlang:purge_module(literals),
421    NoOldHeap ! done,
422    receive
423        {'EXIT',NoOldHeap,{A,B,C,D}} ->
424            ok;
425        Other_NoOldHeap ->
426            ct:fail({unexpected,Other_NoOldHeap})
427    end,
428    {module,literals} = erlang:load_module(literals, Code),
429
430    %% Have a process with an inconsistent heap (legal while GC is disabled)
431    %% that references the literals in the 'literals' module.
432    InconsistentHeap = spawn_link(fun() -> inconsistent_heap(Self) end),
433    receive go -> ok end,
434    true = erlang:delete_module(literals),
435    false = erlang:check_process_code(InconsistentHeap, literals),
436    erlang:check_process_code(self(), literals),
437    true = erlang:purge_module(literals),
438    InconsistentHeap ! done,
439    receive
440        {'EXIT',InconsistentHeap,{A,B,C}} ->
441            ok;
442        Other_InconsistentHeap ->
443            ct:fail({unexpected,Other_InconsistentHeap})
444    end,
445    {module,literals} = erlang:load_module(literals, Code),
446
447    %% Have a process WITH an old heap that references the literals
448    %% in the 'literals' module.
449    OldHeap = spawn_link(fun() -> old_heap(Self) end),
450    receive go -> ok end,
451    true = erlang:delete_module(literals),
452    false = erlang:check_process_code(OldHeap, literals),
453    erlang:check_process_code(self(), literals),
454    erlang:purge_module(literals),
455    OldHeap ! done,
456    receive
457	{'EXIT',OldHeap,{A,B,C,D,[1,2,3|_]=Seq}} when length(Seq) =:= 16 ->
458	    ok
459    end,
460
461    {module,literals} = erlang:load_module(literals, Code),
462    %% Have a hibernated process that references the literals
463    %% in the 'literals' module.
464    {Hib, Mon} = spawn_monitor(fun() -> hibernated(Self) end),
465    receive go -> ok end,
466    [{heap_size,OldHeapSz},
467     {total_heap_size,OldTotHeapSz}] = process_info(Hib, [heap_size,
468							  total_heap_size]),
469    OldHeapSz = OldTotHeapSz,
470    io:format("OldHeapSz=~p OldTotHeapSz=~p~n", [OldHeapSz, OldTotHeapSz]),
471    true = erlang:delete_module(literals),
472    false = erlang:check_process_code(Hib, literals),
473    erlang:check_process_code(self(), literals),
474    erlang:purge_module(literals),
475    receive after 1000 -> ok end,
476    [{heap_size,HeapSz},
477     {total_heap_size,TotHeapSz}] = process_info(Hib, [heap_size,
478						       total_heap_size]),
479    io:format("HeapSz=~p TotHeapSz=~p~n", [HeapSz, TotHeapSz]),
480    Hib ! hej,
481    receive
482	{'DOWN', Mon, process, Hib, Reason} ->
483	    {undef, [{no_module,
484		      no_function,
485		      [{A,B,C,D,[1,2,3|_]=Seq}], _}]} = Reason,
486	    16 = length(Seq)
487    end,
488    HeapSz = TotHeapSz, %% Ensure restored to hibernated state...
489    true = HeapSz > OldHeapSz,
490    literal_area_collector_test:check_idle(5000),
491    ok.
492
493no_old_heap(Parent) ->
494    A = literals:a(),
495    B = literals:b(),
496    C = literals:huge_bignum(),
497    D = literals:funs(),
498    Res = {A,B,C,D},
499    Parent ! go,
500    receive
501        done ->
502            exit(Res)
503    end.
504
505old_heap(Parent) ->
506    A = literals:a(),
507    B = literals:b(),
508    C = literals:huge_bignum(),
509    D = literals:funs(),
510    Res = {A,B,C,D,lists:seq(1, 16)},
511    create_old_heap(),
512    Parent ! go,
513    receive
514        done ->
515            exit(Res)
516    end.
517
518inconsistent_heap(Parent) ->
519    A = literals:a(),
520    B = literals:b(),
521    C = literals:huge_bignum(),
522    Res = {A,B,C},
523    Parent ! go,
524
525    %% Disable the GC and return a tuple whose arity and contents are broken
526    BrokenTerm = erts_debug:set_internal_state(inconsistent_heap, start),
527    receive
528    after 5000 ->
529        %% Fix the tuple and enable the GC again
530        ok = erts_debug:set_internal_state(inconsistent_heap, BrokenTerm),
531        erlang:garbage_collect()
532    end,
533
534    receive
535        done ->
536            exit(Res)
537    end.
538
539hibernated(Parent) ->
540    A = literals:a(),
541    B = literals:b(),
542    C = literals:huge_bignum(),
543    D = literals:funs(),
544    Res = {A,B,C,D,lists:seq(1, 16)},
545    Parent ! go,
546    erlang:hibernate(no_module, no_function, [Res]).
547
548create_old_heap() ->
549    case process_info(self(), [heap_size,total_heap_size]) of
550        [{heap_size,Sz},{total_heap_size,Total}] when Sz < Total ->
551            ok;
552        _ ->
553            create_old_heap()
554    end.
555
556constant_refc_binaries(Config) when is_list(Config) ->
557    wait_for_memory_deallocations(),
558    Bef = memory_binary(),
559    io:format("Binary data (bytes) before test: ~p\n", [Bef]),
560
561    %% Compile the the literals module.
562    Data = proplists:get_value(data_dir, Config),
563    File = filename:join(Data, "literals"),
564    {ok,literals,Code} = compile:file(File, [report,binary]),
565
566    %% Load the code and make sure that the binary is a refc binary.
567    {module,literals} = erlang:load_module(literals, Code),
568    Bin = literals:binary(),
569    Sz = byte_size(Bin),
570    Check = erlang:md5(Bin),
571    io:format("Size of literal refc binary: ~p\n", [Sz]),
572    {refc_binary,Sz,_,_} = erts_debug:get_internal_state({binary_info,Bin}),
573    true = erlang:delete_module(literals),
574    false = erlang:check_process_code(self(), literals),
575    true = erlang:purge_module(literals),
576
577    %% Now try to provoke a memory leak.
578    provoke_mem_leak(10, Code, Check),
579
580    %% Calculate the change in allocated binary data.
581    erlang:garbage_collect(),
582    wait_for_memory_deallocations(),
583    Aft = memory_binary(),
584    io:format("Binary data (bytes) after test: ~p", [Aft]),
585    Diff = Aft - Bef,
586    if
587        Diff < 0 ->
588            io:format("~p less bytes", [abs(Diff)]);
589        Diff > 0 ->
590            io:format("~p more bytes", [Diff]);
591        true ->
592            ok
593    end,
594
595    %% Test for leaks. We must accept some natural variations in
596    %% the size of allocated binaries.
597    if
598        Diff > 64*1024 ->
599            ct:fail(binary_leak);
600        true ->
601            ok
602    end.
603
604memory_binary() ->
605    try
606        erlang:memory(binary)
607    catch
608        error:notsup ->
609            0
610    end.
611
612provoke_mem_leak(0, _, _) -> ok;
613provoke_mem_leak(N, Code, Check) ->
614    {module,literals} = erlang:load_module(literals, Code),
615
616    %% Create several processes with references to the literal binary.
617    Self = self(),
618    Pids = [spawn_link(fun() ->
619                               create_binaries(Self, NumRefs, Check)
620                       end) || NumRefs <- lists:seq(1, 10)],
621    [receive {started,Pid} -> ok end || Pid <- Pids],
622
623    %% Make the code old and remove references to the constant pool
624    %% in all processes.
625    true = erlang:delete_module(literals),
626    Ms = [spawn_monitor(fun() ->
627                                false = erlang:check_process_code(Pid, literals)
628                        end) || Pid <- Pids],
629    [receive
630         {'DOWN',R,process,P,normal} ->
631             ok
632     end || {P,R} <- Ms],
633
634    %% Purge the code.
635    true = erlang:purge_module(literals),
636
637    %% Tell the processes that the code has been purged.
638    [begin
639         monitor(process, Pid),
640         Pid ! purged
641     end || Pid <- Pids],
642
643    %% Wait for all processes to terminate.
644    [receive
645         {'DOWN',_,process,Pid,normal} ->
646             ok
647     end || Pid <- Pids],
648
649    %% We now expect that the binary has been deallocated.
650    provoke_mem_leak(N-1, Code, Check).
651
652create_binaries(Parent, NumRefs, Check) ->
653    Bin = literals:binary(),
654    Bins = lists:duplicate(NumRefs, Bin),
655    {bits,Bits} = literals:bits(),
656    Parent ! {started,self()},
657    receive
658        purged ->
659            %% The code has been purged. Now make sure that
660            %% the binaries haven't been corrupted.
661            Check = erlang:md5(Bin),
662            [Bin = B || B <- Bins],
663            <<42:13,Bin/binary>> = Bits,
664
665            %% Remove all references to the binaries
666            %% Doing it explicitly like this ensures that
667            %% the binaries are gone when the parent process
668            %% receives the 'DOWN' message.
669            erlang:garbage_collect()
670    end.
671
672wait_for_memory_deallocations() ->
673    try
674        erts_debug:set_internal_state(wait, deallocations)
675    catch
676        error:undef ->
677            erts_debug:set_internal_state(available_internal_state, true),
678            wait_for_memory_deallocations()
679    end.
680
681fake_literals(_Config) ->
682    Mod = fake__literals__module,
683    try
684        do_fake_literals(Mod)
685    after
686        _ = code:purge(Mod),
687        _ = code:delete(Mod),
688        _ = code:purge(Mod),
689        _ = code:delete(Mod)
690    end,
691    ok.
692
693do_fake_literals(Mod) ->
694    Tid = ets:new(test, []),
695    ExtTerms = get_external_terms(),
696    Term0 = {self(),make_ref(),Tid,fun() -> ok end,ExtTerms},
697    Terms = [begin
698                 make_literal_module(Mod, Term0),
699                 Mod:term()
700             end || _ <- lists:seq(1, 10)],
701    verify_lit_terms(Terms, Term0),
702    true = ets:delete(Tid),
703    ok.
704
705make_literal_module(Mod, Term) ->
706    Exp = [{term,0}],
707    Attr = [],
708    Fs = [{function,term,0,2,
709           [{label,1},
710            {line,[]},
711            {func_info,{atom,Mod},{atom,term},0},
712            {label,2},
713            {move,{literal,Term},{x,0}},
714            return]}],
715    Asm = {Mod,Exp,Attr,Fs,3},
716    {ok,Mod,Beam} = compile:forms(Asm, [from_asm,binary,report]),
717    code:load_binary(Mod, atom_to_list(Mod), Beam).
718
719verify_lit_terms([H|T], Term) ->
720    case H =:= Term of
721        true ->
722            verify_lit_terms(T, Term);
723        false ->
724            error({bad_term,H})
725    end;
726verify_lit_terms([], _) ->
727    ok.
728
729get_external_terms() ->
730    {ok,Node} =	test_server:start_node(?FUNCTION_NAME, slave, []),
731    Ref = rpc:call(Node, erlang, make_ref, []),
732    Ports = rpc:call(Node, erlang, ports, []),
733    Pid = rpc:call(Node, erlang, self, []),
734    _ = test_server:stop_node(Node),
735    {Ref,hd(Ports),Pid}.
736
737%% OTP-7559: c_p->cp could contain garbage and create a false dependency
738%% to a module in a process. (Thanks to Richard Carlsson.)
739false_dependency(Config) when is_list(Config) ->
740    Data = proplists:get_value(data_dir, Config),
741    File = filename:join(Data, "cpbugx"),
742    {ok,cpbugx,Code} = compile:file(File, [binary,report]),
743
744    do_false_dependency(fun cpbugx:before/0, Code),
745    do_false_dependency(fun cpbugx:before2/0, Code),
746    do_false_dependency(fun cpbugx:before3/0, Code),
747
748    ok.
749
750do_false_dependency(Init, Code) ->
751    {module,cpbugx} = erlang:load_module(cpbugx, Code),
752
753    %% Spawn process. Make sure it has the appropriate init function
754    %% and returned. CP should not contain garbage after the return.
755    Parent = self(),
756    Pid = spawn_link(fun() -> false_dependency_loop(Parent, Init, true) end),
757    receive initialized -> ok end,
758
759    %% Reload the module. Make sure the process is still alive.
760    {module,cpbugx} = erlang:load_module(cpbugx, Code),
761    io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))),
762    true = is_process_alive(Pid),
763
764    %% There should not be any dependency to cpbugx.
765    false = erlang:check_process_code(Pid, cpbugx),
766
767    %% Kill the process and completely unload the code.
768    unlink(Pid), exit(Pid, kill),
769    true = erlang:purge_module(cpbugx),
770    true = erlang:delete_module(cpbugx),
771    true = erlang:purge_module(cpbugx),
772    ok.
773
774false_dependency_loop(Parent, Init, SendInitAck) ->
775    Init(),
776    case SendInitAck of
777        true -> Parent ! initialized;
778        false -> void
779                 %% Just send one init-ack. I guess the point of this test
780                 %% wasn't to fill parents msg-queue (?). Seen to cause
781                 %% out-of-mem (on halfword-vm for some reason) by
782                 %% 91 million msg in queue. /sverker
783    end,
784    receive
785        _ -> false_dependency_loop(Parent, Init, false)
786    end.
787
788coverage(Config) when is_list(Config) ->
789    {'EXIT',{badarg,_}} = (catch erlang:purge_module({a,b,c})),
790    {'EXIT',{badarg,_}} = (catch erlang:check_process_code(not_a_pid, ?MODULE)),
791    {'EXIT',{badarg,_}} = (catch erlang:check_process_code(self(), [not_a_module])),
792    {'EXIT',{badarg,_}} = (catch erlang:delete_module([a,b,c])),
793    {'EXIT',{badarg,_}} = (catch erlang:module_loaded(42)),
794    ok.
795
796fun_confusion(Config) when is_list(Config) ->
797    Data = proplists:get_value(data_dir, Config),
798    Src = filename:join(Data, "fun_confusion"),
799    Mod = fun_confusion,
800
801    %% Load first version of module.
802    compile_load(Mod, Src, 1),
803    F1 = Mod:f(),
804    1 = F1(),
805
806    %% Load second version of module.
807    compile_load(Mod, Src, 2),
808    F2 = Mod:f(),
809
810    %% F1 should refer to the old code, not the newly loaded code.
811    1 = F1(),
812    2 = F2(),
813    ok.
814
815compile_load(Mod, Src, Ver) ->
816    {ok,Mod,Code1} = compile:file(Src, [binary,{d,version,Ver}]),
817    {module,Mod} = code:load_binary(Mod, "fun_confusion.beam", Code1),
818    ok.
819
820
821t_copy_literals(Config) when is_list(Config) ->
822    %% Compile the the literals module.
823    Data = proplists:get_value(data_dir, Config),
824    File = filename:join(Data, "literals"),
825    {ok,literals,Code} = compile:file(File, [report,binary]),
826    {module,literals} = erlang:load_module(literals, Code),
827
828    N   = 30,
829    Me  = self(),
830    %% reload literals code every 567 ms
831    Rel = spawn_link(fun() -> reloader(literals,Code,567) end),
832    %% add new literal msgs to the loop every 789 ms
833    Sat = spawn_link(fun() -> saturate(Me,789) end),
834    %% run for 10s
835    _   = spawn_link(fun() -> receive after 10000 -> Me ! done end end),
836    ok  = chase_msg(N, Me),
837    %% cleanup
838    Rel ! done,
839    Sat ! done,
840    ok  = flush(),
841    ok.
842
843-define(mod, t_copy_literals_frags).
844t_copy_literals_frags(Config) when is_list(Config) ->
845    Bin = gen_lit(?mod,[{a,{1,2,3,4,5,6,7}},
846                        {b,"hello world"},
847                        {c, <<"hello world">>},
848                        {d, {"hello world", {1.0, 2.0, <<"some">>, "string"}}},
849                        {e, <<"off heap", 0, 1, 2, 3, 4, 5, 6, 7,
850                                          8, 9,10,11,12,13,14,15,
851                                          0, 1, 2, 3, 4, 5, 6, 7,
852                                          8, 9,10,11,12,13,14,15,
853                                          0, 1, 2, 3, 4, 5, 6, 7,
854                                          8, 9,10,11,12,13,14,15,
855                                          0, 1, 2, 3, 4, 5, 6, 7,
856                                          8, 9,10,11,12,13,14,15>>},
857                        {f, fun ?MODULE:all/0}]),
858
859    {module, ?mod} = erlang:load_module(?mod, Bin),
860    N = 6000,
861    Recv = spawn_opt(fun() -> receive
862                                  read ->
863                                      io:format("reading"),
864                                      literal_receiver()
865                              end
866                     end, [link,{min_heap_size, 10000}]),
867    Switcher = spawn_link(fun() -> literal_switcher() end),
868    Pids = [spawn_opt(fun() -> receive
869                                   {Pid, go, Recv, N} ->
870                                       io:format("sender batch (~w) start ~w~n",[N,self()]),
871                                       literal_sender(N,Recv),
872                                       Pid ! {self(), ok}
873                               end
874                      end, [link,{min_heap_size,800}]) || _ <- lists:seq(1,100)],
875    _ = [Pid ! {self(), go, Recv, N} || Pid <- Pids],
876    %% don't read immediately
877    timer:sleep(5),
878    Recv ! read,
879    Switcher ! {switch,?mod,Bin,[Recv|Pids],200},
880    _ = [receive {Pid, ok} -> ok end || Pid <- Pids],
881    Switcher ! {self(), done},
882    receive {Switcher, ok} -> ok end,
883    Recv ! {self(), done},
884    receive {Recv, ok} -> ok end,
885    ok.
886
887literal_receiver() ->
888    receive
889        {Pid, done} ->
890            io:format("reader_done~n"),
891            Pid ! {self(), ok};
892        {_Pid, msg, [A,B,C,D,E]} ->
893            A = ?mod:a(),
894            B = ?mod:b(),
895            C = ?mod:c(),
896            D = ?mod:d(),
897            E = ?mod:e(),
898            F = ?mod:f(),
899            literal_receiver();
900        {Pid, sender_confirm} ->
901            io:format("sender confirm ~w~n", [Pid]),
902            Pid ! {self(), ok},
903            literal_receiver()
904    end.
905
906literal_sender(0, Recv) ->
907    Recv ! {self(), sender_confirm},
908    receive {Recv, ok} -> ok end;
909literal_sender(N, Recv) ->
910    Recv ! {self(), msg, [?mod:a(),
911                          ?mod:b(),
912                          ?mod:c(),
913                          ?mod:d(),
914                          ?mod:e(),
915                          ?mod:f()]},
916    literal_sender(N - 1, Recv).
917
918literal_switcher() ->
919    receive
920        {switch,Mod,Bin,Pids,Tmo} ->
921            literal_switcher(Mod,Bin,Pids,Tmo)
922    end.
923literal_switcher(Mod,Bin,Pids,Tmo) ->
924    receive
925        {Pid,done} ->
926            Pid ! {self(),ok}
927    after Tmo ->
928              io:format("load module ~w~n", [Mod]),
929              {module, Mod} = erlang:load_module(Mod,Bin),
930              ok = check_and_purge(Pids,Mod),
931              io:format("purge complete ~w~n", [Mod]),
932              literal_switcher(Mod,Bin,Pids,Tmo+Tmo)
933    end.
934
935check_and_purge([],Mod) ->
936    erlang:purge_module(Mod),
937    ok;
938check_and_purge(Pids,Mod) ->
939    io:format("purge ~w~n", [Mod]),
940    Tag = make_ref(),
941    _ = [begin
942             erlang:check_process_code(Pid,Mod,[{async,{Tag,Pid}}])
943         end || Pid <- Pids],
944    Retry = check_and_purge_receive(Pids,Tag,[]),
945    check_and_purge(Retry,Mod).
946
947check_and_purge_receive([Pid|Pids],Tag,Retry) ->
948    receive
949        {check_process_code, {Tag, Pid}, false} ->
950            check_and_purge_receive(Pids,Tag,Retry);
951        {check_process_code, {Tag, Pid}, true} ->
952            check_and_purge_receive(Pids,Tag,[Pid|Retry])
953    end;
954check_and_purge_receive([],_,Retry) ->
955    Retry.
956
957
958gen_lit(Module,Terms) ->
959    FunStrings = [lists:flatten(io_lib:format("~w() -> ~w.~n", [F,Term]))||{F,Term}<-Terms],
960    FunForms = function_forms(FunStrings),
961    Forms    = [{attribute,erl_anno:new(1),module,Module},
962                {attribute,erl_anno:new(2),export,[FA || {FA,_} <- FunForms]}] ++
963               [Function || {_, Function} <- FunForms],
964    {ok, Module, Bin} = compile:forms(Forms),
965    Bin.
966
967function_forms([]) -> [];
968function_forms([S|Ss]) ->
969    {ok, Ts,_} = erl_scan:string(S),
970    {ok, Form} = erl_parse:parse_form(Ts),
971    Fun   = element(3, Form),
972    Arity = element(4, Form),
973    [{{Fun,Arity}, Form}|function_forms(Ss)].
974
975chase_msg(0, Pid) ->
976    chase_loop(Pid);
977chase_msg(N, Master) ->
978    Pid = spawn_link(fun() -> chase_msg(N - 1,Master) end),
979    chase_loop(Pid).
980
981chase_loop(Pid) ->
982    receive
983        done ->
984            Pid ! done,
985            ok;
986        {_From,Msg} ->
987            Pid ! {self(), Msg},
988            ok = traverse(Msg),
989            chase_loop(Pid)
990    end.
991
992saturate(Pid,Time) ->
993    Es = [msg1,msg2,msg3,msg4,msg5],
994    Msg = [literals:E()||E <- Es],
995    Pid ! {self(), Msg},
996    receive
997        done -> ok
998    after Time ->
999              saturate(Pid,Time)
1000    end.
1001
1002traverse([]) -> ok;
1003traverse([H|T]) ->
1004    ok = traverse(H),
1005    traverse(T);
1006traverse(T) when is_tuple(T) -> ok;
1007traverse(B) when is_binary(B) -> ok;
1008traverse(I) when is_integer(I) -> ok;
1009traverse(#{ 1 := V1, b := V2 }) ->
1010    ok = traverse(V1),
1011    ok = traverse(V2),
1012    ok.
1013
1014
1015reloader(Mod,Code,Time) ->
1016    receive
1017        done -> ok
1018    after Time ->
1019              code:purge(Mod),
1020              {module,Mod} = erlang:load_module(Mod, Code),
1021              reloader(Mod,Code,Time)
1022    end.
1023
1024erl_544(Config) when is_list(Config) ->
1025    case file:native_name_encoding() of
1026        utf8 ->
1027            {ok, CWD} = file:get_cwd(),
1028            try
1029                Mod = erl_544,
1030                FileName = atom_to_list(Mod) ++ ".erl",
1031                Priv = proplists:get_value(priv_dir, Config),
1032                Data = proplists:get_value(data_dir, Config),
1033                {ok, FileContent} = file:read_file(filename:join(Data,
1034                                                                 FileName)),
1035                Dir = filename:join(Priv, [16#2620,16#2620,16#2620]),
1036                File = filename:join(Dir, FileName),
1037                io:format("~ts~n", [File]),
1038                ok = file:make_dir(Dir),
1039                ok = file:set_cwd(Dir),
1040                ok = file:write_file(File, [FileContent]),
1041                {ok, Mod} = compile:file(File),
1042                Res1 = (catch Mod:err()),
1043                io:format("~p~n", [Res1]),
1044                {'EXIT', {err, [{Mod, err, 0, Info1}|_]}} = Res1,
1045                File = proplists:get_value(file, Info1),
1046                Me = self(),
1047                Go = make_ref(),
1048                Tester = spawn_link(fun () ->
1049                                            Mod:wait(Me, Go),
1050                                            Mod:err()
1051                                    end),
1052                receive Go -> ok end,
1053                Res2 = process_info(Tester, current_stacktrace),
1054                io:format("~p~n", [Res2]),
1055                {current_stacktrace, Stack} = Res2,
1056                [{Mod, wait, 2, Info2}|_] = Stack,
1057                File = proplists:get_value(file, Info2),
1058                StackFun = fun(_, _, _) -> false end,
1059                FormatFun = fun (Term, _) -> io_lib:format("~tp", [Term]) end,
1060                Formated =
1061                    erl_error:format_stacktrace(1, Stack, StackFun, FormatFun),
1062                true = is_list(Formated),
1063                ok
1064            after
1065                ok = file:set_cwd(CWD)
1066            end,
1067            ok;
1068        _Enc ->
1069            {skipped, "Only run when native file name encoding is utf8"}
1070    end.
1071
1072%% Test that the copying of literals to a process during purging of
1073%% literals will cause the process to be killed if the max heap size
1074%% is exceeded.
1075max_heap_size(_Config) ->
1076    Mod = ?FUNCTION_NAME,
1077    Value = [I || I <- lists:seq(1, 5000)],
1078    Code = gen_lit(Mod, [{term,Value}]),
1079    {module,Mod} = erlang:load_module(Mod, Code),
1080    SpawnOpts = [monitor,
1081                 {max_heap_size,
1082                  #{size=>1024,
1083                    kill=>true,
1084                    error_logger=>true}}],
1085    {Pid,Ref} = spawn_opt(fun() ->
1086                                  max_heap_size_proc(Mod)
1087                          end, SpawnOpts),
1088    receive
1089        {'DOWN',Ref,process,Pid,Reason} ->
1090            killed = Reason;
1091        Other ->
1092            ct:fail({unexpected_message,Other})
1093    after 10000 ->
1094            ct:fail({process_did_not_die, Pid, erlang:process_info(Pid)})
1095    end.
1096
1097max_heap_size_proc(Mod) ->
1098    Value = Mod:term(),
1099    code:delete(Mod),
1100    code:purge(Mod),
1101    receive
1102        _ -> Value
1103    end.
1104
1105check_process_code_signal_order(Config) when is_list(Config) ->
1106    process_flag(scheduler, 1),
1107    process_flag(priority, high),
1108    {module,versions} = erlang:load_module(versions, compile_version(1, Config)),
1109    Pid = spawn_opt(versions, loop, [], [{scheduler, 1}]),
1110    true = erlang:delete_module(versions),
1111    true = erlang:check_process_code(Pid, versions),
1112    Ref = make_ref(),
1113    spam_signals(Pid, 10000),
1114    %% EXIT signal *should* arrive...
1115    exit(Pid, kill),
1116    %% ... before CPC signal...
1117    async = erlang:check_process_code(Pid, versions, [{async, Ref}]),
1118    %% ... which means that the result of the check_process_code *should* be 'false'...
1119    false = busy_wait_cpc_res(Ref),
1120    ok.
1121
1122busy_wait_cpc_res(Ref) ->
1123    receive
1124	{check_process_code, Ref, Res} ->
1125	    Res
1126    after 0 ->
1127	    busy_wait_cpc_res(Ref)
1128    end.
1129
1130spam_signals(P, N) when N =< 0 ->
1131    ok;
1132spam_signals(P, N) ->
1133    link(P),
1134    unlink(P),
1135    spam_signals(P, N-2).
1136
1137check_process_code_dirty_exec_proc(Config) when is_list(Config) ->
1138    Pid = spawn(fun () ->
1139			erts_debug:dirty_io(wait, 10000)
1140		end),
1141    receive after 100 -> ok end,
1142    false = erlang:check_process_code(Pid, non_existing_module),
1143    {status, running} = process_info(Pid, status),
1144    exit(Pid, kill),
1145    false = is_process_alive(Pid),
1146    ok.
1147
1148%% Utilities.
1149
1150make_sub_binary(Bin) when is_binary(Bin) ->
1151    {_,B1} = split_binary(list_to_binary([0,1,3,Bin,4,5,6,7]), 3),
1152    {B,_} = split_binary(B1, size(Bin)),
1153    B;
1154make_sub_binary(List) ->
1155    make_sub_binary(list_to_binary(List)).
1156
1157make_unaligned_sub_binary(Bin0) ->
1158    Bin1 = <<0:3,Bin0/binary,31:5>>,
1159    Sz = size(Bin0),
1160    <<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
1161    Bin.
1162
1163%% Add 1 bit to the size of the binary.
1164bit_sized_binary(Bin0) ->
1165    Bin = <<Bin0/binary,1:1>>,
1166    BitSize = bit_size(Bin),
1167    BitSize = 8*size(Bin) + 1,
1168    Bin.
1169
1170flush() ->
1171    receive _ -> flush() after 0 -> ok end.
1172
1173id(I) -> I.
1174
1175