1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 1997-2013. 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(distribution_SUITE).
   21: -compile(r15).
   22: 
   23: -define(VERSION_MAGIC,       131).
   24: 
   25: -define(ATOM_EXT,            100).
   26: -define(REFERENCE_EXT,       101).
   27: -define(PORT_EXT,            102).
   28: -define(PID_EXT,             103).
   29: -define(NEW_REFERENCE_EXT,   114).
   30: -define(ATOM_UTF8_EXT,       118).
   31: -define(SMALL_ATOM_UTF8_EXT, 119).
   32: 
   33: %% Tests distribution and the tcp driver.
   34: 
   35: -include_lib("test_server/include/test_server.hrl").
   36: 
   37: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   38: 	 init_per_group/2,end_per_group/2,
   39: 	 ping/1, bulk_send_small/1,
   40: 	 bulk_send_big/1, bulk_send_bigbig/1,
   41: 	 local_send_small/1, local_send_big/1,
   42: 	 local_send_legal/1, link_to_busy/1, exit_to_busy/1,
   43: 	 lost_exit/1, link_to_dead/1, link_to_dead_new_node/1,
   44: 	 applied_monitor_node/1, ref_port_roundtrip/1, nil_roundtrip/1,
   45: 	 trap_bif_1/1, trap_bif_2/1, trap_bif_3/1,
   46: 	 stop_dist/1, 
   47: 	 dist_auto_connect_never/1, dist_auto_connect_once/1,
   48: 	 dist_parallel_send/1,
   49: 	 atom_roundtrip/1,
   50: 	 unicode_atom_roundtrip/1,
   51: 	 atom_roundtrip_r15b/1,
   52: 	 contended_atom_cache_entry/1,
   53: 	 contended_unicode_atom_cache_entry/1,
   54: 	 bad_dist_structure/1,
   55: 	 bad_dist_ext_receive/1,
   56: 	 bad_dist_ext_process_info/1,
   57: 	 bad_dist_ext_control/1,
   58: 	 bad_dist_ext_connection_id/1]).
   59: 
   60: -export([init_per_testcase/2, end_per_testcase/2]).
   61: 
   62: %% Internal exports.
   63: -export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
   64: 	 roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1,
   65: 	 dist_parallel_sender/3, dist_parallel_receiver/0,
   66: 	 dist_evil_parallel_receiver/0,
   67:          sendersender/4, sendersender2/4]).
   68: 
   69: suite() -> [{ct_hooks,[ts_install_cth]}].
   70: 
   71: all() -> 
   72:     [ping, {group, bulk_send}, {group, local_send},
   73:      link_to_busy, exit_to_busy, lost_exit, link_to_dead,
   74:      link_to_dead_new_node, applied_monitor_node,
   75:      ref_port_roundtrip, nil_roundtrip, stop_dist,
   76:      {group, trap_bif}, {group, dist_auto_connect},
   77:      dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip, atom_roundtrip_r15b,
   78:      contended_atom_cache_entry, contended_unicode_atom_cache_entry,
   79:      bad_dist_structure, {group, bad_dist_ext}].
   80: 
   81: groups() -> 
   82:     [{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
   83:      {local_send, [],
   84:       [local_send_small, local_send_big, local_send_legal]},
   85:      {trap_bif, [], [trap_bif_1, trap_bif_2, trap_bif_3]},
   86:      {dist_auto_connect, [],
   87:       [dist_auto_connect_never, dist_auto_connect_once]},
   88:      {bad_dist_ext, [],
   89:       [bad_dist_ext_receive, bad_dist_ext_process_info,
   90:        bad_dist_ext_control, bad_dist_ext_connection_id]}].
   91: 
   92: init_per_suite(Config) ->
   93:     Config.
   94: 
   95: end_per_suite(_Config) ->
   96:     ok.
   97: 
   98: init_per_group(_GroupName, Config) ->
   99:     Config.
  100: 
  101: end_per_group(_GroupName, Config) ->
  102:     Config.
  103: 
  104: -define(DEFAULT_TIMETRAP, 4*60*1000).
  105: 
  106: init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
  107:     Dog=?t:timetrap(?DEFAULT_TIMETRAP),
  108:     [{watchdog, Dog},{testcase, Func}|Config].
  109: 
  110: end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
  111:     Dog=?config(watchdog, Config),
  112:     ?t:timetrap_cancel(Dog).
  113: 
  114: ping(doc) ->
  115:     ["Tests pinging a node in different ways."];
  116: ping(Config) when is_list(Config) ->
  117:     Times = 1024,
  118: 
  119:     %% Ping a non-existing node many times.  This used to crash the emulator
  120:     %% on Windows.
  121: 
  122:     ?line Host = hostname(),
  123:     ?line BadName = list_to_atom("__pucko__@" ++ Host),
  124:     ?line io:format("Pinging ~s (assumed to not exist)", [BadName]),
  125:     ?line test_server:do_times(Times, fun() -> pang = net_adm:ping(BadName)
  126: 			       end),
  127: 
  128:     %% Pings another node.
  129: 
  130:     ?line {ok, OtherNode} = start_node(distribution_SUITE_other),
  131:     ?line io:format("Pinging ~s (assumed to exist)", [OtherNode]),
  132:     ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(OtherNode) end),
  133:     ?line stop_node(OtherNode),
  134: 
  135:     %% Pings our own node many times.
  136: 
  137:     ?line Node = node(),
  138:     ?line io:format("Pinging ~s (the same node)", [Node]),
  139:     ?line test_server:do_times(Times, fun() -> pong = net_adm:ping(Node) end),
  140: 
  141:     ok.
  142: 
  143: bulk_send_small(Config) when is_list(Config) ->
  144:     ?line bulk_send(64, 32).
  145: 
  146: bulk_send_big(Config) when is_list(Config) ->
  147:     ?line bulk_send(32, 64).
  148: 
  149: bulk_send_bigbig(Config) when is_list(Config) ->
  150:     ?line bulk_sendsend(32*5, 4).
  151: 
  152: bulk_send(Terms, BinSize) ->
  153:     ?line Dog = test_server:timetrap(test_server:seconds(30)),
  154: 
  155:     ?line io:format("Sending ~w binaries, each of size ~w K",
  156: 		    [Terms, BinSize]),
  157:     ?line {ok, Node} = start_node(bulk_receiver),
  158:     ?line Recv = spawn(Node, erlang, apply, [fun receiver/2, [0, 0]]),
  159:     ?line Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)),
  160:     ?line Size = Terms*size(Bin),
  161:     ?line {Elapsed, {Terms, Size}} = test_server:timecall(?MODULE, sender,
  162: 							  [Recv, Bin, Terms]),
  163:     ?line stop_node(Node),
  164: 
  165:     ?line test_server:timetrap_cancel(Dog),
  166:     {comment, integer_to_list(trunc(Size/1024/Elapsed+0.5)) ++ " K/s"}.
  167: 
  168: bulk_sendsend(Terms, BinSize) ->
  169:     {Rate1, MonitorCount1} = bulk_sendsend2(Terms, BinSize,   5),
  170:     {Rate2, MonitorCount2} = bulk_sendsend2(Terms, BinSize, 995),
  171:     Ratio = if MonitorCount2 == 0 -> MonitorCount1 / 1.0;
  172:                true               -> MonitorCount1 / MonitorCount2
  173:             end,
  174:     Comment = integer_to_list(Rate1) ++ " K/s, " ++
  175: 	integer_to_list(Rate2) ++ " K/s, " ++
  176: 	integer_to_list(MonitorCount1) ++ " monitor msgs, " ++
  177: 	integer_to_list(MonitorCount2) ++ " monitor msgs, " ++
  178: 	float_to_list(Ratio) ++ " monitor ratio",
  179:     if
  180: 	%% A somewhat arbitrary ratio, but hopefully one that will
  181: 	%% accommodate a wide range of CPU speeds.
  182: 	Ratio > 8.0 ->
  183: 	    {comment,Comment};
  184: 	true ->
  185: 	    io:put_chars(Comment),
  186: 	    ?line ?t:fail(ratio_too_low)
  187:     end.
  188: 
  189: bulk_sendsend2(Terms, BinSize, BusyBufSize) ->
  190:     ?line Dog = test_server:timetrap(test_server:seconds(30)),
  191: 
  192:     ?line io:format("Sending ~w binaries, each of size ~w K",
  193: 		    [Terms, BinSize]),
  194:     ?line {ok, NodeRecv} = start_node(bulk_receiver),
  195:     ?line Recv = spawn(NodeRecv, erlang, apply, [fun receiver/2, [0, 0]]),
  196:     ?line Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)),
  197:     %%?line Size = Terms*size(Bin),
  198: 
  199:     %% SLF LEFT OFF HERE.
  200:     %% When the caller uses small hunks, like 4k via
  201:     %% bulk_sendsend(32*5, 4), then (on my laptop at least), we get
  202:     %% zero monitor messages.  But if we use "+zdbbl 5", then we
  203:     %% get a lot of monitor messages.  So, if we can count up the
  204:     %% total number of monitor messages that we get when running both
  205:     %% default busy size and "+zdbbl 5", and if the 5 case gets
  206:     %% "many many more" monitor messages, then we know we're working.
  207: 
  208:     ?line {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)),
  209:     ?line _Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]),
  210:     ?line {Elapsed, {_TermsN, SizeN}, MonitorCount} =
  211:         receive {sendersender, BigRes} ->
  212:                 BigRes
  213:         end,
  214:     ?line stop_node(NodeRecv),
  215:     ?line stop_node(NodeSend),
  216: 
  217:     ?line test_server:timetrap_cancel(Dog),
  218:     {trunc(SizeN/1024/Elapsed+0.5), MonitorCount}.
  219: 
  220: sender(To, _Bin, 0) ->
  221:     To ! {done, self()},
  222:     receive
  223: 	Any ->
  224: 	    Any
  225:     end;
  226: sender(To, Bin, Left) ->
  227:     To ! {term, Bin},
  228:     sender(To, Bin, Left-1).
  229: 
  230: %% Sender process to be run on a slave node
  231: 
  232: sendersender(Parent, To, Bin, Left) ->
  233:     erlang:system_monitor(self(), [busy_dist_port]),
  234:     [spawn(fun() -> sendersender2(To, Bin, Left, false) end) ||
  235:         _ <- lists:seq(1,1)],
  236:     {USec, {Res, MonitorCount}} =
  237:         timer:tc(?MODULE, sendersender2, [To, Bin, Left, true]),
  238:     Parent ! {sendersender, {USec/1000000, Res, MonitorCount}}.
  239: 
  240: sendersender2(To, Bin, Left, SendDone) ->
  241:     sendersender3(To, Bin, Left, SendDone, 0).
  242: 
  243: sendersender3(To, _Bin, 0, SendDone, MonitorCount) ->
  244:     if SendDone ->
  245:             To ! {done, self()};
  246:        true ->
  247:             ok
  248:     end,
  249:     receive
  250:         {monitor, _Pid, _Type, _Info} ->
  251:             sendersender3(To, _Bin, 0, SendDone, MonitorCount + 1)
  252:     after 0 ->
  253:             if SendDone ->
  254:                     receive
  255:                         Any when is_tuple(Any), size(Any) == 2 ->
  256:                             {Any, MonitorCount}
  257:                     end;
  258:                true ->
  259:                     exit(normal)
  260:             end
  261:     end;
  262: sendersender3(To, Bin, Left, SendDone, MonitorCount) ->
  263:     To ! {term, Bin},
  264:     %%timer:sleep(50),
  265:     sendersender3(To, Bin, Left-1, SendDone, MonitorCount).
  266: 
  267: %% Receiver process to be run on a slave node.
  268: 
  269: receiver(Terms, Size) ->
  270:     receive
  271: 	{term, Bin} ->
  272: 	    receiver(Terms+1, Size+size(Bin));
  273: 	{done, ReplyTo} ->
  274: 	    ReplyTo ! {Terms, Size}
  275:     end.
  276: 
  277: 
  278: 
  279: local_send_big(doc) ->
  280:     ["Sends several big message to an non-registered process on ",
  281:      "the local node."];
  282: local_send_big(Config) when is_list(Config) ->
  283:     Data0=local_send_big(doc)++
  284: 	["Tests sending small and big messages to a non-existing ",
  285:         "local registered process."],
  286:     Data1=[Data0,[Data0, Data0, [Data0], Data0],Data0],
  287:     Data2=Data0++lists:flatten(Data1)++
  288: 	list_to_binary(lists:flatten(Data1)),
  289:     Func=fun() -> Data2= {arbitrary_name, node()} ! Data2 end,
  290:     ?line test_server:do_times(4096, Func),
  291:     ok.
  292: 
  293: local_send_small(doc) ->
  294:     ["Sends a small message to an non-registered process on the ",
  295:      "local node."];
  296: local_send_small(Config) when is_list(Config) ->
  297:     Data={some_stupid, "arbitrary", 'Data'},
  298:     Func=fun() -> Data= {unregistered_name, node()} ! Data end,
  299:     ?line test_server:do_times(4096, Func),
  300:     ok.
  301: 
  302: local_send_legal(doc) ->
  303:     ["Sends data to a registered process on the local node, ",
  304:      "as if it was on another node."];
  305: local_send_legal(Config) when is_list(Config) ->
  306:     Times=16384,
  307:     Data={local_send_legal(doc), local_send_legal(doc)},
  308:     Pid=spawn(?MODULE,receiver2, [0, 0]) ,
  309:     ?line true=register(registered_process, Pid),
  310: 
  311:     Func=fun() -> Data={registered_process, node()} ! Data end,
  312:     TotalSize=size(Data)*Times,
  313:     ?line test_server:do_times(Times, Func),
  314: 
  315:     % Check that all msgs really came through.
  316:     Me=self(),
  317:     ?line {done, Me}=
  318: 	{registered_process, node()} ! {done, Me},
  319:     receive
  320: 	{Times, TotalSize} ->
  321: 	    ok;
  322: 	_ ->
  323: 	    test_server:fail("Wrong number of msgs received.")
  324:     end,
  325:     ok.
  326: 
  327: receiver2(Num, TotSize) ->
  328:     receive
  329: 	{done, ReplyTo} ->
  330: 	    ReplyTo ! {Num, TotSize};
  331: 	Stuff ->
  332: 	    receiver2(Num+1, TotSize+size(Stuff))
  333:     end.
  334: 
  335: link_to_busy(doc) -> "Test that link/1 to a busy distribution port works.";
  336: link_to_busy(Config) when is_list(Config) ->
  337:     ?line Dog = test_server:timetrap(test_server:seconds(60)),
  338:     ?line {ok, Node} = start_node(link_to_busy),
  339:     ?line Recv = spawn(Node, erlang, apply, [fun sink/1, [link_to_busy_sink]]),
  340: 
  341:     Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
  342: 		 "true" -> start_busy_dist_port_tracer();
  343: 		 _ -> false
  344: 	     end,
  345: 
  346:     %% We will spawn off a process which will try to link to the other
  347:     %% node.  The linker process will not actually run until this
  348:     %% process is suspended due to the busy distribution port (because
  349:     %% of the big send).  When the link/1 is run, the linker
  350:     %% process will block, too, because of the because busy port,
  351:     %% and will later be restarted.
  352: 
  353:     ?line do_busy_test(Node, fun () -> linker(Recv) end),
  354: 
  355:     %% Same thing, but we apply link/1 instead of calling it directly.
  356: 
  357:     ?line do_busy_test(Node, fun () -> applied_linker(Recv) end),
  358: 
  359:     %% Same thing again, but we apply link/1 in the tail of a function.
  360: 
  361:     ?line do_busy_test(Node, fun () -> tail_applied_linker(Recv) end),
  362: 
  363:     %% Done.
  364:     ?line stop_node(Node),
  365:     ?line stop_busy_dist_port_tracer(Tracer),
  366:     ?line test_server:timetrap_cancel(Dog),
  367:     ok.
  368: 
  369: linker(Pid) ->
  370:     true = link(Pid),
  371:     {links, Links} = process_info(self(), links),
  372:     true = lists:member(Pid, Links).
  373: 
  374: applied_linker(Pid) ->
  375:     true = apply(erlang, link, [Pid]),
  376:     {links, Links} = process_info(self(), links),
  377:     true = lists:member(Pid, Links).
  378: 
  379: tail_applied_linker(Pid) ->
  380:     apply(erlang, link, [Pid]).
  381:     
  382: exit_to_busy(doc) -> "Test that exit/2 to a busy distribution port works.";
  383: exit_to_busy(Config) when is_list(Config) ->
  384:     ?line Dog = test_server:timetrap(test_server:seconds(60)),
  385:     ?line {ok, Node} = start_node(exit_to_busy),
  386: 
  387:     Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
  388: 		 "true" -> start_busy_dist_port_tracer();
  389: 		 _ -> false
  390: 	     end,
  391: 
  392:     %% We will spawn off a process which will try to exit a process on
  393:     %% the other node.  That process will not actually run until this
  394:     %% process is suspended due to the busy distribution port
  395:     %% The process executing exit/2 will block,
  396:     %% too, because of the busy distribution port, and will be allowed
  397:     %% to continue when the port becomes non-busy.
  398: 
  399:     ?line Recv1 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
  400:     ?line M1 = erlang:monitor(process, Recv1),
  401:     ?line do_busy_test(Node, fun () -> joey_killer(Recv1) end),
  402:     ?line receive
  403: 	      {'DOWN', M1, process, Recv1, R1} ->
  404: 		  ?line joey_said_die = R1
  405: 	  end,
  406: 
  407:     %% Same thing, but tail call to exit/2.
  408:     ?line Recv2 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
  409:     ?line M2 = erlang:monitor(process, Recv2),
  410:     ?line do_busy_test(Node, fun () -> tail_joey_killer(Recv2) end),
  411:     ?line receive
  412: 	      {'DOWN', M2, process, Recv2, R2} ->
  413: 		  ?line joey_said_die = R2
  414: 	  end,
  415: 
  416:     %% Same thing, but we apply exit/2 instead of calling it directly.
  417:     ?line Recv3 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
  418:     ?line M3 = erlang:monitor(process, Recv3),
  419:     ?line do_busy_test(Node, fun () -> applied_joey_killer(Recv3) end),
  420:     ?line receive
  421: 	      {'DOWN', M3, process, Recv3, R3} ->
  422: 		  ?line joey_said_die = R3
  423: 	  end,
  424: 
  425:     %% Same thing again, but we apply exit/2 in the tail of a function.
  426:     ?line Recv4 = spawn(Node, fun () -> sink(exit_to_busy_sink) end),
  427:     ?line M4 = erlang:monitor(process, Recv4),
  428:     ?line do_busy_test(Node, fun () -> tail_applied_joey_killer(Recv4) end),
  429:     ?line receive
  430: 	      {'DOWN', M4, process, Recv4, R4} ->
  431: 		  ?line joey_said_die = R4
  432: 	  end,
  433:     
  434:     %% Done.
  435:     ?line stop_node(Node),
  436:     ?line stop_busy_dist_port_tracer(Tracer),
  437:     ?line test_server:timetrap_cancel(Dog),
  438:     ok.
  439: 
  440: make_busy_data() ->
  441:     Size = 1024*1024,
  442:     Key = '__busy__port__data__',
  443:     case get(Key) of
  444: 	undefined ->
  445: 	    Data = list_to_binary(lists:duplicate(Size, 253)),
  446: 	    put(Key, Data),
  447: 	    Data;
  448: 	Data ->
  449: 	    true = is_binary(Data),
  450: 	    true = size(Data) == Size,
  451: 	    Data
  452:     end.
  453: 
  454: make_busy(Node, Time) when is_integer(Time) ->
  455:     Own = 500,
  456:     freeze_node(Node, Time+Own),
  457:     Data = make_busy_data(),
  458:     %% first make port busy
  459:     Pid = spawn_link(fun () ->
  460: 			     forever(fun () ->
  461: 					     dport_reg_send(Node,
  462: 							    '__noone__',
  463: 							    Data)
  464: 				     end)
  465: 		     end),
  466:     receive after Own -> ok end,
  467:     until(fun () ->
  468: 		  case process_info(Pid, status) of
  469: 		      {status, suspended} -> true;
  470: 		      _ -> false
  471: 		  end
  472: 	  end),
  473:     %% then dist entry
  474:     make_busy(Node, [nosuspend], Data),
  475:     Pid.
  476: 
  477: make_busy(Node, Opts, Data) ->
  478:     case erlang:send({'__noone__', Node}, Data, Opts) of
  479: 	nosuspend -> nosuspend;
  480: 	_ -> make_busy(Node, Opts, Data)
  481:     end.
  482: 
  483: unmake_busy(Pid) ->
  484:     unlink(Pid),
  485:     exit(Pid, bang).
  486: 
  487: do_busy_test(Node, Fun) ->
  488:     Busy = make_busy(Node, 1000),
  489:     {P, M} = spawn_monitor(Fun),
  490:     receive after 100 -> ok end,
  491:     Pinfo = process_info(P, [status, current_function]),
  492:     unmake_busy(Busy),
  493:     ?t:format("~p : ~p~n", [P, Pinfo]),
  494:     case Pinfo of
  495: 	undefined ->
  496: 	    receive
  497: 		{'DOWN', M, process, P, Reason} ->
  498: 		    ?t:format("~p died with exit reason ~p~n", [P, Reason])
  499: 	    end,
  500: 	    ?t:fail(premature_death);
  501: 	_ ->
  502: 	    %% Don't match arity; it is different in debug and
  503: 	    %% optimized emulator
  504: 	    [{status, suspended},
  505: 	     {current_function, {erlang, bif_return_trap, _}}] = Pinfo,
  506: 	    receive
  507: 		{'DOWN', M, process, P, Reason} ->
  508: 		    ?t:format("~p died with exit reason ~p~n", [P, Reason]),
  509: 		    normal = Reason
  510: 	    end
  511:     end.
  512: 
  513: remote_is_process_alive(Pid) ->
  514:     rpc:call(node(Pid), erlang, is_process_alive,
  515: 	     [Pid]).
  516: 
  517: joey_killer(Pid) ->
  518:     exit(Pid, joey_said_die),
  519:     until(fun () -> false == remote_is_process_alive(Pid) end).
  520: 
  521: tail_joey_killer(Pid) ->
  522:     exit(Pid, joey_said_die).
  523: 
  524: applied_joey_killer(Pid) ->
  525:     apply(erlang, exit, [Pid, joey_said_die]),
  526:     until(fun () -> false == remote_is_process_alive(Pid) end).
  527: 
  528: tail_applied_joey_killer(Pid) ->
  529:     apply(erlang, exit, [Pid, joey_said_die]).
  530: 
  531: sink(Name) ->
  532:     register(Name, self()),
  533:     sink1().
  534: 
  535: sink1() ->
  536:     receive
  537: 	_Any -> sink1()
  538:     end.
  539: 
  540: lost_exit(doc) ->
  541:     "Test that EXIT and DOWN messages send to another node are not lost if "
  542: 	"the distribution port is busy.";
  543: lost_exit(Config) when is_list(Config) ->
  544:     ?line {ok, Node} = start_node(lost_exit),
  545: 
  546:     Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
  547: 		 "true" -> start_busy_dist_port_tracer();
  548: 		 _ -> false
  549: 	     end,
  550: 
  551:     Self = self(),
  552:     Die = make_ref(),
  553:     ?line R1 = spawn(fun () -> receive after infinity -> ok end end),
  554:     ?line MR1 = erlang:monitor(process, R1),
  555:     
  556:     ?line {L1, ML1} = spawn_monitor(fun() ->
  557: 					    link(R1),
  558: 					    Self ! {self(), linked},
  559: 					    receive
  560: 						Die ->
  561: 						    exit(controlled_suicide)
  562: 					    end
  563: 				    end),
  564: 
  565:     ?line R2 = spawn(fun () ->
  566: 			     M = erlang:monitor(process, L1),
  567: 			     receive
  568: 				 {'DOWN', M, process, L1, R} ->
  569: 				     Self ! {self(), got_down_message, L1, R}
  570: 			     end
  571: 		     end),
  572: 
  573:     ?line receive {L1, linked} -> ok end,
  574:     
  575:     Busy = make_busy(Node, 2000),
  576:     receive after 100 -> ok end,
  577:     L1 ! Die,
  578:     ?line receive
  579: 	      {'DOWN', ML1, process, L1, RL1} ->
  580: 		  ?line controlled_suicide = RL1
  581: 	  end,
  582:     receive after 500 -> ok end,
  583:     unmake_busy(Busy),
  584: 
  585:     ?line receive
  586: 	      {'DOWN', MR1, process, R1, RR1} ->
  587: 		  ?line controlled_suicide = RR1
  588: 	  end,
  589:     
  590:     ?line receive
  591: 	      {R2, got_down_message, L1, RR2} ->
  592: 		  ?line controlled_suicide = RR2
  593: 	  end,
  594: 
  595:     %% Done.
  596:     ?line stop_busy_dist_port_tracer(Tracer),
  597:     ?line stop_node(Node),
  598:     ok.
  599: 
  600: dummy_waiter() ->
  601:     receive
  602:     after infinity ->
  603: 	    ok
  604:     end.
  605: 
  606: link_to_dead(doc) ->
  607:     ["Test that linking to a dead remote process gives an EXIT message ",
  608:      "AND that the link is teared down."];
  609: link_to_dead(Config) when is_list(Config) ->
  610:     ?line process_flag(trap_exit, true),
  611:     ?line {ok, Node} = start_node(link_to_dead),
  612: %    ?line monitor_node(Node, true),
  613:     ?line net_adm:ping(Node), %% Ts_cross_server workaround.
  614:     ?line Pid = spawn(Node, ?MODULE, dead_process, []),
  615:     receive
  616:     after 5000 -> ok
  617:     end,
  618:     ?line link(Pid),
  619:     ?line receive
  620: 	      {'EXIT', Pid, noproc} ->
  621: 		  ok;
  622: 	      Other ->
  623: 		  ?line test_server:fail({unexpected_message, Other})
  624: 	  after 5000 ->
  625: 		  ?line test_server:fail(nothing_received)
  626: 	  end,
  627:     ?line {links, Links} = process_info(self(), links),
  628:     ?line io:format("Pid=~p, links=~p", [Pid, Links]),
  629:     ?line false = lists:member(Pid, Links),
  630:     ?line stop_node(Node),
  631:     ?line receive
  632: 	      Message ->
  633: 		  ?line test_server:fail({unexpected_message, Message})
  634: 	  after 3000 ->
  635: 		  ok
  636: 	  end,
  637:     ok.
  638:     
  639: dead_process() ->
  640:     erlang:error(die).
  641: 
  642: link_to_dead_new_node(doc) ->
  643:     ["Test that linking to a pid on node that has gone and restarted gives ",
  644:      "the correct EXIT message (OTP-2304)."];
  645: link_to_dead_new_node(Config) when is_list(Config) ->
  646:     ?line process_flag(trap_exit, true),
  647: 
  648:     %% Start the node, get a Pid and stop the node again.
  649:     ?line {ok, Node} = start_node(link_to_dead_new_node),
  650:     ?line Pid = spawn(Node, ?MODULE, dead_process, []),
  651:     ?line stop_node(Node),
  652: 
  653:     %% Start a new node with the same name.
  654:     ?line {ok, Node} = start_node(link_to_dead_new_node),
  655:     ?line link(Pid),
  656:     ?line receive
  657: 	      {'EXIT', Pid, noproc} ->
  658: 		  ok;
  659: 	      Other ->
  660: 		  ?line test_server:fail({unexpected_message, Other})
  661: 	  after 5000 ->
  662: 		  ?line test_server:fail(nothing_received)
  663: 	  end,
  664: 
  665:     %% Make sure that the link wasn't created.
  666:     ?line {links, Links} = process_info(self(), links),
  667:     ?line io:format("Pid=~p, links=~p", [Pid, Links]),
  668:     ?line false = lists:member(Pid, Links),
  669:     ?line stop_node(Node),
  670:     ?line receive
  671: 	      Message ->
  672: 		  ?line test_server:fail({unexpected_message, Message})
  673: 	  after 3000 ->
  674: 		  ok
  675: 	  end,
  676:     ok.
  677: 
  678: applied_monitor_node(doc) ->
  679:     "Test that monitor_node/2 works when applied.";
  680: applied_monitor_node(Config) when is_list(Config) ->
  681:     ?line NonExisting = list_to_atom("__non_existing__@" ++ hostname()),
  682: 
  683:     %% Tail-recursive call to apply (since the node is non-existing,
  684:     %% there will be a trap).
  685: 
  686:     ?line true = tail_apply(erlang, monitor_node, [NonExisting, true]),
  687:     ?line [{nodedown, NonExisting}] = test_server:messages_get(),
  688: 
  689:     %% Ordinary call (with trap).
  690: 
  691:     ?line true = apply(erlang, monitor_node, [NonExisting, true]),
  692:     ?line [{nodedown, NonExisting}] = test_server:messages_get(),
  693:     
  694:     ok.
  695: 
  696: tail_apply(M, F, A) ->
  697:     apply(M, F, A).
  698: 
  699: ref_port_roundtrip(doc) ->
  700:     "Test that sending a port or reference to another node and back again "
  701: 	"doesn't correct them in any way.";
  702: ref_port_roundtrip(Config) when is_list(Config) ->
  703:     ?line process_flag(trap_exit, true),
  704:     ?line Port = open_port({spawn, efile}, []),
  705:     ?line Ref = make_ref(),
  706:     ?line {ok, Node} = start_node(ref_port_roundtrip),
  707:     ?line net_adm:ping(Node),
  708:     ?line Term = {Port, Ref},
  709:     ?line io:format("Term before: ~p", [show_term(Term)]),
  710:     ?line Pid = spawn_link(Node, ?MODULE, roundtrip, [Term]),
  711:     ?line receive after 5000 -> ok end,
  712:     ?line stop_node(Node),
  713:     ?line receive
  714: 	      {'EXIT', Pid, {Port, Ref}} ->
  715: 		  ?line io:format("Term after: ~p", [show_term(Term)]),
  716: 		  ok;
  717: 	      Other ->
  718: 		  ?line io:format("Term after: ~p", [show_term(Term)]),
  719: 		  ?line test_server:fail({unexpected, Other})
  720: 	  after 10000 ->
  721: 		  ?line test_server:fail(timeout)
  722: 	  end,
  723:     ok.
  724: 
  725: roundtrip(Term) ->
  726:     exit(Term).
  727: 
  728: nil_roundtrip(doc) ->
  729:     "Test that the smallest external term [] aka NIL can be sent to "
  730: 	"another node node and back again.";
  731: nil_roundtrip(Config) when is_list(Config) ->
  732:     ?line process_flag(trap_exit, true),
  733:     ?line {ok, Node} = start_node(nil_roundtrip),
  734:     ?line net_adm:ping(Node),
  735:     ?line Pid = spawn_link(Node, ?MODULE, bounce, [self()]),
  736:     ?line Pid ! [],
  737:     ?line receive
  738: 	      [] ->
  739: 		  ?line receive
  740: 			    {'EXIT', Pid, []} ->
  741: 				?line stop_node(Node),
  742: 				ok
  743: 			end
  744: 	  end.
  745: 
  746: bounce(Dest) ->
  747:     receive Msg ->
  748: 	    Dest ! Msg,
  749: 	    exit(Msg)
  750:     end.
  751: 
  752: show_term(Term) ->
  753:     binary_to_list(term_to_binary(Term)).
  754: 
  755: stop_dist(doc) ->
  756:     ["Tests behaviour after net_kernel:stop (OTP-2586)."];
  757: stop_dist(Config) when is_list(Config) ->
  758:     ?line Str = os:cmd(atom_to_list(lib:progname())
  759: 		       ++ " -noshell -pa "
  760: 		       ++ ?config(data_dir, Config)
  761: 		       ++ " -s run"),
  762:     %% The "true" may be followed by an error report, so ignore anything that
  763:     %% follows it.
  764:     ?line "true\n"++_ = Str,
  765: 
  766:     %% "May fail on FreeBSD due to differently configured name lookup - ask Arndt",
  767:     %% if you can find him.
  768: 
  769:     ok.
  770: 
  771: 
  772: trap_bif_1(doc) ->
  773:     [""];
  774: trap_bif_1(Config) when is_list(Config) ->
  775:     ?line {true} = tr1(),
  776:     ok.
  777: 
  778: trap_bif_2(doc) ->
  779:     [""];
  780: trap_bif_2(Config) when is_list(Config) ->
  781:     ?line {true} = tr2(),
  782:     ok.
  783: 
  784: trap_bif_3(doc) ->
  785:     [""];
  786: trap_bif_3(Config) when is_list(Config) ->
  787:     ?line {hoo} = tr3(),
  788:     ok.
  789: 
  790: tr1() ->
  791:     ?line NonExisting = 'abc@boromir',
  792:     ?line X = erlang:monitor_node(NonExisting, true),
  793:     {X}.
  794: 
  795: tr2() ->
  796:     ?line NonExisting = 'abc@boromir',
  797:     ?line X = apply(erlang, monitor_node, [NonExisting, true]),
  798:     {X}.
  799: 
  800: tr3() ->
  801:     ?line NonExisting = 'abc@boromir',
  802:     ?line X = {NonExisting, glirp} ! hoo,
  803:     {X}.
  804: 
  805: 
  806: 
  807: 
  808: % This has to be done by nodes with differrent cookies, otherwise global
  809: % will connect nodes, which is correct, but makes it hard to test.
  810: % * Start two nodes, n1 and n2. n2 with the dist_auto_connect once parameter
  811: % * n2 pings n1 -> connection
  812: % * check that they now know each other
  813: % * Kill n1
  814: % * Make sure n2 gets pang when pinging n1
  815: % * restart n1
  816: % * Make sure n2 *still gets pang*!
  817: % * Ping n2 from n1 -> pong
  818: % * n2 now also gets pong when pinging n1
  819: % * disconnect n2 from n1
  820: % * n2 gets pang when pinging n1
  821: % * n2 forces connection by using net_kernel:connect_node (ovverrides)
  822: % * n2 gets pong when pinging n1.
  823: dist_auto_connect_once(doc) -> "Test the dist_auto_connect once kernel parameter";
  824: dist_auto_connect_once(Config) when is_list(Config) ->
  825:     ?line Sock = start_relay_node(dist_auto_connect_relay_node,[]),
  826:     ?line NN = inet_rpc_nodename(Sock),
  827:     ?line Sock2 = start_relay_node(dist_auto_connect_once_node,
  828: 				   "-kernel dist_auto_connect once"),
  829:     ?line NN2 = inet_rpc_nodename(Sock2),
  830:     ?line {ok,[]} = do_inet_rpc(Sock,erlang,nodes,[]),
  831:     ?line {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
  832:     ?line {ok,[NN2]} = do_inet_rpc(Sock,erlang,nodes,[]),
  833:     ?line {ok,[NN]} = do_inet_rpc(Sock2,erlang,nodes,[]),
  834:     ?line [_,HostPartPeer] = string:tokens(atom_to_list(NN),"@"),
  835:     ?line [_,MyHostPart] = string:tokens(atom_to_list(node()),"@"),
  836:     % Give net_kernel a chance to change the state of the node to up to.
  837:     ?line receive after 1000 -> ok end,
  838:     case HostPartPeer of
  839: 	MyHostPart ->
  840: 	    ?line ok = stop_relay_node(Sock),
  841: 	    ?line {ok,pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]);
  842: 	_ ->
  843: 	    ?line {ok, true} = do_inet_rpc(Sock,net_kernel,disconnect,[NN2]),
  844: 	    receive
  845: 	    after 500 -> ok
  846: 	    end
  847:     end,
  848:     ?line {ok, []} = do_inet_rpc(Sock2,erlang,nodes,[]),
  849:     Sock3 = case HostPartPeer of
  850: 		MyHostPart ->
  851: 		    ?line start_relay_node(dist_auto_connect_relay_node,[]);
  852: 		_ ->
  853: 		    Sock
  854: 	    end,
  855:     ?line TS1 = timestamp(),
  856:     ?line {ok, pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
  857:     ?line TS2 = timestamp(),
  858:     RefT = net_kernel:connecttime() - 1000,
  859:     ?line true = ((TS2 - TS1) < RefT),
  860:     ?line TS3 = timestamp(),
  861:     ?line {ok, true} = do_inet_rpc(Sock2,erlang,monitor_node,
  862: 				   [NN,true,[allow_passive_connect]]),
  863:     ?line TS4 = timestamp(),
  864:     ?line true = ((TS4 - TS3) > RefT),
  865:     ?line {ok, pong} = do_inet_rpc(Sock3,net_adm,ping,[NN2]),
  866:     ?line {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
  867:     ?line {ok, true} = do_inet_rpc(Sock3,net_kernel,disconnect,[NN2]),
  868:     receive
  869:     after 500 -> ok
  870:     end,
  871:     ?line {ok, pang} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
  872:     ?line {ok, true} = do_inet_rpc(Sock2,net_kernel,connect_node,[NN]),
  873:     ?line {ok, pong} = do_inet_rpc(Sock2,net_adm,ping,[NN]),
  874:     ?line stop_relay_node(Sock3),
  875:     ?line stop_relay_node(Sock2).
  876:     
  877: 
  878: 
  879: %% Start a relay node and a lonely (dist_auto_connect never) node.
  880: %% Lonely node pings relay node. That should fail. 
  881: %% Lonely node connects to relay node with net_kernel:connect_node/1.
  882: %% Result is sent here through relay node.
  883: dist_auto_connect_never(Config) when is_list(Config) ->
  884:     Self = self(),
  885:     ?line {ok, RelayNode} = 
  886: 	start_node(dist_auto_connect_relay),
  887:     ?line spawn(RelayNode, 
  888: 	      fun() ->
  889: 		      register(dist_auto_connect_relay, self()),
  890: 		      dist_auto_connect_relay(Self) 
  891: 	      end),
  892:     ?line {ok, Handle} = dist_auto_connect_start(dist_auto_connect, never),
  893:     ?line Result = 
  894: 	receive
  895: 	    {do_dist_auto_connect, ok} ->
  896: 		ok;
  897: 	    {do_dist_auto_connect, Error} ->
  898: 		{error, Error};
  899: 	    Other ->
  900: 		{error, Other}
  901: 	after 32000 ->
  902: 		timeout
  903: 	end,
  904:     ?line stop_node(RelayNode),
  905:     ?line Stopped =  dist_auto_connect_stop(Handle),
  906:     ?line Junk = 
  907: 	receive 
  908: 	    {do_dist_auto_connect, _} = J -> 
  909: 		J 
  910: 	after 0 -> 
  911: 		ok 
  912: 	end,
  913:     ?line {ok, ok, ok} = {Result, Stopped, Junk},
  914:     ok.
  915: 
  916: 
  917: do_dist_auto_connect([never]) ->
  918:     Node = list_to_atom("dist_auto_connect_relay@" ++ hostname()),
  919:     io:format("~p:do_dist_auto_connect([false]) Node=~p~n", 
  920: 	      [?MODULE, Node]),
  921:     Ping = net_adm:ping(Node),
  922:     io:format("~p:do_dist_auto_connect([false]) Ping=~p~n", 
  923: 	      [?MODULE, Ping]),
  924:     Result = case Ping of
  925: 		 pang -> ok;
  926: 		 _ -> {error, Ping}
  927: 	     end,
  928:     io:format("~p:do_dist_auto_connect([false]) Result=~p~n", 
  929: 	      [?MODULE, Result]),
  930:     net_kernel:connect_node(Node),
  931:     catch {dist_auto_connect_relay, Node} ! {do_dist_auto_connect, Result};
  932: %    receive after 1000 -> ok end,
  933: %    halt();
  934: 
  935: do_dist_auto_connect(Arg) ->
  936:     io:format("~p:do_dist_auto_connect(~p)~n", 
  937: 	      [?MODULE, Arg]),
  938:     receive after 10000 -> ok end,
  939:     halt().
  940: 	    
  941: 
  942: dist_auto_connect_start(Name, Value) when is_atom(Name) ->
  943:     dist_auto_connect_start(atom_to_list(Name), Value);
  944: dist_auto_connect_start(Name, Value) when is_list(Name), is_atom(Value) ->
  945:     Node = list_to_atom(lists:append([Name, "@", hostname()])),
  946:     ModuleDir = filename:dirname(code:which(?MODULE)),
  947:     ValueStr = atom_to_list(Value),
  948:     Cookie = atom_to_list(erlang:get_cookie()),
  949:     Cmd = lists:concat(
  950: 	    [%"xterm -e ",
  951: 	     atom_to_list(lib:progname()),
  952: %	     " -noinput ",
  953: 	     " -detached ", 
  954: 	     long_or_short(), " ", Name,
  955: 	     " -setcookie ", Cookie,
  956: 	     " -pa ", ModuleDir,
  957: 	     " -s ", atom_to_list(?MODULE), 
  958: 	     " do_dist_auto_connect ", ValueStr,
  959: 	     " -kernel dist_auto_connect ", ValueStr]),
  960:     io:format("~p:dist_auto_connect_start() cmd: ~p~n", [?MODULE, Cmd]),
  961:     Port = open_port({spawn, Cmd}, [stream]),
  962:     {ok, {Port, Node}}.
  963: 
  964: 
  965: dist_auto_connect_stop({Port, Node}) ->
  966:     Pid = spawn_link(fun() -> rpc:call(Node, erlang, halt, []) end),
  967:     dist_auto_connect_stop(Port, Node, Pid, 5000).
  968: 
  969: dist_auto_connect_stop(Port, _Node, Pid, N) when is_integer(N), N =< 0 ->
  970:     exit(Pid, normal),
  971:     catch erlang:port_close(Port),
  972:     Result = {error, node_not_down},
  973:     io:format("~p:dist_auto_connect_stop() ~p~n", [?MODULE, Result]),
  974:     Result;
  975: dist_auto_connect_stop(Port, Node, Pid, N) when is_integer(N) ->
  976:     case net_adm:ping(Node) of
  977: 	pong ->
  978: 	    receive after 100 -> ok end,
  979: 	    dist_auto_connect_stop(Port, Node, Pid, N-100);
  980: 	pang ->
  981: 	    exit(Pid, normal),
  982: 	    catch erlang:port_close(Port),
  983: 	    io:format("~p:dist_auto_connect_stop() ok~n", [?MODULE]),
  984: 	    ok
  985:     end.
  986: 
  987: 
  988: dist_auto_connect_relay(Parent) ->	    
  989:     receive X ->
  990: 	    catch Parent ! X
  991:     end,
  992:     dist_auto_connect_relay(Parent).
  993: 
  994: 
  995: dist_parallel_send(doc) ->
  996:     [];
  997: dist_parallel_send(suite) ->
  998:     [];
  999: dist_parallel_send(Config) when is_list(Config) ->
 1000:     ?line {ok, RNode} = start_node(dist_parallel_receiver),
 1001:     ?line {ok, SNode} = start_node(dist_parallel_sender),
 1002:     ?line WatchDog = spawn_link(
 1003: 		       fun () ->
 1004: 			       TRef = erlang:start_timer((?DEFAULT_TIMETRAP
 1005: 							  div 2),
 1006: 							 self(),
 1007: 							 oops),
 1008: 			       receive
 1009: 				   {timeout, TRef, _ } ->
 1010: 				       spawn(SNode,
 1011: 					     fun () ->
 1012: 						     abort(timeout)
 1013: 					     end),
 1014: 				       spawn(RNode,
 1015: 					     fun () ->
 1016: 						     abort(timeout)
 1017: 					     end)
 1018: %%				       rpc:cast(SNode, erlang, halt,
 1019: %%						["Timetrap (sender)"]),
 1020: %%				       rpc:cast(RNode, erlang, halt,
 1021: %%						["Timetrap (receiver)"])
 1022: 			       end
 1023: 		       end),
 1024:     ?line MkSndrs = fun (Receiver) ->
 1025: 			    lists:map(fun (_) ->
 1026: 					      spawn_link(SNode,
 1027: 							 ?MODULE,
 1028: 							 dist_parallel_sender,
 1029: 							 [self(),
 1030: 							  Receiver,
 1031: 							  1000])
 1032: 				      end,
 1033: 				      lists:seq(1, 64))
 1034: 		    end,
 1035:     ?line SndrsStart = fun (Sndrs) ->
 1036: 			       Parent = self(),
 1037: 			       spawn_link(
 1038: 				 SNode,
 1039: 				 fun () ->
 1040: 					 lists:foreach(fun (P) ->
 1041: 							       P ! {go, Parent}
 1042: 						       end,
 1043: 						       Sndrs)
 1044: 				 end)
 1045: 		       end,
 1046:     ?line SndrsWait = fun (Sndrs) ->
 1047: 			      lists:foreach(fun (P) ->
 1048: 						    receive {P, done} -> ok end
 1049: 					    end,
 1050: 					    Sndrs)
 1051: 		      end,
 1052:     ?line DPR = spawn_link(RNode, ?MODULE, dist_parallel_receiver, []),
 1053:     ?line Sndrs1 = MkSndrs(DPR),
 1054:     ?line SndrsStart(Sndrs1),
 1055:     ?line SndrsWait(Sndrs1),
 1056:     ?line unlink(DPR),
 1057:     ?line exit(DPR, bang),
 1058: 
 1059:     ?line DEPR = spawn_link(RNode, ?MODULE, dist_evil_parallel_receiver, []),
 1060:     ?line Sndrs2 = MkSndrs(DEPR),
 1061:     ?line SndrsStart(Sndrs2),
 1062:     ?line SndrsWait(Sndrs2),
 1063:     ?line unlink(DEPR),
 1064:     ?line exit(DEPR, bang),
 1065: 
 1066:     ?line unlink(WatchDog),
 1067:     ?line exit(WatchDog, bang),
 1068: 
 1069:     ?line stop_node(RNode),
 1070:     ?line stop_node(SNode),
 1071: 
 1072:     ?line ok.
 1073: 
 1074: do_dist_parallel_sender(Parent, _Receiver, 0) ->
 1075:     Parent ! {self(), done};
 1076: do_dist_parallel_sender(Parent, Receiver, N) ->
 1077:     Receiver ! {self(), "Some data"},
 1078:     do_dist_parallel_sender(Parent, Receiver, N-1).
 1079: 
 1080: dist_parallel_sender(Parent, Receiver, N) ->
 1081:     receive {go, Parent} -> ok end,
 1082:     do_dist_parallel_sender(Parent, Receiver, N).
 1083: 
 1084: dist_parallel_receiver() ->
 1085:     receive {_Sender, _Data} -> ok end,
 1086:     dist_parallel_receiver().
 1087: 
 1088: dist_evil_parallel_receiver() ->
 1089:     receive {Sender, _Data} -> ok end,
 1090:     net_kernel:disconnect(node(Sender)),
 1091:     dist_evil_parallel_receiver().
 1092: 
 1093: atom_roundtrip(Config) when is_list(Config) ->
 1094:     ?line AtomData = atom_data(),
 1095:     ?line verify_atom_data(AtomData),
 1096:     ?line {ok, Node} = start_node(Config),
 1097:     ?line do_atom_roundtrip(Node, AtomData),
 1098:     ?line stop_node(Node),
 1099:     ?line ok.
 1100: 
 1101: atom_roundtrip_r15b(Config) when is_list(Config) ->
 1102:     case ?t:is_release_available("r15b") of
 1103: 	true ->
 1104: 	    ?line AtomData = atom_data(),
 1105: 	    ?line verify_atom_data(AtomData),
 1106: 	    ?line {ok, Node} = start_node(Config, [], "r15b"),
 1107: 	    ?line do_atom_roundtrip(Node, AtomData),
 1108: 	    ?line stop_node(Node),
 1109: 	    ?line ok;
 1110: 	false ->
 1111: 	    ?line {skip,"No OTP R15B available"}
 1112:     end.
 1113: 
 1114: unicode_atom_roundtrip(Config) when is_list(Config) ->
 1115:     ?line AtomData = unicode_atom_data(),
 1116:     ?line verify_atom_data(AtomData),
 1117:     ?line {ok, Node} = start_node(Config),
 1118:     ?line do_atom_roundtrip(Node, AtomData),
 1119:     ?line stop_node(Node),
 1120:     ?line ok.
 1121: 
 1122: do_atom_roundtrip(Node, AtomData) ->
 1123:     ?line Parent = self(),
 1124:     ?line Proc = spawn_link(Node, fun () -> verify_atom_data_loop(Parent) end),
 1125:     ?line Proc ! {self(), AtomData},
 1126:     ?line receive {Proc, AD1} -> AtomData = AD1 end,
 1127:     ?line Proc ! {self(), AtomData},
 1128:     ?line receive {Proc, AD2} -> AtomData = AD2 end,
 1129:     ?line RevAtomData = lists:reverse(AtomData),
 1130:     ?line Proc ! {self(), RevAtomData},
 1131:     ?line receive {Proc, RAD1} -> RevAtomData = RAD1 end,
 1132:     ?line unlink(Proc),
 1133:     ?line exit(Proc, bang),
 1134:     ?line ok.
 1135: 
 1136: verify_atom_data_loop(From) ->
 1137:     receive
 1138: 	{From, AtomData} ->
 1139: 	    verify_atom_data(AtomData),
 1140: 	    From ! {self(), AtomData},
 1141: 	    verify_atom_data_loop(From)
 1142:     end.
 1143: 
 1144: atom_data() ->
 1145:     lists:map(fun (N) ->
 1146: 		      ATxt = "a"++integer_to_list(N),
 1147: 		      {list_to_atom(ATxt), ATxt}
 1148: 	      end,
 1149: 	      lists:seq(1, 2000)).
 1150: 
 1151: verify_atom_data(AtomData) ->
 1152:     lists:foreach(fun ({Atom, AtomTxt}) when is_atom(Atom) ->
 1153: 			  AtomTxt = atom_to_list(Atom);
 1154: 		      ({PPR, AtomTxt}) ->
 1155: 			  % Pid, Port, or Ref
 1156: 			  AtomTxt = atom_to_list(node(PPR))
 1157: 		  end,
 1158: 		  AtomData).
 1159: 
 1160: uc_atom_tup(ATxt) ->
 1161:     Atom = string_to_atom(ATxt),
 1162:     ATxt = atom_to_list(Atom),
 1163:     {Atom, ATxt}.
 1164: 
 1165: uc_pid_tup(ATxt) ->
 1166:     ATxtExt = string_to_atom_ext(ATxt),
 1167:     Pid = mk_pid({ATxtExt, 1}, 4711,17),
 1168:     true = is_pid(Pid),
 1169:     Atom = node(Pid),
 1170:     true = is_atom(Atom),
 1171:     ATxt = atom_to_list(Atom),
 1172:     {Pid, ATxt}.
 1173: 
 1174: uc_port_tup(ATxt) ->
 1175:     ATxtExt = string_to_atom_ext(ATxt),
 1176:     Port = mk_port({ATxtExt, 2}, 4711),
 1177:     true = is_port(Port),
 1178:     Atom = node(Port),
 1179:     true = is_atom(Atom),
 1180:     ATxt = atom_to_list(Atom),
 1181:     {Port, ATxt}.
 1182: 
 1183: uc_ref_tup(ATxt) ->
 1184:     ATxtExt = string_to_atom_ext(ATxt),
 1185:     Ref = mk_ref({ATxtExt, 3}, [4711,17, 4711]),
 1186:     true = is_reference(Ref),
 1187:     Atom = node(Ref),
 1188:     true = is_atom(Atom),
 1189:     ATxt = atom_to_list(Atom),
 1190:     {Ref, ATxt}.
 1191: 
 1192: 
 1193: unicode_atom_data() ->
 1194:     [uc_pid_tup(lists:seq(16#1f600, 16#1f600+249) ++ "@host"),
 1195:      uc_pid_tup(lists:seq(16#1f600, 16#1f600+30) ++ "@host"),
 1196:      uc_port_tup(lists:seq(16#1f600, 16#1f600+249) ++ "@host"),
 1197:      uc_port_tup(lists:seq(16#1f600, 16#1f600+30) ++ "@host"),
 1198:      uc_ref_tup(lists:seq(16#1f600, 16#1f600+249) ++ "@host"),
 1199:      uc_ref_tup(lists:seq(16#1f600, 16#1f600+30) ++ "@host"),
 1200:      uc_atom_tup(lists:seq(16#1f600, 16#1f600+254)),
 1201:      uc_atom_tup(lists:seq(16#1f600, 16#1f600+63)),
 1202:      uc_atom_tup(lists:seq(0, 254)),
 1203:      uc_atom_tup(lists:seq(100, 163)),
 1204:      uc_atom_tup(lists:seq(200, 354)),
 1205:      uc_atom_tup(lists:seq(200, 263)),
 1206:      uc_atom_tup(lists:seq(2000, 2254)),
 1207:      uc_atom_tup(lists:seq(2000, 2063)),
 1208:      uc_atom_tup(lists:seq(65500, 65754)),
 1209:      uc_atom_tup(lists:seq(65500, 65563))
 1210:      | lists:map(fun (N) ->
 1211: 			 uc_atom_tup(lists:seq(64000+N, 64254+N))
 1212: 		 end,
 1213: 		 lists:seq(1, 2000))].
 1214: 
 1215: contended_atom_cache_entry(Config) when is_list(Config) ->
 1216:     contended_atom_cache_entry_test(Config, latin1).
 1217: 
 1218: contended_unicode_atom_cache_entry(Config) when is_list(Config) ->
 1219:     contended_atom_cache_entry_test(Config, unicode).
 1220: 
 1221: contended_atom_cache_entry_test(Config, Type) ->
 1222:     ?line TestServer = self(),
 1223:     ?line ProcessPairs = 10,
 1224:     ?line Msgs = 100000,
 1225:     ?line {ok, SNode} = start_node(Config),
 1226:     ?line {ok, RNode} = start_node(Config),
 1227:     ?line Success = make_ref(),
 1228:     ?line spawn_link(
 1229: 	    SNode,
 1230: 	    fun () ->
 1231: 		    erts_debug:set_internal_state(available_internal_state,
 1232: 						  true),
 1233: 		    Master = self(),
 1234: 		    CIX = get_cix(),
 1235: 		    TestAtoms = case Type of
 1236: 				    latin1 ->
 1237: 					get_conflicting_atoms(CIX,
 1238: 							      ProcessPairs);
 1239: 				    unicode ->
 1240: 					get_conflicting_unicode_atoms(CIX,
 1241: 								      ProcessPairs)
 1242: 				end,
 1243: 		    io:format("Testing with the following atoms all using "
 1244: 			      "cache index ~p:~n ~w~n",
 1245: 			      [CIX, TestAtoms]),
 1246: 		    Ps = lists:map(
 1247: 			   fun (A) ->
 1248: 				   Ref = make_ref(),
 1249: 				   R = spawn_link(
 1250: 					 RNode,
 1251: 					 fun () ->
 1252: 						 Atom = receive
 1253: 							    {Ref, txt, ATxt} ->
 1254: 								case Type of
 1255: 								    latin1 ->
 1256: 									list_to_atom(ATxt);
 1257: 								    unicode ->
 1258: 									string_to_atom(ATxt)
 1259: 								end
 1260: 							end,
 1261: 						 receive_ref_atom(Ref,
 1262: 								  Atom,
 1263: 								  Msgs),
 1264: 						 Master ! {self(), success}
 1265: 					 end),
 1266: 				   S = spawn_link(
 1267: 					 SNode,
 1268: 					 fun () ->
 1269: 						 receive go -> ok end,
 1270: 						 R ! {Ref,
 1271: 						      txt,
 1272: 						      atom_to_list(A)},
 1273: 						 send_ref_atom(R, Ref, A, Msgs)
 1274: 					 end),
 1275: 				   {S, R}
 1276: 			   end,
 1277: 			   TestAtoms),
 1278: 		    lists:foreach(fun ({S, _}) ->
 1279: 					  S ! go
 1280: 				  end,
 1281: 				  Ps),
 1282: 		    lists:foreach(fun ({_, R}) ->
 1283: 					  receive {R, success} -> ok end
 1284: 				  end,
 1285: 				  Ps),
 1286: 		    TestServer ! Success
 1287: 	    end),
 1288:     ?line receive
 1289: 	      Success ->
 1290: 		  ok
 1291: 	  end,
 1292:     ?line stop_node(SNode),
 1293:     ?line stop_node(RNode),
 1294:     ?line ok.
 1295: 
 1296: send_ref_atom(_To, _Ref, _Atom, 0) ->
 1297:     ok;
 1298: send_ref_atom(To, Ref, Atom, N) ->
 1299:     To ! {Ref, Atom},
 1300:     send_ref_atom(To, Ref, Atom, N-1).
 1301: 
 1302: receive_ref_atom(_Ref, _Atom, 0) ->
 1303:     ok;
 1304: receive_ref_atom(Ref, Atom, N) ->
 1305:     receive
 1306: 	{Ref, Value} ->
 1307: 	    Atom = Value
 1308:     end,
 1309:     receive_ref_atom(Ref, Atom, N-1).
 1310:       
 1311: get_cix() ->
 1312:     get_cix(1000).
 1313: 
 1314: get_cix(CIX) when is_integer(CIX), CIX < 0 ->
 1315:     get_cix(0);
 1316: get_cix(CIX) when is_integer(CIX) ->
 1317:     get_cix(CIX,
 1318: 	    unwanted_cixs(),
 1319: 	    erts_debug:get_internal_state(max_atom_out_cache_index)).
 1320: 
 1321: get_cix(CIX, Unwanted, MaxCIX) when CIX > MaxCIX ->
 1322:     get_cix(0, Unwanted, MaxCIX);
 1323: get_cix(CIX, Unwanted, MaxCIX) ->
 1324:     case lists:member(CIX, Unwanted) of
 1325: 	true -> get_cix(CIX+1, Unwanted, MaxCIX);
 1326: 	false -> CIX
 1327:     end.
 1328: 
 1329: unwanted_cixs() ->
 1330:     lists:map(fun (Node) ->
 1331: 		      erts_debug:get_internal_state({atom_out_cache_index,
 1332: 						     Node})
 1333: 	      end,
 1334: 	      nodes()).
 1335:     
 1336:     
 1337: get_conflicting_atoms(_CIX, 0) ->
 1338:     [];
 1339: get_conflicting_atoms(CIX, N) ->
 1340:     {A, B, C} = now(),
 1341:     Atom = list_to_atom("atom" ++ integer_to_list(A*1000000000000
 1342: 						  + B*1000000
 1343: 						  + C)),
 1344:     case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of
 1345: 	CIX ->
 1346: 	    [Atom|get_conflicting_atoms(CIX, N-1)];
 1347: 	_ ->
 1348: 	    get_conflicting_atoms(CIX, N)
 1349:     end.
 1350: 
 1351: get_conflicting_unicode_atoms(_CIX, 0) ->
 1352:     [];
 1353: get_conflicting_unicode_atoms(CIX, N) ->
 1354:     {A, B, C} = now(),
 1355:     Atom = string_to_atom([16#1f608] ++ "atom" ++ integer_to_list(A*1000000000000
 1356: 								  + B*1000000
 1357: 								  + C)),
 1358:     case erts_debug:get_internal_state({atom_out_cache_index, Atom}) of
 1359: 	CIX ->
 1360: 	    [Atom|get_conflicting_unicode_atoms(CIX, N-1)];
 1361: 	_ ->
 1362: 	    get_conflicting_unicode_atoms(CIX, N)
 1363:     end.
 1364: 
 1365: -define(COOKIE, '').
 1366: -define(DOP_LINK,		1).
 1367: -define(DOP_SEND,		2).
 1368: -define(DOP_EXIT,		3).
 1369: -define(DOP_UNLINK,		4).
 1370: -define(DOP_REG_SEND,		6).
 1371: -define(DOP_GROUP_LEADER,	7).
 1372: -define(DOP_EXIT2,		8).
 1373: 
 1374: -define(DOP_SEND_TT,		12).
 1375: -define(DOP_EXIT_TT,		13).
 1376: -define(DOP_REG_SEND_TT,	16).
 1377: -define(DOP_EXIT2_TT,		18).
 1378: 
 1379: -define(DOP_MONITOR_P,		19).
 1380: -define(DOP_DEMONITOR_P,	20).
 1381: -define(DOP_MONITOR_P_EXIT,	21).
 1382: 
 1383: start_monitor(Offender,P) ->
 1384:     ?line Parent = self(),
 1385:     ?line Q = spawn(Offender,
 1386: 		    fun () -> 
 1387: 			    Ref = erlang:monitor(process,P),
 1388: 			    Parent ! {self(),ref,Ref},
 1389: 			    receive
 1390: 				just_stay_alive -> ok
 1391: 			    end
 1392: 		    end),
 1393:     ?line Ref = receive
 1394: 		    {Q,ref,R} ->
 1395: 			R
 1396: 		after  5000 ->
 1397: 			error
 1398: 		end,
 1399:     io:format("Ref is ~p~n",[Ref]),
 1400:     ok.
 1401: start_link(Offender,P) ->
 1402:     ?line Parent = self(),
 1403:     ?line Q = spawn(Offender,
 1404: 		    fun () ->
 1405: 			    process_flag(trap_exit,true),
 1406: 			    link(P),
 1407: 			    Parent ! {self(),ref,P},
 1408: 			    receive
 1409: 				just_stay_alive -> ok
 1410: 			    end
 1411: 		    end),
 1412:     ?line Ref = receive
 1413: 		    {Q,ref,R} ->
 1414: 			R
 1415: 		after  5000 ->
 1416: 			error
 1417: 		end,
 1418:     io:format("Ref is ~p~n",[Ref]),
 1419:     ok.
 1420: 
 1421: bad_dist_structure(suite) ->
 1422:     [];
 1423: bad_dist_structure(doc) ->
 1424:     ["Test dist messages with valid structure (binary to term ok) but malformed"
 1425:      "control content"];
 1426: bad_dist_structure(Config) when is_list(Config) ->
 1427:     %process_flag(trap_exit,true),
 1428:     ODog = ?config(watchdog, Config),
 1429:     ?t:timetrap_cancel(ODog),
 1430:     Dog = ?t:timetrap(?t:seconds(15)),
 1431: 
 1432:     ?line {ok, Offender} = start_node(bad_dist_structure_offender),
 1433:     ?line {ok, Victim} = start_node(bad_dist_structure_victim),
 1434:     ?line start_node_monitors([Offender,Victim]),
 1435:     ?line Parent = self(),
 1436:     ?line P = spawn(Victim,
 1437: 		    fun () ->
 1438: 			    process_flag(trap_exit,true),
 1439: 			    Parent ! {self(), started},
 1440: 			    receive check_msgs -> ok end,
 1441: 			    bad_dist_struct_check_msgs([one,
 1442: 							two]),
 1443: 			    Parent ! {self(), messages_checked},
 1444: 			    receive done -> ok end
 1445: 		    end),
 1446:     ?line receive {P, started} -> ok end,
 1447:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1448:     ?line verify_up(Offender, Victim),
 1449:     ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
 1450:     ?line start_monitor(Offender,P),
 1451:     ?line P ! one,
 1452:     ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal},2),
 1453:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1454:     ?line start_monitor(Offender,P),
 1455:     ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal,normal},2),
 1456:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1457:     ?line start_link(Offender,P),
 1458:     ?line send_bad_structure(Offender, P,{?DOP_LINK},0),
 1459:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1460:     ?line start_link(Offender,P),
 1461:     ?line send_bad_structure(Offender, P,{?DOP_UNLINK,'replace'},2),
 1462:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1463:     ?line start_link(Offender,P),
 1464:     ?line send_bad_structure(Offender, P,{?DOP_UNLINK,'replace',make_ref()},2),
 1465:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1466:     ?line start_link(Offender,P),
 1467:     ?line send_bad_structure(Offender, P,{?DOP_UNLINK,make_ref(),P},0),
 1468:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1469:     ?line start_link(Offender,P),
 1470:     ?line send_bad_structure(Offender, P,{?DOP_UNLINK,normal,normal},0),
 1471:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1472:     ?line start_monitor(Offender,P),
 1473:     ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P},2),
 1474:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1475:     ?line start_monitor(Offender,P),
 1476:     ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P,normal},2),
 1477:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1478:     ?line start_monitor(Offender,P),
 1479:     ?line send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P},2),
 1480:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1481:     ?line start_monitor(Offender,P),
 1482:     ?line send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P,normal},2),
 1483:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1484:     ?line send_bad_structure(Offender, P,{?DOP_EXIT,'replace',P},2),
 1485:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1486:     ?line send_bad_structure(Offender, P,{?DOP_EXIT,make_ref(),normal,normal},0),
 1487:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1488:     ?line send_bad_structure(Offender, P,{?DOP_EXIT_TT,'replace',token,P},2),
 1489:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1490:     ?line send_bad_structure(Offender, P,{?DOP_EXIT_TT,make_ref(),token,normal,normal},0),
 1491:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1492:     ?line send_bad_structure(Offender, P,{?DOP_EXIT2,'replace',P},2),
 1493:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1494:     ?line send_bad_structure(Offender, P,{?DOP_EXIT2,make_ref(),normal,normal},0),
 1495:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1496:     ?line send_bad_structure(Offender, P,{?DOP_EXIT2_TT,'replace',token,P},2),
 1497:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1498:     ?line send_bad_structure(Offender, P,{?DOP_EXIT2_TT,make_ref(),token,normal,normal},0),
 1499:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1500:     ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace'},2),
 1501:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1502:     ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace','atomic'},2),
 1503:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1504:     ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace',P},0),
 1505:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1506:     ?line send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name},2,{message}),
 1507:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1508:     ?line send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name,token},0,{message}),
 1509:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1510:     ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace',''},2,{message}),
 1511:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1512:     ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',P},0,{message}),
 1513:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1514:     ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name},0,{message}),
 1515:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1516:     ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name,{token}},2,{message}),
 1517:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1518:     ?line send_bad_structure(Offender, P,{?DOP_SEND_TT,'',P},0,{message}),
 1519:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1520:     ?line send_bad_structure(Offender, P,{?DOP_SEND_TT,'',name,token},0,{message}),
 1521:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1522:     ?line send_bad_structure(Offender, P,{?DOP_SEND,''},0,{message}),
 1523:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1524:     ?line send_bad_structure(Offender, P,{?DOP_SEND,'',name},0,{message}),
 1525:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1526:     ?line send_bad_structure(Offender, P,{?DOP_SEND,'',P,{token}},0,{message}),
 1527:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1528:     ?line P ! two,
 1529:     ?line P ! check_msgs,
 1530:     ?line receive 
 1531: 	      {P, messages_checked} -> ok 
 1532: 	  after 5000 ->
 1533: 		  exit(victim_is_dead)
 1534: 	  end,
 1535: 
 1536:     ?line {message_queue_len, 0}
 1537: 	= rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
 1538:     
 1539:     ?line unlink(P),
 1540:     ?line P ! done,
 1541:     ?line stop_node(Offender),
 1542:     ?line stop_node(Victim),
 1543:     ?t:timetrap_cancel(Dog),
 1544:     ok.
 1545: 
 1546: 
 1547: 
 1548: bad_dist_ext_receive(Config) when is_list(Config) ->
 1549:     ?line {ok, Offender} = start_node(bad_dist_ext_receive_offender),
 1550:     ?line {ok, Victim} = start_node(bad_dist_ext_receive_victim),
 1551:     ?line start_node_monitors([Offender,Victim]),
 1552: 
 1553:     ?line Parent = self(),
 1554: 
 1555:     ?line P = spawn_link(Victim,
 1556: 			 fun () ->
 1557: 				 Parent ! {self(), started},
 1558: 				 receive check_msgs -> ok end,
 1559: 				 bad_dist_ext_check_msgs([one,
 1560: 							  two,
 1561: 							  three]),
 1562: 				 Parent ! {self(), messages_checked},
 1563: 				 receive done -> ok end
 1564: 			 end),
 1565: 
 1566:     ?line receive {P, started} -> ok end,
 1567:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1568:     ?line verify_up(Offender, Victim),
 1569:     ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
 1570:     ?line P ! one,
 1571:     ?line send_bad_msg(Offender, P),
 1572:     ?line P ! two,
 1573:     ?line verify_down(Offender, connection_closed, Victim, killed),
 1574:     ?line {message_queue_len, 2}
 1575: 	= rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
 1576: 
 1577:     ?line Suspended = make_ref(),
 1578:     ?line S = spawn(Victim,
 1579: 		     fun () ->
 1580: 			     erlang:suspend_process(P),
 1581: 			     Parent ! Suspended,
 1582: 			     receive after infinity -> ok end
 1583: 		     end),
 1584:     ?line MS = erlang:monitor(process, S),
 1585:     ?line receive Suspended -> ok end,
 1586:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1587:     ?line verify_up(Offender, Victim),
 1588:     ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
 1589:     ?line send_bad_msgs(Offender, P, 5),
 1590:     ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])),
 1591:     ?line P ! three,
 1592:     ?line send_bad_msgs(Offender, P, 5),
 1593: 
 1594:     %% Make sure bad msgs has reached Victim
 1595:     ?line rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
 1596: 
 1597:     ?line verify_still_up(Offender, Victim),
 1598:     ?line {message_queue_len, 13}
 1599: 	= rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
 1600:     
 1601:     ?line exit(S, bang),
 1602:     ?line receive {'DOWN', MS, process, S, bang} -> ok end,
 1603:     ?line verify_down(Offender, connection_closed, Victim, killed),
 1604:     ?line {message_queue_len, 3}
 1605: 	= rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
 1606: 
 1607:     ?line P ! check_msgs,
 1608:     ?line receive {P, messages_checked} -> ok end,
 1609: 
 1610:     ?line {message_queue_len, 0}
 1611: 	= rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
 1612:     
 1613:     ?line P ! done,
 1614:     ?line unlink(P),
 1615:     ?line verify_no_down(Offender, Victim),
 1616:     ?line stop_node(Offender),
 1617:     ?line stop_node(Victim).
 1618: 
 1619: 
 1620: bad_dist_ext_process_info(Config) when is_list(Config) ->
 1621:     ?line {ok, Offender} = start_node(bad_dist_ext_process_info_offender),
 1622:     ?line {ok, Victim} = start_node(bad_dist_ext_process_info_victim),
 1623:     ?line start_node_monitors([Offender,Victim]),
 1624: 
 1625:     ?line Parent = self(),
 1626:     ?line P = spawn_link(Victim,
 1627: 			 fun () ->
 1628: 				 Parent ! {self(), started},
 1629: 				 receive check_msgs -> ok end,
 1630: 				 bad_dist_ext_check_msgs([one, two]),
 1631: 				 Parent ! {self(), messages_checked},
 1632: 				 receive done -> ok end
 1633: 			 end),
 1634: 
 1635:     ?line receive {P, started} -> ok end,
 1636:     ?line P ! one,
 1637:     
 1638:     ?line Suspended = make_ref(),
 1639:     ?line S = spawn(Victim,
 1640: 		     fun () ->
 1641: 			     erlang:suspend_process(P),
 1642: 			     Parent ! Suspended,
 1643: 			     receive after infinity -> ok end
 1644: 		     end),
 1645: 
 1646:     ?line receive Suspended -> ok end,
 1647:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1648:     ?line verify_up(Offender, Victim),
 1649:     ?line send_bad_msgs(Offender, P, 5),
 1650: 
 1651:     ?line P ! two,
 1652:     ?line send_bad_msgs(Offender, P, 5),
 1653: 
 1654:     %% Make sure bad msgs has reached Victim
 1655:     ?line rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
 1656: 
 1657:     ?line verify_still_up(Offender, Victim),
 1658:     ?line {message_queue_len, 12}
 1659: 	= rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
 1660:     ?line verify_still_up(Offender, Victim),
 1661:     ?line [{message_queue_len, 2},
 1662: 	   {messages, [one, two]}]
 1663: 	= rpc:call(Victim, erlang, process_info, [P, [message_queue_len,
 1664: 						      messages]]),
 1665:     ?line verify_down(Offender, connection_closed, Victim, killed),
 1666: 
 1667:     ?line P ! check_msgs,
 1668:     ?line exit(S, bang),
 1669:     ?line receive {P, messages_checked} -> ok end,
 1670: 
 1671:     ?line {message_queue_len, 0}
 1672: 	= rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
 1673: 
 1674:     ?line P ! done,
 1675:     ?line unlink(P),
 1676:     ?line verify_no_down(Offender, Victim),
 1677:     ?line stop_node(Offender),
 1678:     ?line stop_node(Victim).
 1679: 
 1680: bad_dist_ext_control(Config) when is_list(Config) ->
 1681:     ?line {ok, Offender} = start_node(bad_dist_ext_control_offender),
 1682:     ?line {ok, Victim} = start_node(bad_dist_ext_control_victim),
 1683:     ?line start_node_monitors([Offender,Victim]),
 1684: 
 1685:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1686:     ?line verify_up(Offender, Victim),
 1687:     ?line send_bad_dhdr(Offender, Victim),
 1688:     ?line verify_down(Offender, connection_closed, Victim, killed),
 1689: 
 1690:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1691:     ?line verify_up(Offender, Victim),
 1692:     ?line send_bad_ctl(Offender, Victim),
 1693:     ?line verify_down(Offender, connection_closed, Victim, killed),
 1694: 
 1695:     ?line verify_no_down(Offender, Victim),
 1696:     ?line stop_node(Offender),
 1697:     ?line stop_node(Victim).
 1698: 
 1699: bad_dist_ext_connection_id(Config) when is_list(Config) ->
 1700:     ?line {ok, Offender} = start_node(bad_dist_ext_connection_id_offender),
 1701:     ?line {ok, Victim} = start_node(bad_dist_ext_connection_id_victim),
 1702:     ?line start_node_monitors([Offender,Victim]),
 1703: 
 1704:     ?line Parent = self(),
 1705:     ?line P = spawn_link(Victim,
 1706: 			 fun () ->
 1707: 				 Parent ! {self(), started},
 1708: 				 receive check_msgs -> ok end,
 1709: 				 bad_dist_ext_check_msgs([]),
 1710: 				 Parent ! {self(), messages_checked},
 1711: 				 receive done -> ok end
 1712: 			 end),
 1713: 
 1714:     ?line receive {P, started} -> ok end,
 1715:     ?line Suspended = make_ref(),
 1716:     ?line S = spawn(Victim,
 1717: 		    fun () ->
 1718: 			    erlang:suspend_process(P),
 1719: 			    Parent ! Suspended,
 1720: 			    receive after infinity -> ok end
 1721: 		    end),
 1722:     ?line MS = erlang:monitor(process, S),
 1723:     ?line receive Suspended -> ok end,
 1724:     ?line pong = rpc:call(Victim, net_adm, ping, [Offender]),
 1725:     ?line verify_up(Offender, Victim),
 1726:     ?line send_bad_msg(Offender, P),
 1727: 
 1728:     %% Make sure bad msg has reached Victim
 1729:     ?line rpc:call(Offender, rpc, call, [Victim, erlang, node, []]),
 1730: 
 1731:     ?line {message_queue_len, 1}
 1732: 	= rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
 1733: 
 1734:     ?line true = rpc:call(Offender, net_kernel, disconnect, [Victim]),
 1735:     ?line verify_down(Offender, disconnect, Victim, connection_closed),
 1736:     ?line pong = rpc:call(Offender, net_adm, ping, [Victim]),
 1737: 
 1738:     ?line verify_up(Offender, Victim),
 1739:     %% We have a new connection between Offender and Victim, bad message
 1740:     %% should not bring it down.
 1741: 
 1742:     ?line {message_queue_len, 1}
 1743: 	= rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
 1744: 
 1745:     ?line exit(S, bang),
 1746:     ?line receive {'DOWN', MS, process, S, bang} -> ok end,
 1747:     %% Wait for a while (if the connection is taken down it might take a
 1748:     %% while).
 1749:     ?line receive after 2000 -> ok end,
 1750:     ?line verify_still_up(Offender, Victim),
 1751: 
 1752:     ?line P ! check_msgs,
 1753:     ?line receive {P, messages_checked} -> ok end,
 1754: 
 1755:     ?line {message_queue_len, 0}
 1756: 	= rpc:call(Victim, erlang, process_info, [P, message_queue_len]),
 1757:     
 1758:     ?line verify_still_up(Offender, Victim),
 1759:     ?line P ! done,
 1760:     ?line unlink(P),
 1761:     ?line verify_no_down(Offender, Victim),
 1762:     ?line stop_node(Offender),
 1763:     ?line stop_node(Victim).
 1764: 
 1765: 
 1766: bad_dist_struct_check_msgs([]) ->
 1767:     receive
 1768: 	Msg ->
 1769: 	    exit({unexpected_message, Msg})
 1770:     after 0 ->
 1771: 	    ok
 1772:     end;
 1773: bad_dist_struct_check_msgs([M|Ms]) ->
 1774:     receive
 1775: 	{'EXIT',_,_} = EM ->
 1776: 	    io:format("Ignoring exit message: ~p~n",[EM]),
 1777: 	    bad_dist_struct_check_msgs([M|Ms]);
 1778: 	Msg ->
 1779: 	    M = Msg,
 1780: 	    bad_dist_struct_check_msgs(Ms)
 1781:     end.
 1782: bad_dist_ext_check_msgs([]) ->
 1783:     receive
 1784: 	Msg ->
 1785: 	    exit({unexpected_message, Msg})
 1786:     after 0 ->
 1787: 	    ok
 1788:     end;
 1789: bad_dist_ext_check_msgs([M|Ms]) ->
 1790:     receive
 1791: 	Msg ->
 1792: 	    M = Msg,
 1793: 	    bad_dist_ext_check_msgs(Ms)
 1794:     end.
 1795:     
 1796: 
 1797: dport_reg_send(Node, Name, Msg) ->
 1798:     DPrt = case dport(Node) of
 1799: 	       undefined ->
 1800: 		   pong = net_adm:ping(Node),
 1801: 		   dport(Node);
 1802: 	       Prt ->
 1803: 		   Prt
 1804: 	   end,
 1805:     port_command(DPrt, [dmsg_hdr(),
 1806: 			dmsg_ext({?DOP_REG_SEND,
 1807: 				  self(),
 1808: 				  ?COOKIE,
 1809: 				  Name}),
 1810: 			dmsg_ext(Msg)]).
 1811: 
 1812: 
 1813: dport_send(To, Msg) ->
 1814:     Node = node(To),
 1815:     DPrt = case dport(Node) of
 1816: 	       undefined ->
 1817: 		   pong = net_adm:ping(Node),
 1818: 		   dport(Node);
 1819: 	       Prt ->
 1820: 		   Prt
 1821: 	   end,
 1822:     port_command(DPrt, [dmsg_hdr(),
 1823: 			dmsg_ext({?DOP_SEND,
 1824: 				  ?COOKIE,
 1825: 				  To}),
 1826: 			dmsg_ext(Msg)]).
 1827: send_bad_structure(Offender,Victim,Bad,WhereToPutSelf) ->
 1828:     send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,[]).
 1829: send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) ->
 1830:     Parent = self(),
 1831:     Done = make_ref(),
 1832:     spawn(Offender,
 1833: 	  fun () ->
 1834: 		  Node = node(Victim),
 1835: 		  pong = net_adm:ping(Node),
 1836: 		  DPrt = dport(Node),
 1837: 		  Bad1 = case WhereToPutSelf of
 1838: 			     0 -> 
 1839: 				 Bad;
 1840: 			     N when N > 0 ->
 1841: 				 setelement(N,Bad,self())
 1842: 			 end,
 1843: 		  DData = [dmsg_hdr(),
 1844: 			   dmsg_ext(Bad1)] ++
 1845: 		      case PayLoad of
 1846: 			  [] -> [];
 1847: 			  _Other -> [dmsg_ext(PayLoad)]
 1848: 		      end,
 1849: 		  port_command(DPrt, DData),
 1850: 		  Parent ! {DData,Done}
 1851: 	  end),
 1852:     receive 
 1853: 	{WhatSent,Done} -> 
 1854: 	    io:format("Offender sent ~p~n",[WhatSent]),
 1855: 	    ok
 1856:     after 5000 ->
 1857: 	    exit(unable_to_send)
 1858:     end.
 1859:    
 1860: 
 1861: %% send_bad_msgs():
 1862: %% Send a valid distribution header and control message
 1863: %% but an invalid message. This invalid message will be
 1864: %% enqueued in the receivers message queue.
 1865: send_bad_msg(BadNode, To) ->
 1866:     send_bad_msgs(BadNode, To, 1).
 1867: 
 1868: send_bad_msgs(BadNode, To, Repeat) when is_atom(BadNode),
 1869: 					is_pid(To),
 1870: 					is_integer(Repeat) ->
 1871:     Parent = self(),
 1872:     Done = make_ref(),
 1873:     spawn_link(BadNode,
 1874: 	       fun () ->
 1875: 		       Node = node(To),
 1876: 		       pong = net_adm:ping(Node),
 1877: 		       DPrt = dport(Node),
 1878: 		       DData = [dmsg_hdr(),
 1879: 				dmsg_ext({?DOP_SEND, ?COOKIE, To}),
 1880: 				dmsg_bad_atom_cache_ref()],
 1881: 		       repeat(fun () -> port_command(DPrt, DData) end, Repeat),
 1882: 		       Parent ! Done
 1883: 	       end),
 1884:     receive Done -> ok end.
 1885: 
 1886: %% send_bad_ctl():
 1887: %% Send a valid distribution header but an invalid control message.
 1888: send_bad_ctl(BadNode, ToNode) when is_atom(BadNode), is_atom(ToNode) ->
 1889:     Parent = self(),
 1890:     Done = make_ref(),
 1891:     spawn_link(BadNode,
 1892: 	       fun () ->
 1893: 		       pong = net_adm:ping(ToNode),
 1894: 		       %% We creat a valid ctl msg and replace an
 1895: 		       %% atom with an invalid atom cache reference
 1896: 		       <<131,Replace/binary>> = term_to_binary(replace),
 1897: 		       Ctl = dmsg_ext({?DOP_REG_SEND,
 1898: 				       self(),
 1899: 				       ?COOKIE,
 1900: 				       replace}),
 1901: 		       CtlBeginSize = size(Ctl) - size(Replace),
 1902: 		       <<CtlBegin:CtlBeginSize/binary, Replace/binary>> = Ctl,
 1903: 		       port_command(dport(ToNode),
 1904: 				    [dmsg_fake_hdr2(),
 1905: 				     CtlBegin,
 1906: 				     dmsg_bad_atom_cache_ref(),
 1907: 				     dmsg_ext({a, message})]),
 1908: 		       Parent ! Done
 1909: 	       end),
 1910:     receive Done -> ok end.
 1911: 
 1912: %% send_bad_dhr():
 1913: %% Send an invalid distribution header
 1914: send_bad_dhdr(BadNode, ToNode) when is_atom(BadNode), is_atom(ToNode) ->
 1915:     Parent = self(),
 1916:     Done = make_ref(),
 1917:     spawn_link(BadNode,
 1918: 	       fun () ->
 1919: 		       pong = net_adm:ping(ToNode),
 1920: 		       port_command(dport(ToNode), dmsg_bad_hdr()),
 1921: 		       Parent ! Done
 1922: 	       end),
 1923:     receive Done -> ok end.
 1924: 
 1925: dport(Node) when is_atom(Node) ->
 1926:     case catch erts_debug:get_internal_state(available_internal_state) of
 1927: 	true -> true;
 1928: 	_ -> erts_debug:set_internal_state(available_internal_state, true)
 1929:     end,
 1930:     erts_debug:get_internal_state({dist_port, Node}).
 1931: 
 1932: dmsg_hdr() ->
 1933:     [131, % Version Magic
 1934:      $D,  % Dist header
 1935:      0].  % No atom cache referenses
 1936: 
 1937: dmsg_bad_hdr() ->
 1938:     [131, % Version Magic
 1939:      $D,  % Dist header
 1940:      255].  % 255 atom references
 1941:     
 1942: 
 1943: %% dmsg_fake_hdr1() ->
 1944: %%     A = <<"fake header atom 1">>,
 1945: %%     [131, % Version Magic
 1946: %%      $D, 1, 16#8, 0, size(A), A]. % Fake header
 1947: 
 1948: dmsg_fake_hdr2() ->
 1949:     A1 = <<"fake header atom 1">>,
 1950:     A2 = <<"atom 2">>,
 1951:     A3 = <<"atom 3">>,
 1952:     [131, % Version Magic
 1953:      $D,
 1954:      3,
 1955:      16#88, 16#08, % Flags
 1956:      0, size(A1), A1,
 1957:      1, size(A2), A2,
 1958:      2, size(A3), A3].
 1959: 
 1960: dmsg_ext(Term) ->	
 1961:     <<131, Res/binary>> = term_to_binary(Term),
 1962:     Res.
 1963: 
 1964: dmsg_bad_atom_cache_ref() ->
 1965:     [$R, 137].
 1966: 
 1967: %%% Utilities
 1968: 
 1969: timestamp() ->
 1970:     {A,B,C} = erlang:now(),
 1971:     (C div 1000) + (B * 1000) + (A * 1000000000).
 1972: 
 1973: start_node(X) ->
 1974:     start_node(X, [], []).
 1975: 
 1976: start_node(X, Y) ->
 1977:     start_node(X, Y, []).
 1978: 
 1979: start_node(Name, Args, Rel) when is_atom(Name), is_list(Rel) ->
 1980:     Pa = filename:dirname(code:which(?MODULE)),
 1981:     Cookie = atom_to_list(erlang:get_cookie()),
 1982:     RelArg = case Rel of
 1983: 		 [] -> [];
 1984: 		 _ -> [{erl,[{release,Rel}]}]
 1985: 	     end,
 1986:     test_server:start_node(Name, slave, 
 1987: 			   [{args,
 1988: 			     Args++" -setcookie "++Cookie++" -pa \""++Pa++"\""}
 1989: 			    | RelArg]);
 1990: start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) ->
 1991:     Name = list_to_atom((atom_to_list(?MODULE)
 1992: 			 ++ "-"
 1993: 			 ++ atom_to_list(?config(testcase, Config))
 1994: 			 ++ "-"
 1995: 			 ++ integer_to_list(timestamp()))),
 1996:     start_node(Name, Args, Rel).
 1997: 
 1998: stop_node(Node) ->
 1999:     test_server:stop_node(Node).
 2000: 
 2001: freeze_node(Node, MS) ->
 2002:     Own = 300,
 2003:     DoingIt = make_ref(),
 2004:     Freezer = self(),
 2005:     spawn_link(Node,
 2006: 	       fun () ->
 2007: 		       erts_debug:set_internal_state(available_internal_state,
 2008: 						     true),
 2009: 		       dport_send(Freezer, DoingIt),
 2010: 		       receive after Own -> ok end,
 2011: 		       erts_debug:set_internal_state(block, MS+Own)
 2012: 	       end),
 2013:     receive DoingIt -> ok end,
 2014:     receive after Own -> ok end.
 2015: 
 2016: inet_rpc_nodename({N,H,_Sock}) ->
 2017:     list_to_atom(N++"@"++H).
 2018: 
 2019: do_inet_rpc({_,_,Sock},M,F,A) ->
 2020:     Bin = term_to_binary({M,F,A}),
 2021:     gen_tcp:send(Sock,Bin),
 2022:     case gen_tcp:recv(Sock,0) of
 2023: 	{ok, Bin2} ->
 2024: 	    T = binary_to_term(Bin2),
 2025: 	    {ok,T};
 2026: 	Else ->
 2027: 	    {error, Else}
 2028:     end.
 2029: 
 2030: inet_rpc_server([Host, PortList]) ->
 2031:     Port = list_to_integer(PortList),
 2032:     {ok, Sock} = gen_tcp:connect(Host, Port,[binary, {packet, 4}, 
 2033:                                         {active, false}]),
 2034:     inet_rpc_server_loop(Sock).
 2035: 
 2036: inet_rpc_server_loop(Sock) ->
 2037:     case gen_tcp:recv(Sock,0) of
 2038: 	{ok, Bin} ->
 2039: 	    {M,F,A} = binary_to_term(Bin),
 2040: 	    Res = (catch apply(M,F,A)),
 2041: 	    RB = term_to_binary(Res),
 2042: 	    gen_tcp:send(Sock,RB),
 2043: 	    inet_rpc_server_loop(Sock);
 2044: 	_ ->
 2045: 	    erlang:halt()
 2046:     end.
 2047: 				 
 2048: 
 2049: start_relay_node(Node, Args) ->
 2050:     Pa = filename:dirname(code:which(?MODULE)),
 2051:     Cookie = "NOT"++atom_to_list(erlang:get_cookie()),
 2052:     {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, 
 2053:                                         {active, false}]),
 2054:     {ok, Port} = inet:port(LSock),
 2055:     {ok, Host} = inet:gethostname(),
 2056:     RunArg = "-run " ++ atom_to_list(?MODULE) ++ " inet_rpc_server " ++
 2057: 	Host ++ " " ++ integer_to_list(Port),
 2058:     {ok, NN} = 
 2059: 	test_server:start_node(Node, peer, 
 2060: 			       [{args, Args ++
 2061: 				 " -setcookie "++Cookie++" -pa "++Pa++" "++
 2062: 				 RunArg}]),
 2063:     [N,H] = string:tokens(atom_to_list(NN),"@"),
 2064:     {ok, Sock} = gen_tcp:accept(LSock),
 2065:     pang = net_adm:ping(NN),
 2066:     {N,H,Sock}.
 2067: 
 2068: stop_relay_node({N,H,Sock}) ->
 2069:     catch do_inet_rpc(Sock,erlang,halt,[]),
 2070:     catch gen_tcp:close(Sock),
 2071:     wait_dead(N,H,10).
 2072: 
 2073: wait_dead(N,H,0) ->
 2074:     {error,{not_dead,N,H}};
 2075: wait_dead(N,H,X) ->
 2076:     case erl_epmd:port_please(N,H) of
 2077: 	{port,_,_} ->
 2078: 	    receive 
 2079: 	    after 1000 ->
 2080: 		    ok
 2081: 	    end,
 2082: 	    wait_dead(N,H,X-1);
 2083: 	noport ->
 2084: 	    ok;
 2085: 	Else ->
 2086: 	    {error, {unexpected, Else}}
 2087:     end.
 2088:     
 2089: 
 2090: start_node_monitors(Nodes) ->
 2091:     Master = self(),
 2092:     lists:foreach(fun (Node) ->
 2093: 			  spawn(Node,
 2094: 				fun () ->
 2095: 					node_monitor(Master)
 2096: 				end)
 2097: 		  end,
 2098: 		  Nodes),
 2099:     ok.
 2100: 
 2101: node_monitor(Master) ->
 2102:     Opts = [nodedown_reason,{node_type,all}],
 2103:     Nodes0 = nodes(connected),
 2104:     net_kernel:monitor_nodes(true, Opts),
 2105:     Nodes1 = nodes(connected),
 2106:     case lists:sort(Nodes0) == lists:sort(Nodes1) of
 2107: 	true ->
 2108: 	    lists:foreach(fun (Node) ->
 2109: 				  Master ! {nodeup, node(), Node}
 2110: 			  end,
 2111: 			  Nodes0),
 2112: 	    ?t:format("~p ~p: ~p~n", [node(), erlang:now(), Nodes0]),
 2113: 	    node_monitor_loop(Master);
 2114: 	false ->
 2115: 	    net_kernel:monitor_nodes(false, Opts),
 2116: 	    flush_node_changes(),
 2117: 	    node_monitor(Master)
 2118:     end.
 2119: 
 2120: flush_node_changes() ->
 2121:     receive
 2122: 	{NodeChange, _Node, _InfoList} when NodeChange == nodeup;
 2123: 					    NodeChange == nodedown ->
 2124: 	    flush_node_changes()
 2125:     after 0 ->
 2126: 	    ok
 2127:     end.
 2128: 
 2129: node_monitor_loop(Master) ->
 2130:     receive
 2131: 	{nodeup, Node, _InfoList} = Msg ->
 2132: 	    Master ! {nodeup, node(), Node},
 2133: 	    ?t:format("~p ~p: ~p~n", [node(), erlang:now(), Msg]),
 2134: 	    node_monitor_loop(Master);
 2135: 	{nodedown, Node, InfoList} = Msg ->
 2136: 	    Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of
 2137: 			 {value, {nodedown_reason, R}} -> R;
 2138: 			 _ -> undefined
 2139: 		     end,
 2140: 	    Master ! {nodedown, node(), Node, Reason},
 2141: 	    ?t:format("~p ~p: ~p~n", [node(), erlang:now(), Msg]),
 2142: 	    node_monitor_loop(Master)
 2143:     end.
 2144: 
 2145: verify_up(A, B) ->
 2146:     receive {nodeup, A, B} -> ok end,
 2147:     receive {nodeup, B, A} -> ok end.
 2148: 
 2149: verify_still_up(A, B) ->
 2150:     true = lists:member(B, rpc:call(A, erlang, nodes, [connected])),
 2151:     true = lists:member(A, rpc:call(B, erlang, nodes, [connected])),
 2152:     verify_no_down(A, B).
 2153: 
 2154: verify_no_down(A, B) ->
 2155:     receive
 2156: 	{nodedown, A, B, _} = Msg0 ->
 2157: 	    ?t:fail(Msg0)
 2158:     after 0 ->
 2159: 	    ok
 2160:     end,
 2161:     receive
 2162: 	{nodedown, B, A, _} = Msg1 ->
 2163: 	    ?t:fail(Msg1)
 2164:     after 0 ->
 2165: 	    ok
 2166:     end.
 2167: 
 2168: %% verify_down(A, B) ->
 2169: %%     receive {nodedown, A, B, _} -> ok end,
 2170: %%     receive {nodedown, B, A, _} -> ok end.
 2171: 
 2172: verify_down(A, ReasonA, B, ReasonB) ->
 2173:     receive
 2174: 	{nodedown, A, B, _} = Msg0 ->
 2175: 	    {nodedown, A, B, ReasonA} = Msg0
 2176:     end,
 2177:     receive
 2178: 	{nodedown, B, A, _} = Msg1 ->
 2179: 	    {nodedown, B, A, ReasonB} = Msg1
 2180:     end,
 2181:     ok.
 2182: 
 2183: hostname() ->
 2184:     from($@, atom_to_list(node())).
 2185: 
 2186: from(H, [H | T]) -> T;
 2187: from(H, [_ | T]) -> from(H, T);
 2188: from(_, []) -> [].
 2189: 
 2190: %% fun_spawn(Fun) ->
 2191: %%     fun_spawn(Fun, []).
 2192: 
 2193: %% fun_spawn(Fun, Args) ->
 2194: %%     spawn_link(erlang, apply, [Fun, Args]).
 2195: 
 2196: 
 2197: long_or_short() -> 
 2198:     case net_kernel:longnames() of
 2199: 	true -> " -name ";
 2200: 	false -> " -sname "
 2201:     end.
 2202: 
 2203: until(Fun) ->
 2204:     case Fun() of
 2205: 	true ->
 2206: 	    ok;
 2207: 	false ->
 2208: 	    receive after 10 -> ok end,
 2209: 	    until(Fun)
 2210:     end.
 2211: 
 2212: forever(Fun) ->
 2213:     Fun(),
 2214:     forever(Fun).
 2215: 
 2216: abort(Why) ->
 2217:     erts_debug:set_internal_state(available_internal_state, true),
 2218:     erts_debug:set_internal_state(abort, Why).
 2219: 
 2220: 
 2221: start_busy_dist_port_tracer() ->
 2222:     Tracer = spawn_link(fun () -> busy_dist_port_tracer() end),
 2223:     erlang:system_monitor(Tracer, [busy_dist_port]),
 2224:     Tracer.
 2225: 
 2226: stop_busy_dist_port_tracer(Tracer) when is_pid(Tracer) ->
 2227:     unlink(Tracer),
 2228:     exit(Tracer, bye);
 2229: stop_busy_dist_port_tracer(_) ->
 2230:     true.
 2231: 
 2232: busy_dist_port_tracer() ->
 2233:     receive
 2234: 	{monitor, _SuspendedProcess, busy_dist_port, _Port} = M ->
 2235: 	    erlang:display(M),
 2236: 	    busy_dist_port_tracer()
 2237:     end.
 2238: 
 2239: repeat(_Fun, 0) ->
 2240:     ok;
 2241: repeat(Fun, N) ->
 2242:     Fun(),
 2243:     repeat(Fun, N-1).
 2244: 
 2245: string_to_atom_ext(String) ->
 2246:     Utf8List = string_to_utf8_list(String),
 2247:     Len = length(Utf8List),
 2248:     case Len < 256 of
 2249: 	true ->
 2250: 	    [?SMALL_ATOM_UTF8_EXT, Len | Utf8List];
 2251: 	false ->
 2252: 	    [?ATOM_UTF8_EXT, Len bsr 8, Len band 16#ff | Utf8List]
 2253:     end.
 2254: 
 2255: string_to_atom(String) ->
 2256:     binary_to_term(list_to_binary([?VERSION_MAGIC
 2257: 				   | string_to_atom_ext(String)])).
 2258: 
 2259: string_to_utf8_list([]) ->
 2260:     [];
 2261: string_to_utf8_list([CP|CPs]) when is_integer(CP),
 2262: 				   0 =< CP,
 2263: 				   CP =< 16#7F ->
 2264:     [CP | string_to_utf8_list(CPs)];
 2265: string_to_utf8_list([CP|CPs]) when is_integer(CP),
 2266: 				   16#80 =< CP,
 2267: 				   CP =< 16#7FF ->
 2268:     [16#C0 bor (CP bsr 6),
 2269:      16#80 bor (16#3F band CP)
 2270:      | string_to_utf8_list(CPs)];
 2271: string_to_utf8_list([CP|CPs]) when is_integer(CP),
 2272: 				   16#800 =< CP,
 2273: 				   CP =< 16#FFFF ->
 2274:     [16#E0 bor (CP bsr 12),
 2275:      16#80 bor (16#3F band (CP bsr 6)),
 2276:      16#80 bor (16#3F band CP)
 2277:      | string_to_utf8_list(CPs)];
 2278: string_to_utf8_list([CP|CPs]) when is_integer(CP),
 2279: 				   16#10000 =< CP,
 2280: 				   CP =< 16#10FFFF ->
 2281:     [16#F0 bor (CP bsr 18),
 2282:      16#80 bor (16#3F band (CP bsr 12)),
 2283:      16#80 bor (16#3F band (CP bsr 6)),
 2284:      16#80 bor (16#3F band CP)
 2285:      | string_to_utf8_list(CPs)].
 2286: 
 2287: utf8_list_to_string([]) ->
 2288:     [];
 2289: utf8_list_to_string([B|Bs]) when is_integer(B),
 2290: 				 0 =< B,
 2291: 				 B =< 16#7F ->
 2292:     [B | utf8_list_to_string(Bs)];
 2293: utf8_list_to_string([B0, B1 | Bs]) when is_integer(B0),
 2294: 					16#C0 =< B0,
 2295: 					B0 =< 16#DF,
 2296: 					is_integer(B1),
 2297: 					16#80 =< B1,
 2298: 					B1 =< 16#BF ->
 2299:     [(((B0 band 16#1F) bsl 6)
 2300: 	  bor (B1 band 16#3F))
 2301:      | utf8_list_to_string(Bs)];
 2302: utf8_list_to_string([B0, B1, B2 | Bs]) when is_integer(B0),
 2303: 					    16#E0 =< B0,
 2304: 					    B0 =< 16#EF,
 2305: 					    is_integer(B1),
 2306: 					    16#80 =< B1,
 2307: 					    B1 =< 16#BF,
 2308: 					    is_integer(B2),
 2309: 					    16#80 =< B2,
 2310: 					    B2 =< 16#BF ->
 2311:     [(((B0 band 16#F) bsl 12)
 2312: 	  bor ((B1 band 16#3F) bsl 6)
 2313: 	  bor (B2 band 16#3F))
 2314:      | utf8_list_to_string(Bs)];
 2315: utf8_list_to_string([B0, B1, B2, B3 | Bs]) when is_integer(B0),
 2316: 						16#F0 =< B0,
 2317: 						B0 =< 16#F7,
 2318: 						is_integer(B1),
 2319: 						16#80 =< B1,
 2320: 						B1 =< 16#BF,
 2321: 						is_integer(B2),
 2322: 						16#80 =< B2,
 2323: 						B2 =< 16#BF,
 2324: 						is_integer(B3),
 2325: 						16#80 =< B3,
 2326: 						B3 =< 16#BF ->
 2327:     [(((B0 band 16#7) bsl 18)
 2328: 	  bor ((B1 band 16#3F) bsl 12)
 2329: 	  bor ((B2 band 16#3F) bsl 6)
 2330: 	  bor (B3 band 16#3F))
 2331:      | utf8_list_to_string(Bs)].
 2332: 
 2333: mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
 2334:     <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
 2335:     mk_pid({NodeNameExt, Creation}, Number, Serial);
 2336: mk_pid({NodeNameExt, Creation}, Number, Serial) ->
 2337:     case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
 2338: 					      ?PID_EXT,
 2339: 					      NodeNameExt,
 2340: 					      uint32_be(Number),
 2341: 					      uint32_be(Serial),
 2342: 					      uint8(Creation)])) of
 2343: 	Pid when is_pid(Pid) ->
 2344: 	    Pid;
 2345: 	{'EXIT', {badarg, _}} ->
 2346: 	    exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]});
 2347: 	Other ->
 2348: 	    exit({unexpected_binary_to_term_result, Other})
 2349:     end.
 2350: 
 2351: mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
 2352:     <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
 2353:     mk_port({NodeNameExt, Creation}, Number);
 2354: mk_port({NodeNameExt, Creation}, Number) ->
 2355:     case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
 2356: 					      ?PORT_EXT,
 2357: 					      NodeNameExt,
 2358: 					      uint32_be(Number),
 2359: 					      uint8(Creation)])) of
 2360: 	Port when is_port(Port) ->
 2361: 	    Port;
 2362: 	{'EXIT', {badarg, _}} ->
 2363: 	    exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]});
 2364: 	Other ->
 2365: 	    exit({unexpected_binary_to_term_result, Other})
 2366:     end.
 2367: 
 2368: mk_ref({NodeName, Creation}, [Number] = NL) when is_atom(NodeName),
 2369: 						 is_integer(Creation),
 2370: 						 is_integer(Number) ->
 2371:     <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
 2372:     mk_ref({NodeNameExt, Creation}, NL);
 2373: mk_ref({NodeNameExt, Creation}, [Number]) when is_integer(Creation),
 2374: 					       is_integer(Number) ->
 2375:     case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
 2376: 					      ?REFERENCE_EXT,
 2377: 					      NodeNameExt,
 2378: 					      uint32_be(Number),
 2379: 					      uint8(Creation)])) of
 2380: 	Ref when is_reference(Ref) ->
 2381: 	    Ref;
 2382: 	{'EXIT', {badarg, _}} ->
 2383: 	    exit({badarg, mk_ref, [{NodeNameExt, Creation}, [Number]]});
 2384: 	Other ->
 2385: 	    exit({unexpected_binary_to_term_result, Other})
 2386:     end;
 2387: mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
 2388: 					   is_integer(Creation),
 2389: 					   is_list(Numbers) ->
 2390:     <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
 2391:     mk_ref({NodeNameExt, Creation}, Numbers);
 2392: mk_ref({NodeNameExt, Creation}, Numbers) when is_integer(Creation),
 2393: 					      is_list(Numbers) ->
 2394:     case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
 2395: 					      ?NEW_REFERENCE_EXT,
 2396: 					      uint16_be(length(Numbers)),
 2397: 					      NodeNameExt,
 2398: 					      uint8(Creation),
 2399: 					      lists:map(fun (N) ->
 2400: 								uint32_be(N)
 2401: 							end,
 2402: 							Numbers)])) of
 2403: 	Ref when is_reference(Ref) ->
 2404: 	    Ref;
 2405: 	{'EXIT', {badarg, _}} ->
 2406: 	    exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]});
 2407: 	Other ->
 2408: 	    exit({unexpected_binary_to_term_result, Other})
 2409:     end.
 2410: 
 2411: 
 2412: uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
 2413:     [(Uint bsr 24) band 16#ff,
 2414:      (Uint bsr 16) band 16#ff,
 2415:      (Uint bsr 8) band 16#ff,
 2416:      Uint band 16#ff];
 2417: uint32_be(Uint) ->
 2418:     exit({badarg, uint32_be, [Uint]}).
 2419: 
 2420: 
 2421: uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 ->
 2422:     [(Uint bsr 8) band 16#ff,
 2423:      Uint band 16#ff];
 2424: uint16_be(Uint) ->
 2425:     exit({badarg, uint16_be, [Uint]}).
 2426: 
 2427: uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
 2428:     Uint band 16#ff;
 2429: uint8(Uint) ->
 2430:     exit({badarg, uint8, [Uint]}).