1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1996-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: -module(proc_lib_SUITE). 20: 21: %% 22: %% Define to run outside of test server 23: %% 24: %%-define(STANDALONE,1). 25: 26: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 27: init_per_group/2,end_per_group/2, 28: crash/1, sync_start_nolink/1, sync_start_link/1, 29: spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1, 30: hibernate/1]). 31: -export([ otp_6345/1, init_dont_hang/1]). 32: 33: -export([hib_loop/1, awaken/1]). 34: 35: -export([init/1, 36: handle_event/2, handle_call/2, handle_info/2, 37: terminate/2]). 38: 39: -export([otp_6345_init/1, init_dont_hang_init/1]). 40: 41: 42: -ifdef(STANDALONE). 43: -define(line, noop, ). 44: -else. 45: -include_lib("test_server/include/test_server.hrl"). 46: -endif. 47: 48: suite() -> [{ct_hooks,[ts_install_cth]}]. 49: 50: all() -> 51: [crash, {group, sync_start}, spawn_opt, hibernate, 52: {group, tickets}]. 53: 54: groups() -> 55: [{tickets, [], [otp_6345, init_dont_hang]}, 56: {sync_start, [], [sync_start_nolink, sync_start_link]}]. 57: 58: init_per_suite(Config) -> 59: Config. 60: 61: end_per_suite(_Config) -> 62: ok. 63: 64: init_per_group(_GroupName, Config) -> 65: Config. 66: 67: end_per_group(_GroupName, Config) -> 68: Config. 69: 70: 71: 72: %%----------------------------------------------------------------- 73: %% We don't have to test that spwn and spawn_link actually spawns 74: %% new processes - if they don't we can't run this suite! 75: %% But we want to test that start and start_link really is 76: %% synchronous, and we want to test that the crash report is ok. 77: %%----------------------------------------------------------------- 78: crash(Config) when is_list(Config) -> 79: error_logger:add_report_handler(?MODULE, self()), 80: 81: Pid = proc_lib:spawn(?MODULE, sp1, []), 82: Pid ! die, 83: ?line Report = receive 84: {crash_report, Pid, Report0} -> Report0 85: after 2000 -> test_server:fail(no_crash_report) 86: end, 87: ?line proc_lib:format(Report), 88: ?line [PidRep, []] = Report, 89: ?line {value, {initial_call,{?MODULE,sp1,[]}}} = 90: lists:keysearch(initial_call, 1, PidRep), 91: Self = self(), 92: ?line {value, {ancestors,[Self]}} = 93: lists:keysearch(ancestors, 1, PidRep), 94: ?line {value, {error_info,{exit,die,_StackTrace1}}} = 95: lists:keysearch(error_info, 1, PidRep), 96: 97: F = fun sp1/0, 98: Pid1 = proc_lib:spawn(node(), F), 99: Pid1 ! die, 100: ?line [PidRep1, []] = receive 101: {crash_report, Pid1, Report1} -> Report1 102: after 2000 -> test_server:fail(no_crash_report) 103: end, 104: ?line {value, {initial_call,{Fmod,Fname,[]}}} = 105: lists:keysearch(initial_call, 1, PidRep1), 106: ?line {module,Fmod} = erlang:fun_info(F, module), 107: ?line {name,Fname} = erlang:fun_info(F, name), 108: ?line {value, {ancestors,[Self]}} = 109: lists:keysearch(ancestors, 1, PidRep1), 110: ?line {value, {error_info,{exit,die,_StackTrace2}}} = 111: lists:keysearch(error_info, 1, PidRep1), 112: 113: Pid2 = proc_lib:spawn(?MODULE, sp2, []), 114: test_server:sleep(100), 115: ?line {?MODULE,sp2,[]} = proc_lib:initial_call(Pid2), 116: ?line {?MODULE,sp2,0} = proc_lib:translate_initial_call(Pid2), 117: Pid2 ! die, 118: ?line [Pid2Rep, [{neighbour, LinkRep}]] = 119: receive 120: {crash_report, Pid2, Report2} -> Report2 121: after 2000 -> test_server:fail(no_crash_report) 122: end, 123: ?line {value, {initial_call,{?MODULE,sp2,[]}}} = 124: lists:keysearch(initial_call, 1, Pid2Rep), 125: ?line {value, {ancestors,[Self]}} = 126: lists:keysearch(ancestors, 1, Pid2Rep), 127: ?line {value, {error_info,{exit,die,_StackTrace3}}} = 128: lists:keysearch(error_info, 1, Pid2Rep), 129: ?line {value, {initial_call,{?MODULE,sp1,[]}}} = 130: lists:keysearch(initial_call, 1, LinkRep), 131: 132: %% Make sure that we don't get a crash report if a process 133: %% terminates with reason 'shutdown' or reason {shutdown,Reason}. 134: ?line process_flag(trap_exit, true), 135: ?line Pid3 = proc_lib:spawn_link(erlang, apply, 136: [fun() -> exit(shutdown) end,[]]), 137: 138: ?line Pid4 = proc_lib:spawn_link(erlang, apply, 139: [fun() -> exit({shutdown,{a,b,c}}) end,[]]), 140: 141: ?line receive {'EXIT',Pid3,shutdown} -> ok end, 142: ?line receive {'EXIT',Pid4,{shutdown,{a,b,c}}} -> ok end, 143: ?line process_flag(trap_exit, false), 144: 145: receive 146: Any -> 147: ?line ?t:fail({unexpected_message,Any}) 148: after 2000 -> 149: ok 150: end. 151: 152: 153: sync_start_nolink(Config) when is_list(Config) -> 154: _Pid = spawn_link(?MODULE, sp5, [self()]), 155: receive 156: {sync_started, F} -> 157: exit(F, kill), 158: test_server:fail(async_start) 159: after 1000 -> ok 160: end, 161: receive 162: {Pid2, init} -> 163: Pid2 ! go_on 164: end, 165: receive 166: {sync_started, _} -> ok 167: after 1000 -> 168: exit(Pid2, kill), 169: test_server:fail(no_sync_start) 170: end, 171: ok. 172: 173: sync_start_link(Config) when is_list(Config) -> 174: _Pid = spawn_link(?MODULE, sp3, [self()]), 175: receive 176: {sync_started, _} -> test_server:fail(async_start) 177: after 1000 -> ok 178: end, 179: receive 180: {Pid2, init} -> 181: Pid2 ! go_on 182: end, 183: receive 184: {sync_started, _} -> ok 185: after 1000 -> test_server:fail(no_sync_start) 186: end, 187: ok. 188: 189: spawn_opt(Config) when is_list(Config) -> 190: F = fun sp1/0, 191: {name,Fname} = erlang:fun_info(F, name), 192: FunMFArgs = {?MODULE,Fname,[]}, 193: FunMFArity = {?MODULE,Fname,0}, 194: ?line Pid1 = proc_lib:spawn_opt(node(), F, [{priority,low}]), 195: ?line Pid = proc_lib:spawn_opt(F, [{priority,low}]), 196: ?line test_server:sleep(100), 197: ?line FunMFArgs = proc_lib:initial_call(Pid), 198: ?line FunMFArity = proc_lib:translate_initial_call(Pid), 199: ?line Pid ! die, 200: ?line FunMFArgs = proc_lib:initial_call(Pid1), 201: ?line FunMFArity = proc_lib:translate_initial_call(Pid1), 202: ?line Pid1 ! die, 203: ok. 204: 205: 206: sp1() -> 207: receive 208: die -> exit(die); 209: _ -> sp1() 210: end. 211: 212: sp2() -> 213: _Pid = proc_lib:spawn_link(?MODULE, sp1, []), 214: receive 215: die -> exit(die); 216: _ -> sp1() 217: end. 218: 219: sp3(Tester) -> 220: Pid = proc_lib:start_link(?MODULE, sp4, [self(), Tester]), 221: Tester ! {sync_started, Pid}. 222: 223: sp5(Tester) -> 224: Pid = proc_lib:start(?MODULE, sp4, [self(), Tester]), 225: Tester ! {sync_started, Pid}. 226: 227: sp4(Parent, Tester) -> 228: Tester ! {self(), init}, 229: receive 230: go_on -> ok 231: end, 232: proc_lib:init_ack(Parent, self()). 233: 234: hibernate(Config) when is_list(Config) -> 235: Ref = make_ref(), 236: Self = self(), 237: LoopData = {Ref,Self}, 238: ?line Pid = proc_lib:spawn_link(?MODULE, hib_loop, [LoopData]), 239: 240: %% Just check that the child process can process and answer messages. 241: ?line Pid ! {Self,loop_data}, 242: receive 243: {loop_data,LoopData} -> ok; 244: Unexpected0 -> 245: ?line io:format("Unexpected: ~p\n", [Unexpected0]), 246: ?line ?t:fail() 247: after 1000 -> 248: ?line io:format("Timeout"), 249: ?line ?t:fail() 250: end, 251: 252: %% Hibernate the process. 253: ?line Pid ! hibernate, 254: erlang:yield(), 255: io:format("~p\n", [process_info(Pid, heap_size)]), 256: 257: 258: %% Send a message to the process... 259: 260: ?line Pid ! {Self,loop_data}, 261: 262: %% ... expect first a wake up message from the process... 263: receive 264: {awaken,LoopData} -> ok; 265: Unexpected1 -> 266: ?line io:format("Unexpected: ~p\n", [Unexpected1]), 267: ?line ?t:fail() 268: after 1000 -> 269: ?line io:format("Timeout"), 270: ?line ?t:fail() 271: end, 272: 273: %% ... followed by the answer to the actual request. 274: receive 275: {loop_data,LoopData} -> ok; 276: Unexpected2 -> 277: ?line io:format("Unexpected: ~p\n", [Unexpected2]), 278: ?line ?t:fail() 279: after 1000 -> 280: ?line io:format("Timeout"), 281: ?line ?t:fail() 282: end, 283: 284: %% Test that errors are handled correctly after wake up from hibernation... 285: 286: ?line process_flag(trap_exit, true), 287: ?line error_logger:add_report_handler(?MODULE, self()), 288: ?line Pid ! crash, 289: 290: %% We should receive two messages. Especially in the SMP emulator, 291: %% we can't be sure of the message order, so sort the messages before 292: %% matching. 293: 294: Messages = lists:sort(hib_receive_messages(2)), 295: io:format("~p", [Messages]), 296: ?line [{'EXIT',Pid,i_crashed},{crash_report,Pid,[Report,[]]}] = Messages, 297: 298: %% Check that the initial_call has the expected format. 299: ?line {value,{initial_call,{?MODULE,hib_loop,[_]}}} = 300: lists:keysearch(initial_call, 1, Report), 301: 302: ok. 303: 304: hib_loop(LoopData) -> 305: receive 306: hibernate -> 307: proc_lib:hibernate(?MODULE, awaken, [LoopData]); 308: {Pid,loop_data} -> 309: Pid ! {loop_data,LoopData}; 310: crash -> 311: exit(i_crashed) 312: end, 313: hib_loop(LoopData). 314: 315: awaken({_,Parent}=LoopData) -> 316: Parent ! {awaken,LoopData}, 317: hib_loop(LoopData). 318: 319: hib_receive_messages(0) -> []; 320: hib_receive_messages(N) -> 321: receive 322: Any -> [Any|hib_receive_messages(N-1)] 323: end. 324: 325: otp_6345(suite) -> 326: []; 327: otp_6345(doc) -> 328: ["'monitor' spawn_opt option"]; 329: otp_6345(Config) when is_list(Config) -> 330: Opts = [link,monitor], 331: {'EXIT', {badarg,[{proc_lib,check_for_monitor,_,_}|_Stack]}} = 332: (catch proc_lib:start(?MODULE, otp_6345_init, [self()], 333: 1000, Opts)), 334: ok. 335: 336: otp_6345_init(Parent) -> 337: proc_lib:init_ack(Parent, {ok, self()}), 338: otp_6345_loop(). 339: 340: otp_6345_loop() -> 341: receive 342: _Msg -> 343: otp_6345_loop() 344: end. 345: 346: %% OTP-9803 347: init_dont_hang(suite) -> 348: []; 349: init_dont_hang(doc) -> 350: ["Check that proc_lib:start don't hang if spawned process crashes before proc_lib:init_ack/2"]; 351: init_dont_hang(Config) when is_list(Config) -> 352: %% Start should behave as start_link 353: process_flag(trap_exit, true), 354: StartLinkRes = proc_lib:start_link(?MODULE, init_dont_hang_init, [self()]), 355: try 356: StartLinkRes = proc_lib:start(?MODULE, init_dont_hang_init, [self()], 1000), 357: StartLinkRes = proc_lib:start(?MODULE, init_dont_hang_init, [self()], 1000, []), 358: ok 359: catch _:Error -> 360: io:format("Error ~p /= ~p ~n",[erlang:get_stacktrace(), StartLinkRes]), 361: exit(Error) 362: end. 363: 364: init_dont_hang_init(Parent) -> 365: 1 = 2. 366: 367: 368: 369: %%----------------------------------------------------------------- 370: %% The error_logger handler used. 371: %%----------------------------------------------------------------- 372: init(Tester) -> 373: {ok, Tester}. 374: 375: handle_event({error_report, _GL, {Pid, crash_report, Report}}, Tester) -> 376: io:format("~s\n", [proc_lib:format(Report)]), 377: Tester ! {crash_report, Pid, Report}, 378: {ok, Tester}; 379: handle_event(_Event, State) -> 380: {ok, State}. 381: 382: handle_info(_, State) -> 383: {ok, State}. 384: 385: handle_call(_Query, State) -> {ok, {error, bad_query}, State}. 386: 387: terminate(_Reason, State) -> 388: State.