1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 1999-2012. All Rights Reserved.
    5: %% 
    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/.
   11: %% 
   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
   15: %% under the License.
   16: %% 
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: -module(code_SUITE).
   21: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   22: 	 init_per_group/2,end_per_group/2,
   23: 	 versions/1,new_binary_types/1,
   24: 	 t_check_process_code/1,t_check_old_code/1,
   25: 	 t_check_process_code_ets/1,
   26: 	 external_fun/1,get_chunk/1,module_md5/1,make_stub/1,
   27: 	 make_stub_many_funs/1,constant_pools/1,constant_refc_binaries/1,
   28: 	 false_dependency/1,coverage/1,fun_confusion/1]).
   29: 
   30: -define(line_trace, 1).
   31: -include_lib("test_server/include/test_server.hrl").
   32: 
   33: suite() -> [{ct_hooks,[ts_install_cth]}].
   34: 
   35: all() -> 
   36:     [versions, new_binary_types, t_check_process_code,
   37:      t_check_process_code_ets, t_check_old_code, external_fun, get_chunk,
   38:      module_md5, make_stub, make_stub_many_funs,
   39:      constant_pools, constant_refc_binaries, false_dependency,
   40:      coverage, fun_confusion].
   41: 
   42: groups() -> 
   43:     [].
   44: 
   45: init_per_suite(Config) ->
   46:     erts_debug:set_internal_state(available_internal_state, true),
   47:     Config.
   48: 
   49: end_per_suite(_Config) ->
   50:     catch erts_debug:set_internal_state(available_internal_state, false),
   51:     ok.
   52: 
   53: init_per_group(_GroupName, Config) ->
   54:     Config.
   55: 
   56: end_per_group(_GroupName, Config) ->
   57:     Config.
   58: 
   59: %% Make sure that only two versions of a module can be loaded.
   60: versions(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:     P1 ! P2 ! done,
   76:     _ = monitor(process, P1),
   77:     _ = monitor(process, P2),
   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: 
   89: compile_version(Version, Config) ->
   90:     Data = ?config(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: 
   96: load_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: 
  107: check_version(Pid) ->
  108:     Pid ! {self(),version},
  109:     receive
  110: 	{Pid,version,Version} ->
  111: 	    Version
  112:     end.
  113: 
  114: new_binary_types(Config) when is_list(Config) ->
  115:     ?line Data = ?config(data_dir, Config),
  116:     ?line File = filename:join(Data, "my_code_test"),
  117:     ?line {ok,my_code_test,Bin} = compile:file(File, [binary]),
  118:     ?line {module,my_code_test} = erlang:load_module(my_code_test,
  119: 						     make_sub_binary(Bin)),
  120:     ?line true = erlang:delete_module(my_code_test),
  121:     ?line true = erlang:purge_module(my_code_test),
  122: 
  123:     ?line {module,my_code_test} = erlang:load_module(my_code_test,
  124: 						     make_unaligned_sub_binary(Bin)),
  125:     ?line true = erlang:delete_module(my_code_test),
  126:     ?line true = erlang:purge_module(my_code_test),
  127: 
  128:     %% Try heap binaries and bad binaries.
  129:     ?line {error,badfile} = erlang:load_module(my_code_test, <<1,2>>),
  130:     ?line {error,badfile} = erlang:load_module(my_code_test,
  131: 						make_sub_binary(<<1,2>>)),
  132:     ?line {error,badfile} = erlang:load_module(my_code_test,
  133: 						make_unaligned_sub_binary(<<1,2>>)),
  134:     ?line {'EXIT',{badarg,_}} = (catch erlang:load_module(my_code_test,
  135: 						      bit_sized_binary(Bin))),
  136:     ok.
  137: 
  138: t_check_process_code(Config) when is_list(Config) ->
  139:     ?line Priv = ?config(priv_dir, Config),
  140:     ?line Data = ?config(data_dir, Config),
  141:     ?line File = filename:join(Data, "my_code_test"),
  142:     ?line Code = filename:join(Priv, "my_code_test"),
  143: 
  144:     ?line {ok,my_code_test} = c:c(File, [{outdir,Priv}]),
  145: 
  146:     ?line MyFun = fun(X, Y) -> X + Y end,	%Confuse things.
  147:     ?line F = my_code_test:make_fun(42),
  148:     ?line 2 = fun_refc(F),
  149:     ?line MyFun2 = fun(X, Y) -> X * Y end,	%Confuse things.
  150:     ?line 44 = F(2),
  151: 
  152:     %% Delete the module and call the fun again.
  153:     ?line true = erlang:delete_module(my_code_test),
  154:     ?line 2 = fun_refc(F),
  155:     ?line 45 = F(3),
  156:     ?line {'EXIT',{undef,_}} = (catch my_code_test:make_fun(33)),
  157: 
  158:     %% The fun should still be there, preventing purge.
  159:     ?line true = erlang:check_process_code(self(), my_code_test),
  160:     gc(),
  161:     gc(),					%Place funs on the old heap.
  162:     ?line true = erlang:check_process_code(self(), my_code_test),
  163: 
  164:     %% Using the funs here guarantees that they will not be prematurely garbed.
  165:     ?line 48 = F(6),
  166:     ?line 3 = MyFun(1, 2),
  167:     ?line 12 = MyFun2(3, 4),
  168: 
  169:     %% Kill all funs.
  170:     t_check_process_code1(Code, []).
  171: 
  172: %% The real fun was killed, but we have some fakes which look similar.
  173: 
  174: t_check_process_code1(Code, Fakes) ->
  175:     ?line MyFun = fun(X, Y) -> X + Y + 1 end,	%Confuse things.
  176:     ?line false = erlang:check_process_code(self(), my_code_test),
  177:     ?line 4 = MyFun(1, 2),
  178:     t_check_process_code2(Code, Fakes).
  179: 
  180: t_check_process_code2(Code, _) ->
  181:     ?line false = erlang:check_process_code(self(), my_code_test),
  182:     ?line true = erlang:purge_module(my_code_test),
  183: 
  184:     %% In the next test we will load the same module twice.
  185:     ?line {module,my_code_test} = code:load_abs(Code),
  186:     ?line F = my_code_test:make_fun(37),
  187:     ?line 2 = fun_refc(F),
  188:     ?line false = erlang:check_process_code(self(), my_code_test),
  189:     ?line {module,my_code_test} = code:load_abs(Code),
  190:     ?line 2 = fun_refc(F),
  191: 
  192:     %% Still false because the fun with the same identify is found
  193:     %% in the current code.
  194:     ?line false = erlang:check_process_code(self(), my_code_test),
  195:     
  196:     %% Some fake funs in the same module should not do any difference.
  197:     ?line false = erlang:check_process_code(self(), my_code_test),
  198: 
  199:     38 = F(1),
  200:     t_check_process_code3(Code, F, []).
  201: 
  202: t_check_process_code3(Code, F, Fakes) ->
  203:     Pid = spawn_link(fun() -> body(F, Fakes) end),
  204:     ?line true = erlang:purge_module(my_code_test),
  205:     ?line false = erlang:check_process_code(self(), my_code_test),
  206:     ?line false = erlang:check_process_code(Pid, my_code_test),
  207: 
  208:     ?line true = erlang:delete_module(my_code_test),
  209:     ?line true = erlang:check_process_code(self(), my_code_test),
  210:     ?line true = erlang:check_process_code(Pid, my_code_test),
  211:     39 = F(2),
  212:     t_check_process_code4(Code, Pid).
  213: 
  214: t_check_process_code4(_Code, Pid) ->
  215:     Pid ! drop_funs,
  216:     receive after 1 -> ok end,
  217:     ?line false = erlang:check_process_code(Pid, my_code_test),
  218:     ok.
  219: 
  220: body(F, Fakes) ->
  221:     receive
  222: 	jog ->
  223: 	    40 = F(3),
  224: 	    erlang:garbage_collect(),
  225: 	    body(F, Fakes);
  226: 	drop_funs ->
  227: 	    dropped_body()
  228:     end.
  229: 
  230: dropped_body() ->
  231:     receive
  232: 	X -> exit(X)
  233:     end.
  234: 
  235: gc() ->
  236:     erlang:garbage_collect(),
  237:     gc1().
  238: gc1() -> ok.
  239: 
  240: t_check_process_code_ets(doc) ->
  241:     "Test check_process_code/2 in combination with a fun obtained from an ets table.";
  242: t_check_process_code_ets(Config) when is_list(Config) ->
  243:     case test_server:is_native(?MODULE) of
  244: 	true ->
  245: 	    {skip,"Native code"};
  246: 	false ->
  247: 	    do_check_process_code_ets(Config)
  248:     end.
  249: 
  250: do_check_process_code_ets(Config) ->
  251:     ?line Priv = ?config(priv_dir, Config),
  252:     ?line Data = ?config(data_dir, Config),
  253:     ?line File = filename:join(Data, "my_code_test"),
  254: 
  255:     ?line erlang:purge_module(my_code_test),
  256:     ?line erlang:delete_module(my_code_test),
  257:     ?line {ok,my_code_test} = c:c(File, [{outdir,Priv}]),
  258: 
  259:     ?line T = ets:new(my_code_test, []),
  260:     ?line ets:insert(T, {7,my_code_test:make_fun(107)}),
  261:     ?line ets:insert(T, {8,my_code_test:make_fun(108)}),
  262:     ?line erlang:delete_module(my_code_test),
  263:     ?line false = erlang:check_process_code(self(), my_code_test),
  264:     Body = fun() ->
  265: 		   [{7,F1}] = ets:lookup(T, 7),
  266: 		   [{8,F2}] = ets:lookup(T, 8),
  267: 		   IdleLoop = fun() -> receive _X -> ok end end,
  268: 		   RecLoop = fun(Again) ->
  269: 				     receive
  270: 					 call -> 110 = F1(3),
  271: 						 100 = F2(-8),
  272: 						 Again(Again);
  273: 					 {drop_funs,To} ->
  274: 					     To ! funs_dropped,
  275: 					     IdleLoop()
  276: 				     end
  277: 			     end,
  278: 		   true = erlang:check_process_code(self(), my_code_test),
  279: 		   RecLoop(RecLoop)
  280: 	   end,
  281:     ?line Pid = spawn_link(Body),
  282:     receive after 1 -> ok end,
  283:     ?line true = erlang:check_process_code(Pid, my_code_test),
  284:     Pid ! call,
  285:     Pid ! {drop_funs,self()},
  286: 
  287:     receive
  288: 	funs_dropped -> ok;
  289: 	Other -> ?t:fail({unexpected,Other})
  290:     after 10000 ->
  291: 	    ?line ?t:fail(no_funs_dropped_answer)
  292:     end,
  293: 
  294:     ?line false = erlang:check_process_code(Pid, my_code_test),
  295:     ok.
  296: 
  297: fun_refc(F) ->
  298:     {refc,Count} = erlang:fun_info(F, refc),
  299:     Count.
  300: 
  301: 
  302: %% Test the erlang:check_old_code/1 BIF.
  303: t_check_old_code(Config) when is_list(Config) ->
  304:     ?line Data = ?config(data_dir, Config),
  305:     ?line File = filename:join(Data, "my_code_test"),
  306: 
  307:     ?line erlang:purge_module(my_code_test),
  308:     ?line erlang:delete_module(my_code_test),
  309:     ?line catch erlang:purge_module(my_code_test),
  310: 
  311:     ?line false = erlang:check_old_code(my_code_test),
  312: 
  313:     ?line {ok,my_code_test,Code} = compile:file(File, [binary]),
  314:     ?line {module,my_code_test} = code:load_binary(my_code_test, File, Code),
  315:     
  316:     ?line false = erlang:check_old_code(my_code_test),
  317:     ?line {module,my_code_test} = code:load_binary(my_code_test, File, Code),
  318:     ?line true = erlang:check_old_code(my_code_test),
  319: 
  320:     ?line true = erlang:purge_module(my_code_test),
  321:     ?line true = erlang:delete_module(my_code_test),
  322:     ?line true = erlang:purge_module(my_code_test),
  323: 
  324:     ?line {'EXIT',_} = (catch erlang:check_old_code([])),
  325:     
  326:     ok.
  327: 
  328: external_fun(Config) when is_list(Config) ->
  329:     ?line false = erlang:function_exported(another_code_test, x, 1),
  330:     AnotherCodeTest = id(another_code_test),
  331:     ExtFun = fun AnotherCodeTest:x/1,
  332:     ?line {'EXIT',{undef,_}} = (catch ExtFun(answer)),
  333:     ?line false = erlang:function_exported(another_code_test, x, 1),
  334:     ?line false = lists:member(another_code_test, erlang:loaded()),
  335:     ?line Data = ?config(data_dir, Config),
  336:     ?line File = filename:join(Data, "another_code_test"),
  337:     ?line {ok,another_code_test,Code} = compile:file(File, [binary,report]),
  338:     ?line {module,another_code_test} = erlang:load_module(another_code_test, Code),
  339:     ?line 42 = ExtFun(answer),
  340:     ok.
  341: 
  342: get_chunk(Config) when is_list(Config) ->
  343:     ?line Data = ?config(data_dir, Config),
  344:     ?line File = filename:join(Data, "my_code_test"),
  345:     ?line {ok,my_code_test,Code} = compile:file(File, [binary]),
  346: 
  347:     %% Should work.
  348:     ?line Chunk = get_chunk_ok("Atom", Code),
  349:     ?line Chunk = get_chunk_ok("Atom", make_sub_binary(Code)),
  350:     ?line Chunk = get_chunk_ok("Atom", make_unaligned_sub_binary(Code)),
  351: 
  352:     %% Should fail.
  353:     ?line {'EXIT',{badarg,_}} = (catch code:get_chunk(bit_sized_binary(Code), "Atom")),
  354:     ?line {'EXIT',{badarg,_}} = (catch code:get_chunk(Code, "bad chunk id")),
  355: 
  356:     %% Invalid beam code or missing chunk should return 'undefined'.
  357:     ?line undefined = code:get_chunk(<<"not a beam module">>, "Atom"),
  358:     ?line undefined = code:get_chunk(Code, "XXXX"),
  359: 
  360:     ok.
  361: 
  362: get_chunk_ok(Chunk, Code) ->
  363:     case code:get_chunk(Code, Chunk) of
  364: 	Bin when is_binary(Bin) -> Bin
  365:     end.
  366: 
  367: module_md5(Config) when is_list(Config) ->
  368:     ?line Data = ?config(data_dir, Config),
  369:     ?line File = filename:join(Data, "my_code_test"),
  370:     ?line {ok,my_code_test,Code} = compile:file(File, [binary]),
  371: 
  372:     %% Should work.
  373:     ?line Chunk = module_md5_ok(Code),
  374:     ?line Chunk = module_md5_ok(make_sub_binary(Code)),
  375:     ?line Chunk = module_md5_ok(make_unaligned_sub_binary(Code)),
  376: 
  377:     %% Should fail.
  378:     ?line {'EXIT',{badarg,_}} = (catch code:module_md5(bit_sized_binary(Code))),
  379: 
  380:     %% Invalid beam code should return 'undefined'.
  381:     ?line undefined = code:module_md5(<<"not a beam module">>),
  382:     ok.
  383:     
  384: module_md5_ok(Code) ->
  385:     case code:module_md5(Code) of
  386: 	Bin when is_binary(Bin), size(Bin) =:= 16 -> Bin
  387:     end.
  388: 
  389: 
  390: make_stub(Config) when is_list(Config) ->
  391:     catch erlang:purge_module(my_code_test),
  392: 
  393:     ?line Data = ?config(data_dir, Config),
  394:     ?line File = filename:join(Data, "my_code_test"),
  395:     ?line {ok,my_code_test,Code} = compile:file(File, [binary]),
  396: 
  397:     ?line my_code_test = code:make_stub_module(my_code_test, Code, {[],[]}),
  398:     ?line true = erlang:delete_module(my_code_test),
  399:     ?line true = erlang:purge_module(my_code_test),
  400: 
  401:     ?line my_code_test = code:make_stub_module(my_code_test, 
  402:  					       make_unaligned_sub_binary(Code),
  403:  					       {[],[]}),
  404:     ?line true = erlang:delete_module(my_code_test),
  405:     ?line true = erlang:purge_module(my_code_test),
  406: 
  407:     ?line my_code_test = code:make_stub_module(my_code_test, zlib:gzip(Code),
  408:  					       {[],[]}),
  409:     ?line true = erlang:delete_module(my_code_test),
  410:     ?line true = erlang:purge_module(my_code_test),
  411: 
  412:     %% Should fail.
  413:     ?line {'EXIT',{badarg,_}} =
  414: 	(catch code:make_stub_module(my_code_test, <<"bad">>, {[],[]})),
  415:     ?line {'EXIT',{badarg,_}} =
  416: 	(catch code:make_stub_module(my_code_test,
  417: 				     bit_sized_binary(Code),
  418: 				     {[],[]})),
  419:     ?line {'EXIT',{badarg,_}} =
  420: 	(catch code:make_stub_module(my_code_test_with_wrong_name,
  421: 				     Code, {[],[]})),
  422:     ok.
  423: 
  424: make_stub_many_funs(Config) when is_list(Config) ->
  425:     catch erlang:purge_module(many_funs),
  426: 
  427:     ?line Data = ?config(data_dir, Config),
  428:     ?line File = filename:join(Data, "many_funs"),
  429:     ?line {ok,many_funs,Code} = compile:file(File, [binary]),
  430: 
  431:     ?line many_funs = code:make_stub_module(many_funs, Code, {[],[]}),
  432:     ?line true = erlang:delete_module(many_funs),
  433:     ?line true = erlang:purge_module(many_funs),
  434:     ?line many_funs = code:make_stub_module(many_funs, 
  435:  					       make_unaligned_sub_binary(Code),
  436:  					       {[],[]}),
  437:     ?line true = erlang:delete_module(many_funs),
  438:     ?line true = erlang:purge_module(many_funs),
  439: 
  440:     %% Should fail.
  441:     ?line {'EXIT',{badarg,_}} =
  442: 	(catch code:make_stub_module(many_funs, <<"bad">>, {[],[]})),
  443:     ?line {'EXIT',{badarg,_}} =
  444: 	(catch code:make_stub_module(many_funs,
  445: 				     bit_sized_binary(Code),
  446: 				     {[],[]})),
  447:     ok.
  448: 
  449: constant_pools(Config) when is_list(Config) ->
  450:     ?line Data = ?config(data_dir, Config),
  451:     ?line File = filename:join(Data, "literals"),
  452:     ?line {ok,literals,Code} = compile:file(File, [report,binary]),
  453:     ?line {module,literals} = erlang:load_module(literals,
  454: 						 make_sub_binary(Code)),
  455: 
  456:     %% Initialize.
  457:     ?line A = literals:a(),
  458:     ?line B = literals:b(),
  459:     ?line C = literals:huge_bignum(),
  460:     ?line process_flag(trap_exit, true),
  461:     Self = self(),
  462: 
  463:     %% Have a process WITHOUT old heap that references the literals
  464:     %% in the 'literals' module.
  465:     ?line NoOldHeap = spawn_link(fun() -> no_old_heap(Self) end),
  466:     receive go -> ok end,
  467:     ?line true = erlang:delete_module(literals),
  468:     ?line false = erlang:check_process_code(NoOldHeap, literals),
  469:     ?line erlang:check_process_code(self(), literals),
  470:     ?line true = erlang:purge_module(literals),
  471:     ?line NoOldHeap ! done,
  472:     ?line receive
  473: 	      {'EXIT',NoOldHeap,{A,B,C}} ->
  474: 		  ok;
  475: 	      Other ->
  476: 		  ?line ?t:fail({unexpected,Other})
  477: 	  end,
  478:     ?line {module,literals} = erlang:load_module(literals, Code),
  479: 
  480:     %% Have a process WITH an old heap that references the literals
  481:     %% in the 'literals' module.
  482:     ?line OldHeap = spawn_link(fun() -> old_heap(Self) end),
  483:     receive go -> ok end,
  484:     ?line true = erlang:delete_module(literals),
  485:     ?line false = erlang:check_process_code(OldHeap, literals),
  486:     ?line erlang:check_process_code(self(), literals),
  487:     ?line erlang:purge_module(literals),
  488:     ?line OldHeap ! done,
  489:     receive
  490: 	{'EXIT',OldHeap,{A,B,C,[1,2,3|_]=Seq}} when length(Seq) =:= 16 ->
  491: 	    ok
  492:     end.
  493: 
  494: no_old_heap(Parent) ->
  495:     A = literals:a(),
  496:     B = literals:b(),
  497:     Res = {A,B,literals:huge_bignum()},
  498:     Parent ! go,
  499:     receive
  500: 	done ->
  501: 	    exit(Res)
  502:     end.
  503: 
  504: old_heap(Parent) ->
  505:     A = literals:a(),
  506:     B = literals:b(),
  507:     Res = {A,B,literals:huge_bignum(),lists:seq(1, 16)},
  508:     create_old_heap(),
  509:     Parent ! go,
  510:     receive
  511: 	done ->
  512: 	    exit(Res)
  513:     end.
  514: 
  515: create_old_heap() ->
  516:     case process_info(self(), [heap_size,total_heap_size]) of
  517: 	[{heap_size,Sz},{total_heap_size,Total}] when Sz < Total ->
  518: 	    ok;
  519: 	_ ->
  520: 	    create_old_heap()
  521:     end.
  522: 
  523: constant_refc_binaries(Config) when is_list(Config) ->
  524:     wait_for_memory_deallocations(),
  525:     Bef = memory_binary(),
  526:     io:format("Binary data (bytes) before test: ~p\n", [Bef]),
  527: 
  528:     %% Compile the the literals module.
  529:     Data = ?config(data_dir, Config),
  530:     File = filename:join(Data, "literals"),
  531:     {ok,literals,Code} = compile:file(File, [report,binary]),
  532: 
  533:     %% Load the code and make sure that the binary is a refc binary.
  534:     {module,literals} = erlang:load_module(literals, Code),
  535:     Bin = literals:binary(),
  536:     Sz = byte_size(Bin),
  537:     Check = erlang:md5(Bin),
  538:     io:format("Size of literal refc binary: ~p\n", [Sz]),
  539:     {refc_binary,Sz,_,_} = erts_debug:get_internal_state({binary_info,Bin}),
  540:     true = erlang:delete_module(literals),
  541:     false = erlang:check_process_code(self(), literals),
  542:     true = erlang:purge_module(literals),
  543: 
  544:     %% Now try to provoke a memory leak.
  545:     provoke_mem_leak(10, Code, Check),
  546: 
  547:     %% Calculate the change in allocated binary data.
  548:     erlang:garbage_collect(),
  549:     wait_for_memory_deallocations(),
  550:     Aft = memory_binary(),
  551:     io:format("Binary data (bytes) after test: ~p", [Aft]),
  552:     Diff = Aft - Bef,
  553:     if
  554: 	Diff < 0 ->
  555: 	    io:format("~p less bytes", [abs(Diff)]);
  556: 	Diff > 0 ->
  557: 	    io:format("~p more bytes", [Diff]);
  558: 	true ->
  559: 	    ok
  560:     end,
  561: 
  562:     %% Test for leaks. We must accept some natural variations in
  563:     %% the size of allocated binaries.
  564:     if
  565: 	Diff > 64*1024 ->
  566: 	    ?t:fail(binary_leak);
  567: 	true ->
  568: 	    ok
  569:     end.
  570: 
  571: memory_binary() ->
  572:     try
  573: 	erlang:memory(binary)
  574:     catch
  575: 	error:notsup ->
  576: 	    0
  577:     end.
  578: 
  579: provoke_mem_leak(0, _, _) -> ok;
  580: provoke_mem_leak(N, Code, Check) ->
  581:     {module,literals} = erlang:load_module(literals, Code),
  582: 
  583:     %% Create several processes with references to the literal binary.
  584:     Self = self(),
  585:     Pids = [spawn_link(fun() ->
  586: 			       create_binaries(Self, NumRefs, Check)
  587: 		       end) || NumRefs <- lists:seq(1, 10)],
  588:     [receive {started,Pid} -> ok end || Pid <- Pids],
  589: 
  590:     %% Make the code old and remove references to the constant pool
  591:     %% in all processes.
  592:     true = erlang:delete_module(literals),
  593:     Ms = [spawn_monitor(fun() ->
  594: 				false = erlang:check_process_code(Pid, literals)
  595: 			end) || Pid <- Pids],
  596:     [receive
  597: 	 {'DOWN',R,process,P,normal} ->
  598: 	     ok
  599:      end || {P,R} <- Ms],
  600: 
  601:     %% Purge the code.
  602:     true = erlang:purge_module(literals),
  603: 
  604:     %% Tell the processes that the code has been purged.
  605:     [begin
  606: 	 monitor(process, Pid),
  607: 	 Pid ! purged
  608:      end || Pid <- Pids],
  609: 
  610:     %% Wait for all processes to terminate.
  611:     [receive
  612: 	 {'DOWN',_,process,Pid,normal} ->
  613: 	     ok
  614:      end || Pid <- Pids],
  615: 
  616:     %% We now expect that the binary has been deallocated.
  617:     provoke_mem_leak(N-1, Code, Check).
  618: 
  619: create_binaries(Parent, NumRefs, Check) ->
  620:     Bin = literals:binary(),
  621:     Bins = lists:duplicate(NumRefs, Bin),
  622:     {bits,Bits} = literals:bits(),
  623:     Parent ! {started,self()},
  624:     receive
  625: 	purged ->
  626: 	    %% The code has been purged. Now make sure that
  627: 	    %% the binaries haven't been corrupted.
  628: 	    Check = erlang:md5(Bin),
  629: 	    [Bin = B || B <- Bins],
  630: 	    <<42:13,Bin/binary>> = Bits,
  631: 
  632: 	    %% Remove all references to the binaries
  633: 	    %% Doing it explicitly like this ensures that
  634: 	    %% the binaries are gone when the parent process
  635: 	    %% receives the 'DOWN' message.
  636: 	    erlang:garbage_collect()
  637:     end.
  638: 
  639: wait_for_memory_deallocations() ->
  640:     try
  641: 	erts_debug:set_internal_state(wait, deallocations)
  642:     catch
  643: 	error:undef ->
  644: 	    erts_debug:set_internal_state(available_internal_state, true),
  645: 	    wait_for_memory_deallocations()
  646:     end.
  647: 
  648: %% OTP-7559: c_p->cp could contain garbage and create a false dependency
  649: %% to a module in a process. (Thanks to Richard Carlsson.)
  650: false_dependency(Config) when is_list(Config) ->
  651:     ?line Data = ?config(data_dir, Config),
  652:     ?line File = filename:join(Data, "cpbugx"),
  653:     ?line {ok,cpbugx,Code} = compile:file(File, [binary,report]),
  654: 
  655:     do_false_dependency(fun cpbugx:before/0, Code),
  656:     do_false_dependency(fun cpbugx:before2/0, Code),
  657:     do_false_dependency(fun cpbugx:before3/0, Code),
  658: 
  659: %%     %% Spawn process. Make sure it has called cpbugx:before/0 and returned.
  660: %%     Parent = self(),
  661: %%     ?line Pid = spawn_link(fun() -> false_dependency_loop(Parent) end),
  662: %%     ?line receive initialized -> ok end,
  663: 
  664: %%     %% Reload the module. Make sure the process is still alive.
  665: %%     ?line {module,cpbugx} = erlang:load_module(cpbugx, Bin),
  666: %%     ?line io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))),
  667: %%     ?line true = is_process_alive(Pid),
  668: 
  669: %%     %% There should not be any dependency to cpbugx.
  670: %%     ?line false = erlang:check_process_code(Pid, cpbugx),
  671:     
  672: 
  673: 
  674: 
  675: %%     %% Kill the process.
  676: %%     ?line unlink(Pid), exit(Pid, kill),
  677:     ok.
  678: 
  679: do_false_dependency(Init, Code) ->
  680:     ?line {module,cpbugx} = erlang:load_module(cpbugx, Code),
  681: 
  682:     %% Spawn process. Make sure it has the appropriate init function
  683:     %% and returned. CP should not contain garbage after the return.
  684:     Parent = self(),
  685:     ?line Pid = spawn_link(fun() -> false_dependency_loop(Parent, Init, true) end),
  686:     ?line receive initialized -> ok end,
  687: 
  688:     %% Reload the module. Make sure the process is still alive.
  689:     ?line {module,cpbugx} = erlang:load_module(cpbugx, Code),
  690:     ?line io:put_chars(binary_to_list(element(2, process_info(Pid, backtrace)))),
  691:     ?line true = is_process_alive(Pid),
  692: 
  693:     %% There should not be any dependency to cpbugx.
  694:     ?line false = erlang:check_process_code(Pid, cpbugx),
  695: 
  696:     %% Kill the process and completely unload the code.
  697:     ?line unlink(Pid), exit(Pid, kill),
  698:     ?line true = erlang:purge_module(cpbugx),
  699:     ?line true = erlang:delete_module(cpbugx),
  700:     ?line code:is_module_native(cpbugx),  % test is_module_native on deleted code
  701:     ?line true = erlang:purge_module(cpbugx),
  702:     ?line code:is_module_native(cpbugx),  % test is_module_native on purged code
  703:     ok.
  704:     
  705: false_dependency_loop(Parent, Init, SendInitAck) ->
  706:     Init(),
  707:     case SendInitAck of
  708: 	true -> Parent ! initialized;
  709: 	false -> void
  710: 		 %% Just send one init-ack. I guess the point of this test
  711: 		 %% wasn't to fill parents msg-queue (?). Seen to cause
  712: 		 %% out-of-mem (on halfword-vm for some reason) by
  713: 		 %% 91 million msg in queue. /sverker
  714:     end,
  715:     receive
  716: 	_ -> false_dependency_loop(Parent, Init, false)
  717:     end.
  718: 
  719: coverage(Config) when is_list(Config) ->
  720:     ?line code:is_module_native(?MODULE),
  721:     ?line {'EXIT',{badarg,_}} = (catch erlang:purge_module({a,b,c})),
  722:     ?line {'EXIT',{badarg,_}} = (catch code:is_module_native({a,b,c})),
  723:     ?line {'EXIT',{badarg,_}} = (catch erlang:check_process_code(not_a_pid, ?MODULE)),
  724:     ?line {'EXIT',{badarg,_}} = (catch erlang:check_process_code(self(), [not_a_module])),
  725:     ?line {'EXIT',{badarg,_}} = (catch erlang:delete_module([a,b,c])),
  726:     ?line {'EXIT',{badarg,_}} = (catch erlang:module_loaded(42)),
  727:     ok.
  728: 
  729: fun_confusion(Config) when is_list(Config) ->
  730:     Data = ?config(data_dir, Config),
  731:     Src = filename:join(Data, "fun_confusion"),
  732:     Mod = fun_confusion,
  733: 
  734:     %% Load first version of module.
  735:     compile_load(Mod, Src, 1),
  736:     F1 = Mod:f(),
  737:     1 = F1(),
  738: 
  739:     %% Load second version of module.
  740:     compile_load(Mod, Src, 2),
  741:     F2 = Mod:f(),
  742: 
  743:     %% F1 should refer to the old code, not the newly loaded code.
  744:     1 = F1(),
  745:     2 = F2(),
  746:     ok.
  747: 
  748: compile_load(Mod, Src, Ver) ->
  749:     {ok,Mod,Code1} = compile:file(Src, [binary,{d,version,Ver}]),
  750:     {module,Mod} = code:load_binary(Mod, "fun_confusion.beam", Code1),
  751:     ok.
  752: 
  753: %% Utilities.
  754: 
  755: make_sub_binary(Bin) when is_binary(Bin) ->
  756:     {_,B1} = split_binary(list_to_binary([0,1,3,Bin,4,5,6,7]), 3),
  757:     {B,_} = split_binary(B1, size(Bin)),
  758:     B;
  759: make_sub_binary(List) ->
  760:     make_sub_binary(list_to_binary(List)).
  761: 
  762: make_unaligned_sub_binary(Bin0) ->
  763:     Bin1 = <<0:3,Bin0/binary,31:5>>,
  764:     Sz = size(Bin0),
  765:     <<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
  766:     Bin.
  767: 
  768: %% Add 1 bit to the size of the binary.
  769: bit_sized_binary(Bin0) ->
  770:     Bin = <<Bin0/binary,1:1>>,
  771:     BitSize = bit_size(Bin),
  772:     BitSize = 8*size(Bin) + 1,
  773:     Bin.
  774: 
  775: id(I) -> I.