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.