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.