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