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