4
%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
22
new_binary_types/1,t_check_process_code/1,t_check_process_code_ets/1,
23
external_fun/1,get_chunk/1,module_md5/1,make_stub/1,
24
make_stub_many_funs/1,constant_pools/1,
25
false_dependency/1,coverage/1]).
27
-include("test_server.hrl").
30
[new_binary_types,t_check_process_code,t_check_process_code_ets,
31
external_fun,get_chunk,module_md5,make_stub,make_stub_many_funs,
32
constant_pools,false_dependency,coverage].
34
new_binary_types(Config) when is_list(Config) ->
35
?line Data = ?config(data_dir, Config),
36
?line File = filename:join(Data, "my_code_test"),
37
?line {ok,my_code_test,Bin} = compile:file(File, [binary]),
38
?line {module,my_code_test} = erlang:load_module(my_code_test,
39
make_sub_binary(Bin)),
40
?line true = erlang:delete_module(my_code_test),
41
?line true = erlang:purge_module(my_code_test),
43
?line {module,my_code_test} = erlang:load_module(my_code_test,
44
make_unaligned_sub_binary(Bin)),
45
?line true = erlang:delete_module(my_code_test),
46
?line true = erlang:purge_module(my_code_test),
48
%% Try heap binaries and bad binaries.
49
?line {error,badfile} = erlang:load_module(my_code_test, <<1,2>>),
50
?line {error,badfile} = erlang:load_module(my_code_test,
51
make_sub_binary(<<1,2>>)),
52
?line {error,badfile} = erlang:load_module(my_code_test,
53
make_unaligned_sub_binary(<<1,2>>)),
54
?line {'EXIT',{badarg,_}} = (catch erlang:load_module(my_code_test,
55
bit_sized_binary(Bin))),
58
t_check_process_code(doc) -> "Test check_process_code/2.";
59
t_check_process_code(Config) when is_list(Config) ->
60
case erlang:system_info(heap_type) of
61
private -> t_check_process_code_1(Config);
62
hybrid -> {skip,"Hybrid heap"}
65
t_check_process_code_1(Config) ->
66
?line Priv = ?config(priv_dir, Config),
67
?line Data = ?config(data_dir, Config),
68
?line File = filename:join(Data, "my_code_test"),
69
?line Code = filename:join(Priv, "my_code_test"),
71
?line {ok,my_code_test} = c:c(File, [{outdir,Priv}]),
73
?line MyFun = fun(X, Y) -> X + Y end, %Confuse things.
74
?line F = my_code_test:make_fun(42),
75
?line 2 = fun_refc(F),
76
?line MyFun2 = fun(X, Y) -> X * Y end, %Confuse things.
79
%% Delete the module and call the fun again.
80
?line true = erlang:delete_module(my_code_test),
81
?line 2 = fun_refc(F),
83
?line {'EXIT',{undef,_}} = (catch my_code_test:make_fun(33)),
85
%% The fun should still be there, preventing purge.
86
?line true = erlang:check_process_code(self(), my_code_test),
88
gc(), %Place funs on the old heap.
89
?line true = erlang:check_process_code(self(), my_code_test),
91
%% Using the funs here guarantees that they will not be prematurely garbed.
93
?line 3 = MyFun(1, 2),
94
?line 12 = MyFun2(3, 4),
97
t_check_process_code1(Code, []).
99
%% The real fun was killed, but we have some fakes which look similar.
101
t_check_process_code1(Code, Fakes) ->
102
?line MyFun = fun(X, Y) -> X + Y + 1 end, %Confuse things.
103
?line false = erlang:check_process_code(self(), my_code_test),
104
?line 4 = MyFun(1, 2),
105
t_check_process_code2(Code, Fakes).
107
t_check_process_code2(Code, _) ->
108
?line false = erlang:check_process_code(self(), my_code_test),
109
?line true = erlang:purge_module(my_code_test),
111
%% In the next test we will load the same module twice.
112
?line {module,my_code_test} = code:load_abs(Code),
113
?line F = my_code_test:make_fun(37),
114
?line 2 = fun_refc(F),
115
?line false = erlang:check_process_code(self(), my_code_test),
116
?line {module,my_code_test} = code:load_abs(Code),
117
?line 2 = fun_refc(F),
119
%% Still false because the fun with the same identify is found
120
%% in the current code.
121
?line false = erlang:check_process_code(self(), my_code_test),
123
%% Some fake funs in the same module should not do any difference.
124
?line false = erlang:check_process_code(self(), my_code_test),
127
t_check_process_code3(Code, F, []).
129
t_check_process_code3(Code, F, Fakes) ->
130
Pid = spawn_link(fun() -> body(F, Fakes) end),
131
?line true = erlang:purge_module(my_code_test),
132
?line false = erlang:check_process_code(self(), my_code_test),
133
?line false = erlang:check_process_code(Pid, my_code_test),
135
?line true = erlang:delete_module(my_code_test),
136
?line true = erlang:check_process_code(self(), my_code_test),
137
?line true = erlang:check_process_code(Pid, my_code_test),
139
t_check_process_code4(Code, Pid).
141
t_check_process_code4(_Code, Pid) ->
143
receive after 1 -> ok end,
144
?line false = erlang:check_process_code(Pid, my_code_test),
151
erlang:garbage_collect(),
163
erlang:garbage_collect(),
167
t_check_process_code_ets(doc) ->
168
"Test check_process_code/2 in combination with a fun obtained from an ets table.";
169
t_check_process_code_ets(Config) when is_list(Config) ->
170
case {test_server:is_native(?MODULE),erlang:system_info(heap_type)} of
172
{skipped,"Native code"};
174
{skipped,"Hybrid heap"};
176
do_check_process_code_ets(Config)
179
do_check_process_code_ets(Config) ->
180
?line Priv = ?config(priv_dir, Config),
181
?line Data = ?config(data_dir, Config),
182
?line File = filename:join(Data, "my_code_test"),
184
?line erlang:purge_module(my_code_test),
185
?line erlang:delete_module(my_code_test),
186
?line {ok,my_code_test} = c:c(File, [{outdir,Priv}]),
188
?line T = ets:new(my_code_test, []),
189
?line ets:insert(T, {7,my_code_test:make_fun(107)}),
190
?line ets:insert(T, {8,my_code_test:make_fun(108)}),
191
?line erlang:delete_module(my_code_test),
192
?line false = erlang:check_process_code(self(), my_code_test),
194
[{7,F1}] = ets:lookup(T, 7),
195
[{8,F2}] = ets:lookup(T, 8),
196
IdleLoop = fun() -> receive _X -> ok end end,
197
RecLoop = fun(Again) ->
207
true = erlang:check_process_code(self(), my_code_test),
210
?line Pid = spawn_link(Body),
211
receive after 1 -> ok end,
212
?line true = erlang:check_process_code(Pid, my_code_test),
214
Pid ! {drop_funs,self()},
218
Other -> ?t:fail({unexpected,Other})
220
?line ?t:fail(no_funs_dropped_answer)
223
?line false = erlang:check_process_code(Pid, my_code_test),
227
{refc,Count} = erlang:fun_info(F, refc),
231
external_fun(Config) when is_list(Config) ->
232
?line false = erlang:function_exported(another_code_test, x, 1),
233
?line ExtFun = erlang:make_fun(id(another_code_test), x, 1),
234
?line {'EXIT',{undef,_}} = (catch ExtFun(answer)),
235
?line false = erlang:function_exported(another_code_test, x, 1),
236
?line false = lists:member(another_code_test, erlang:loaded()),
237
?line Data = ?config(data_dir, Config),
238
?line File = filename:join(Data, "another_code_test"),
239
?line {ok,another_code_test,Code} = compile:file(File, [binary,report]),
240
?line {module,another_code_test} = erlang:load_module(another_code_test, Code),
241
?line 42 = ExtFun(answer),
244
get_chunk(Config) when is_list(Config) ->
245
?line Data = ?config(data_dir, Config),
246
?line File = filename:join(Data, "my_code_test"),
247
?line {ok,my_code_test,Code} = compile:file(File, [binary]),
250
?line Chunk = get_chunk_ok("Atom", Code),
251
?line Chunk = get_chunk_ok("Atom", make_sub_binary(Code)),
252
?line Chunk = get_chunk_ok("Atom", make_unaligned_sub_binary(Code)),
255
?line {'EXIT',{badarg,_}} = (catch code:get_chunk(bit_sized_binary(Code), "Atom")),
256
?line {'EXIT',{badarg,_}} = (catch code:get_chunk(Code, "bad chunk id")),
258
%% Invalid beam code or missing chunk should return 'undefined'.
259
?line undefined = code:get_chunk(<<"not a beam module">>, "Atom"),
260
?line undefined = code:get_chunk(Code, "XXXX"),
264
get_chunk_ok(Chunk, Code) ->
265
case code:get_chunk(Code, Chunk) of
266
Bin when is_binary(Bin) -> Bin
269
module_md5(Config) when is_list(Config) ->
270
?line Data = ?config(data_dir, Config),
271
?line File = filename:join(Data, "my_code_test"),
272
?line {ok,my_code_test,Code} = compile:file(File, [binary]),
275
?line Chunk = module_md5_ok(Code),
276
?line Chunk = module_md5_ok(make_sub_binary(Code)),
277
?line Chunk = module_md5_ok(make_unaligned_sub_binary(Code)),
280
?line {'EXIT',{badarg,_}} = (catch code:module_md5(bit_sized_binary(Code))),
282
%% Invalid beam code should return 'undefined'.
283
?line undefined = code:module_md5(<<"not a beam module">>),
286
module_md5_ok(Code) ->
287
case code:module_md5(Code) of
288
Bin when is_binary(Bin), size(Bin) =:= 16 -> Bin
292
make_stub(Config) when is_list(Config) ->
293
%% No old code to purge if hybrid heap because of skipped test cases,
294
%% so we'll need a catch here.
295
?line (catch erlang:purge_module(my_code_test)),
297
?line Data = ?config(data_dir, Config),
298
?line File = filename:join(Data, "my_code_test"),
299
?line {ok,my_code_test,Code} = compile:file(File, [binary]),
301
?line my_code_test = code:make_stub_module(my_code_test, Code, {[],[]}),
302
?line true = erlang:delete_module(my_code_test),
303
?line true = erlang:purge_module(my_code_test),
305
?line my_code_test = code:make_stub_module(my_code_test,
306
make_unaligned_sub_binary(Code),
308
?line true = erlang:delete_module(my_code_test),
309
?line true = erlang:purge_module(my_code_test),
311
?line my_code_test = code:make_stub_module(my_code_test, zlib:gzip(Code),
313
?line true = erlang:delete_module(my_code_test),
314
?line true = erlang:purge_module(my_code_test),
317
?line {'EXIT',{badarg,_}} =
318
(catch code:make_stub_module(my_code_test, <<"bad">>, {[],[]})),
319
?line {'EXIT',{badarg,_}} =
320
(catch code:make_stub_module(my_code_test,
321
bit_sized_binary(Code),
325
make_stub_many_funs(Config) when is_list(Config) ->
326
%% No old code to purge if hybrid heap because of skipped test cases,
327
%% so we'll need a catch here.
328
?line (catch erlang:purge_module(many_funs)),
330
?line Data = ?config(data_dir, Config),
331
?line File = filename:join(Data, "many_funs"),
332
?line {ok,many_funs,Code} = compile:file(File, [binary]),
334
?line many_funs = code:make_stub_module(many_funs, Code, {[],[]}),
335
?line true = erlang:delete_module(many_funs),
336
?line true = erlang:purge_module(many_funs),
337
?line many_funs = code:make_stub_module(many_funs,
338
make_unaligned_sub_binary(Code),
340
?line true = erlang:delete_module(many_funs),
341
?line true = erlang:purge_module(many_funs),
344
?line {'EXIT',{badarg,_}} =
345
(catch code:make_stub_module(many_funs, <<"bad">>, {[],[]})),
346
?line {'EXIT',{badarg,_}} =
347
(catch code:make_stub_module(many_funs,
348
bit_sized_binary(Code),
352
constant_pools(Config) when is_list(Config) ->
353
?line Data = ?config(data_dir, Config),
354
?line File = filename:join(Data, "literals"),
355
?line {ok,literals,Code} = compile:file(File, [report,binary,constant_pool]),
356
?line {module,literals} = erlang:load_module(literals,
357
make_sub_binary(Code)),
360
?line A = literals:a(),
361
?line B = literals:b(),
362
?line C = literals:huge_bignum(),
363
?line process_flag(trap_exit, true),
366
%% Have a process WITHOUT old heap that references the literals
367
%% in the 'literals' module.
368
?line NoOldHeap = spawn_link(fun() -> no_old_heap(Self) end),
369
receive go -> ok end,
370
?line true = erlang:delete_module(literals),
371
?line false = erlang:check_process_code(NoOldHeap, literals),
372
?line erlang:check_process_code(self(), literals),
373
?line true = erlang:purge_module(literals),
374
?line NoOldHeap ! done,
376
{'EXIT',NoOldHeap,{A,B,C}} ->
379
?line ?t:fail({unexpected,Other})
381
?line {module,literals} = erlang:load_module(literals, Code),
383
%% Have a process WITH an old heap that references the literals
384
%% in the 'literals' module.
385
?line OldHeap = spawn_link(fun() -> old_heap(Self) end),
386
receive go -> ok end,
387
?line true = erlang:delete_module(literals),
388
?line false = erlang:check_process_code(OldHeap, literals),
389
?line erlang:check_process_code(self(), literals),
390
?line erlang:purge_module(literals),
391
?line OldHeap ! done,
393
{'EXIT',OldHeap,{A,B,C,[1,2,3|_]=Seq}} when length(Seq) =:= 16 ->
397
no_old_heap(Parent) ->
400
Res = {A,B,literals:huge_bignum()},
410
Res = {A,B,literals:huge_bignum(),lists:seq(1, 16)},
419
case process_info(self(), [heap_size,total_heap_size]) of
420
[{heap_size,Sz},{total_heap_size,Total}] when Sz < Total ->
426
%% OTP-7559: c_p->cp could contain garbage and create a false dependency
427
%% to a module in a process. (Thanks to Richard Carlsson.)
428
false_dependency(Config) when is_list(Config) ->
429
?line Data = ?config(data_dir, Config),
430
?line File = filename:join(Data, "cpbugx"),
431
?line {ok,cpbugx,Code} = compile:file(File, [binary,report]),
433
do_false_dependency(fun cpbugx:before/0, Code),
434
do_false_dependency(fun cpbugx:before2/0, Code),
435
do_false_dependency(fun cpbugx:before3/0, Code),
437
%% %% Spawn process. Make sure it has called cpbugx:before/0 and returned.
439
%% ?line Pid = spawn_link(fun() -> false_dependency_loop(Parent) end),
440
%% ?line receive initialized -> ok end,
442
%% %% Reload the module. Make sure the process is still alive.
443
%% ?line {module,cpbugx} = erlang:load_module(cpbugx, Bin),
444
%% ?line io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))),
445
%% ?line true = is_process_alive(Pid),
447
%% %% There should not be any dependency to cpbugx.
448
%% ?line false = erlang:check_process_code(Pid, cpbugx),
453
%% %% Kill the process.
454
%% ?line unlink(Pid), exit(Pid, kill),
457
do_false_dependency(Init, Code) ->
458
?line {module,cpbugx} = erlang:load_module(cpbugx, Code),
460
%% Spawn process. Make sure it has the appropriate init function
461
%% and returned. CP should not contain garbage after the return.
463
?line Pid = spawn_link(fun() -> false_dependency_loop(Parent, Init) end),
464
?line receive initialized -> ok end,
466
%% Reload the module. Make sure the process is still alive.
467
?line {module,cpbugx} = erlang:load_module(cpbugx, Code),
468
?line io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))),
469
?line true = is_process_alive(Pid),
471
%% There should not be any dependency to cpbugx.
472
?line false = erlang:check_process_code(Pid, cpbugx),
474
%% Kill the process and completely unload the code.
475
?line unlink(Pid), exit(Pid, kill),
476
?line true = erlang:purge_module(cpbugx),
477
?line true = erlang:delete_module(cpbugx),
478
?line true = erlang:purge_module(cpbugx),
481
false_dependency_loop(Parent, Init) ->
483
Parent ! initialized,
485
_ -> false_dependency_loop(Parent, Init)
488
coverage(Config) when is_list(Config) ->
489
?line code:is_module_native(?MODULE),
490
?line {'EXIT',{badarg,_}} = (catch erlang:purge_module({a,b,c})),
491
?line {'EXIT',{badarg,_}} = (catch code:is_module_native({a,b,c})),
492
?line {'EXIT',{badarg,_}} = (catch erlang:check_process_code(not_a_pid, ?MODULE)),
493
?line {'EXIT',{badarg,_}} = (catch erlang:check_process_code(self(), [not_a_module])),
494
?line {'EXIT',{badarg,_}} = (catch erlang:delete_module([a,b,c])),
495
?line {'EXIT',{badarg,_}} = (catch erlang:module_loaded(42)),
500
make_sub_binary(Bin) when is_binary(Bin) ->
501
{_,B1} = split_binary(list_to_binary([0,1,3,Bin,4,5,6,7]), 3),
502
{B,_} = split_binary(B1, size(Bin)),
504
make_sub_binary(List) ->
505
make_sub_binary(list_to_binary(List)).
507
make_unaligned_sub_binary(Bin0) ->
508
Bin1 = <<0:3,Bin0/binary,31:5>>,
510
<<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
513
%% Add 1 bit to the size of the binary.
514
bit_sized_binary(Bin0) ->
515
Bin = <<Bin0/binary,1:1>>,
516
BitSize = bit_size(Bin),
517
BitSize = 8*size(Bin) + 1,