aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/erl_link_SUITE.erl
blob: d7257ff7aa61e42916b226d3dbe1141c2039e3dd (plain) (tree)
1
2
3
4
5


                   
                                                        
   










                                                                           














                                                                          
                                           
 
                                                             














                                  
                                                   




























                                                                    


                                 
 
         



                                                            
 








                                                                          
 



                         
                                                                         
 
 
                         






                                                        
                          



























































                                                                           
                         













































                                                                
                                                         





                          
                                    









                                                  
                                                            
                                              
                                                           





                                                      
                                     










































































                                                                                

                                                               








































                                                                                

                                                 












































                                                                  


                                             




















                                                       
                                                              






                                       


                                                




















                                                                           
                                                              




















                                                                     
                                                           






























































































                                                                                
                                                                             






















































































                                                                            













































































































































































































































































































































                                                                                          



                                                         
                                                                                   
                                          
                                                                                                   





                                                     
                                                                                      






                                                        
                                      





                                   















































































                                                                          
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%

%%%----------------------------------------------------------------------
%%% File    : erl_link_SUITE.erl
%%% Author  : Rickard Green <[email protected]>
%%% Purpose : Test erlang links
%%% Created : 13 Dec 2001 by Rickard Green <[email protected]>
%%%----------------------------------------------------------------------

-module(erl_link_SUITE).
-author('[email protected]').

%-define(line_trace, 1).
-include_lib("common_test/include/ct.hrl").

-export([all/0, suite/0, init_per_suite/1, end_per_suite/1]).

% Test cases
-export([links/1,
	 dist_links/1,
	 monitor_nodes/1,
	 process_monitors/1,
	 dist_process_monitors/1,
	 busy_dist_port_monitor/1,
	 busy_dist_port_link/1,
	 otp_5772_link/1,
	 otp_5772_dist_link/1,
	 otp_5772_monitor/1,
	 otp_5772_dist_monitor/1,
	 otp_7946/1]).

-export([init_per_testcase/2, end_per_testcase/2]).

% Internal exports
-export([test_proc/0]).


-define(LINK_UNDEF, 0).
-define(LINK_PID,   1).
-define(LINK_NODE,  3).


% These are to be kept in sync with erl_monitors.h 
-define(MON_ORIGIN, 1).
-define(MON_TARGET, 3).


-record(erl_link, {type = ?LINK_UNDEF,
		   pid = [],
		   targets = []}).

% This is to be kept in sync with erl_bif_info.c (make_monitor_list)

-record(erl_monitor, {
            type, % MON_ORIGIN or MON_TARGET (1 or 3)
	    ref,
	    pid, % Process or nodename
	    name = [] % registered name or []
          }).


suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap, {minutes, 1}}].

all() -> 
    [links, dist_links, monitor_nodes, process_monitors,
     dist_process_monitors, busy_dist_port_monitor,
     busy_dist_port_link, otp_5772_link, otp_5772_dist_link,
     otp_5772_monitor, otp_5772_dist_monitor, otp_7946].

init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
    case catch erts_debug:get_internal_state(available_internal_state) of
	true -> ok;
	_ -> erts_debug:set_internal_state(available_internal_state, true)
    end,
    Config.

end_per_testcase(_Func, _Config) ->
    ok.

init_per_suite(Config) ->
    Config.

end_per_suite(_Config) ->
    catch erts_debug:set_internal_state(available_internal_state, false).


%% Tests node local links
links(Config) when is_list(Config) ->
    ?line common_link_test(node(), node()),
    ?line true = link(self()),
    ?line [] = find_erl_link(self(), ?LINK_PID, self()),
    ?line true = unlink(self()),
    ?line ok.

%% Tests distributed links
dist_links(Config) when is_list(Config) ->
    ?line [NodeName] = get_names(1, dist_link),
    ?line {ok, Node} = start_node(NodeName),
    ?line common_link_test(node(), Node),
    ?line TP4 = spawn(?MODULE, test_proc, []),
    ?line TP5 = spawn(?MODULE, test_proc, []),
    ?line TP6 = spawn(Node, ?MODULE, test_proc, []),
    ?line true = tp_call(TP6, fun() -> link(TP4) end),
    ?line check_link(TP4, TP6),
    ?line true = tp_call(TP5,
			 fun() ->
				 process_flag(trap_exit,true),
				 link(TP6)
			 end),
    ?line check_link(TP5, TP6),
    ?line rpc:cast(Node, erlang, halt, []),
    ?line wait_until(fun () -> ?line is_proc_dead(TP4) end),
    ?line check_unlink(TP4, TP6),
    ?line true = tp_call(TP5,
			 fun() ->
				 receive
				     {'EXIT', TP6, noconnection} ->
					 true
				 end
			 end),
    ?line check_unlink(TP5, TP6),
    ?line tp_cast(TP5, fun() -> exit(normal) end),
    ?line ok.

common_link_test(NodeA, NodeB) ->
    ?line TP1 = spawn(NodeA, ?MODULE, test_proc, []),
    ?line check_unlink(TP1, self()),
    ?line TP2 = tp_call(TP1,
			fun () ->
				spawn_link(NodeB, ?MODULE, test_proc, [])
			end),
    ?line check_link(TP1, TP2),
    ?line true = tp_call(TP2, fun() -> unlink(TP1) end),
    ?line check_unlink(TP1, TP2),
    ?line true = tp_call(TP2, fun() -> link(TP1) end),
    ?line check_link(TP1, TP2),
    ?line false = tp_call(TP2, fun() -> process_flag(trap_exit, true) end),
    ?line tp_cast(TP1, fun () -> exit(died) end),
    ?line true = tp_call(TP2, fun() ->
				      receive 
					  {'EXIT', TP1, died} ->
					      true
				      end
			      end),
    ?line check_unlink(TP1, TP2),
    ?line TP3 = tp_call(TP2,
			fun () ->
				spawn_link(NodeA, ?MODULE, test_proc, [])
			end),
    ?line check_link(TP3, TP2),
    ?line tp_cast(TP2, fun() -> exit(died) end),
    ?line wait_until(fun () -> ?line is_proc_dead(TP3) end),
    ?line check_unlink(TP3, TP2),
    ?line ok.

%% Tests monitor of nodes
monitor_nodes(Config) when is_list(Config) ->
    ?line [An, Bn, Cn, Dn] = get_names(4, dist_link),
    ?line {ok, A} = start_node(An),
    ?line {ok, B} = start_node(Bn),
    ?line C = list_to_atom(lists:concat([Cn, "@", hostname()])),
    ?line D = list_to_atom(lists:concat([Dn, "@", hostname()])),
    ?line 0 = no_of_monitor_node(self(), A),
    ?line 0 = no_of_monitor_node(self(), B),
    ?line monitor_node(A, true),
    ?line monitor_node(B, true),
    ?line monitor_node(D, true),
    ?line monitor_node(D, true),

    %% Has been known to crash the emulator.
    ?line {memory,_} = process_info(self(), memory),

    ?line monitor_node(A, false),
    ?line monitor_node(B, true),
    ?line monitor_node(C, true),
    ?line monitor_node(C, false),
    ?line monitor_node(C, true),
    ?line monitor_node(B, true),
    ?line monitor_node(A, false),
    ?line monitor_node(B, true),
    ?line monitor_node(B, false),
    ?line monitor_node(A, true),
    ?line check_monitor_node(self(), A, 1),
    ?line check_monitor_node(self(), B, 3),
    ?line check_monitor_node(self(), C, 0),
    ?line check_monitor_node(self(), D, 0),
    ?line receive {nodedown, C} -> ok end,
    ?line receive {nodedown, C} -> ok end,
    ?line receive {nodedown, C} -> ok end,
    ?line receive {nodedown, D} -> ok end,
    ?line receive {nodedown, D} -> ok end,
    ?line stop_node(A),
    ?line receive {nodedown, A} -> ok end,
    ?line check_monitor_node(self(), A, 0),
    ?line check_monitor_node(self(), B, 3),
    ?line stop_node(B),
    ?line receive {nodedown, B} -> ok end,
    ?line receive {nodedown, B} -> ok end,
    ?line receive {nodedown, B} -> ok end,
    ?line check_monitor_node(self(), B, 0),
    ?line receive
	      {nodedown, X} ->
		  ?line ct:fail({unexpected_nodedown, X})
	  after 0 ->
		  ?line ok
	  end,
    ?line ok.
    

%% Tests node local process monitors
process_monitors(Config) when is_list(Config) ->
    ?line common_process_monitors(node(), node()),
    ?line Mon1 = erlang:monitor(process,self()),
    ?line [] = find_erl_monitor(self(), Mon1),
    ?line [Name] = get_names(1, process_monitors),
    ?line true = register(Name, self()),
    ?line Mon2 = erlang:monitor(process, Name),
    ?line [] = find_erl_monitor(self(), Mon2),
    ?line receive
	      {'DOWN', Mon1, _, _, _} = Msg ->
		  ?line ct:fail({unexpected_down_msg, Msg});
	      {'DOWN', Mon2, _, _, _} = Msg ->
		  ?line ct:fail({unexpected_down_msg, Msg})
	  after 500 ->
		  ?line true = erlang:demonitor(Mon1),
		  ?line true = erlang:demonitor(Mon2),
		  ?line ok
	  end.

%% Tests distributed process monitors
dist_process_monitors(Config) when is_list(Config) -> 
    ?line [Name] = get_names(1,dist_process_monitors),
    ?line {ok, Node} = start_node(Name),
    ?line common_process_monitors(node(), Node),
    ?line TP1 = spawn(Node, ?MODULE, test_proc, []),
    ?line R1 = erlang:monitor(process, TP1),
    ?line TP1O = get_down_object(TP1, self()),
    ?line check_process_monitor(self(), TP1, R1),
    ?line tp_cast(TP1, fun () -> halt() end),
    ?line receive
	      {'DOWN',R1,process,TP1O,noconnection} ->
		  ?line ok
	  end,
    ?line check_process_demonitor(self(), TP1, R1),
    ?line R2 = erlang:monitor(process, TP1),
    ?line receive
	      {'DOWN',R2,process,TP1O,noconnection} ->
		  ?line ok
	  end,
    ?line check_process_demonitor(self(), TP1, R2),
    ?line ok.


common_process_monitors(NodeA, NodeB) ->
    ?line TP1 = spawn(NodeA, ?MODULE, test_proc, []),
    ?line TP2 = spawn(NodeB, ?MODULE, test_proc, []),
    ?line run_common_process_monitors(TP1, TP2),
    ?line TP3 = spawn(NodeA, ?MODULE, test_proc, []),
    ?line TP4 = spawn(NodeB, ?MODULE, test_proc, []),
    ?line [TP4N] = get_names(1, common_process_monitors),
    ?line true = tp_call(TP4, fun () -> register(TP4N,self()) end),
    ?line run_common_process_monitors(TP3,
				      case node() == node(TP4) of
					  true -> TP4N;
					  false -> {TP4N, node(TP4)}
				      end),
    ?line ok.

run_common_process_monitors(TP1, TP2) ->
    ?line R1 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
    ?line check_process_monitor(TP1, TP2, R1),

    ?line tp_call(TP2, fun () -> catch erlang:demonitor(R1) end),
    ?line check_process_monitor(TP1, TP2, R1),

    ?line true = tp_call(TP1, fun () -> erlang:demonitor(R1) end),
    ?line check_process_demonitor(TP1, TP2, R1),

    ?line R2 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
    ?line TP2O = get_down_object(TP2, TP1),
    ?line check_process_monitor(TP1, TP2, R2),
    ?line tp_cast(TP2, fun () -> exit(bye) end),
    ?line wait_until(fun () -> ?line is_proc_dead(TP2) end),
    ?line ok = tp_call(TP1, fun () ->
				    ?line receive
					      {'DOWN',R2,process,TP2O,bye} ->
						  ?line ok
				    end
			    end),
    ?line check_process_demonitor(TP1, TP2, R2),
    
    ?line R3 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
    ?line ok = tp_call(TP1, fun () ->
				    ?line receive
					      {'DOWN',R3,process,TP2O,noproc} ->
						  ?line ok
				    end
			    end),
    ?line check_process_demonitor(TP1, TP2, R3),

    ?line tp_cast(TP1, fun () -> exit(normal) end),
    ?line wait_until(fun () -> ?line is_proc_dead(TP1) end),
    ?line ok.
    

%% Tests distributed monitor/2, demonitor/1, and 'DOWN' message
%% over busy distribution port
busy_dist_port_monitor(Config) when is_list(Config) ->

    ?line Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
		       "true" -> start_busy_dist_port_tracer();
		       _ -> false
		   end,

    ?line [An] = get_names(1, busy_dist_port_monitor),
    ?line {ok, A} = start_node(An),
    ?line TP1 = spawn(A, ?MODULE, test_proc, []),
    %% Check monitor over busy port
    ?line M1 = suspend_on_busy_test(A,
				    "erlang:monitor(process, TP1)",
				    fun () -> erlang:monitor(process, TP1) end),
    ?line check_process_monitor(self(), TP1, M1),
    %% Check demonitor over busy port
    ?line suspend_on_busy_test(A,
			       "erlang:demonitor(M1)",
			       fun () -> erlang:demonitor(M1) end),
    ?line check_process_demonitor(self(), TP1, M1),
    %% Check down message over busy port
    ?line TP2 = spawn(?MODULE, test_proc, []),
    ?line M2 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end),
    ?line check_process_monitor(TP1, TP2, M2),
    ?line Ref = make_ref(),
    ?line Busy = make_busy(A, 1000),
    ?line receive after 100 -> ok end,
    ?line tp_cast(TP2, fun () -> exit(Ref) end),
    ?line receive after 100 -> ok end,
    ?line unmake_busy(Busy),
    ?line Ref = tp_call(TP1, fun () ->
				     receive
					 {'DOWN', M2, process, TP2, Ref} ->
					     Ref
				     end
			     end),
    ?line tp_cast(TP1, fun () -> exit(normal) end),
    ?line stop_node(A),
    ?line stop_busy_dist_port_tracer(Tracer),
    ?line ok.

%% Tests distributed link/1, unlink/1, and 'EXIT'
%% message over busy distribution port
busy_dist_port_link(Config) when is_list(Config) ->
    ?line Tracer = case os:getenv("TRACE_BUSY_DIST_PORT") of
		       "true" -> start_busy_dist_port_tracer();
		       _ -> false
		   end,

    ?line [An] = get_names(1, busy_dist_port_link),
    ?line {ok, A} = start_node(An),
    ?line TP1 = spawn(A, ?MODULE, test_proc, []),
    %% Check link over busy port
    ?line suspend_on_busy_test(A,
			       "link(TP1)",
			       fun () -> link(TP1) end),
    ?line check_link(self(), TP1),
    %% Check unlink over busy port
    ?line suspend_on_busy_test(A,
			       "unlink(TP1)",
			       fun () -> unlink(TP1) end),
    ?line check_unlink(self(), TP1),
    %% Check trap exit message over busy port
    ?line TP2 = spawn(?MODULE, test_proc, []),
    ?line ok = tp_call(TP1, fun () ->
				    process_flag(trap_exit, true),
				    link(TP2),
				    ok
			    end),
    ?line check_link(TP1, TP2),
    ?line Ref = make_ref(),
    ?line Busy = make_busy(A, 1000),
    ?line receive after 100 -> ok end,
    ?line tp_cast(TP2, fun () -> exit(Ref) end),
    ?line receive after 100 -> ok end,
    ?line unmake_busy(Busy),
    ?line Ref = tp_call(TP1, fun () ->
				     receive
					 {'EXIT', TP2, Ref} ->
					     Ref
				     end
			     end),
    ?line tp_cast(TP1, fun () -> exit(normal) end),
    ?line stop_node(A),
    ?line stop_busy_dist_port_tracer(Tracer),
    ?line ok.


otp_5772_link(Config) when is_list(Config) ->
    ?line otp_5772_link_test(node()).

otp_5772_dist_link(Config) when is_list(Config) ->
    ?line [An] = get_names(1, otp_5772_dist_link),
    ?line {ok, A} = start_node(An),
    ?line otp_5772_link_test(A),
    ?line stop_node(A).

otp_5772_link_test(Node) ->
    ?line Prio = process_flag(priority, high),
    ?line TE = process_flag(trap_exit, true),
    ?line TP1 = spawn_opt(Node, ?MODULE, test_proc, [],
			  [link, {priority, low}]),
    exit(TP1, bang),
    unlink(TP1),
    ?line receive
	      {'EXIT', TP1, _} ->
		  ?line ok
	  after 0 ->
		  ?line ok
	  end,
    ?line receive
	      {'EXIT', TP1, _} = Exit ->
		  ?line ct:fail({got_late_exit_message, Exit})
	  after 1000 ->
		  ?line ok
	  end,
    ?line process_flag(trap_exit, TE),
    ?line process_flag(priority, Prio),
    ?line ok.

otp_5772_monitor(Config) when is_list(Config) ->
    ?line otp_5772_monitor_test(node()).

otp_5772_dist_monitor(Config) when is_list(Config) ->
    ?line [An] = get_names(1, otp_5772_dist_monitor),
    ?line {ok, A} = start_node(An),
    ?line otp_5772_monitor_test(A),
    ?line stop_node(A),
    ?line ok.

otp_5772_monitor_test(Node) ->
    ?line Prio = process_flag(priority, high),
    ?line TP1 = spawn_opt(Node, ?MODULE, test_proc, [], [{priority, low}]),
    ?line M1 = erlang:monitor(process, TP1),
    ?line exit(TP1, bang),
    ?line erlang:demonitor(M1),
    ?line receive
	      {'DOWN', M1, _, _, _} ->
		  ?line ok
	  after 0 ->
		  ?line ok
	  end,
    ?line receive
	      {'DOWN', M1, _, _, _} = Down ->
		  ?line ct:fail({got_late_down_message, Down})
	  after 1000 ->
		  ?line ok
	  end,
    ?line process_flag(priority, Prio),
    ?line ok.

otp_7946(Config) when is_list(Config) ->
    ?line [NodeName] = get_names(1, otp_7946),
    ?line {ok, Node} = start_node(NodeName),
    ?line Proc = rpc:call(Node, erlang, whereis, [net_kernel]),
    ?line Mon = erlang:monitor(process, Proc),
    ?line rpc:cast(Node, erlang, halt, []),
    ?line receive {'DOWN', Mon, process, Proc , _} -> ok end,
    ?line {Linker, LMon} = spawn_monitor(fun () ->
						 link(Proc),
						 receive
						 after infinity -> ok
						 end
					 end),
    ?line receive
	      {'DOWN', LMon, process, Linker, Reason} ->
		  ?line io:format("Reason=~p~n", [Reason]),
		  ?line Reason = noconnection
	  end.

%%
%% -- Internal utils --------------------------------------------------------
%%

-define(BUSY_DATA_KEY, '__busy__port__data__').
-define(BUSY_DATA_SIZE, 1024*1024).

busy_data() ->
    case get(?BUSY_DATA_KEY) of
	undefined ->
	    set_busy_data([]);
	Data ->
	    true = is_binary(Data),
	    true = size(Data) == ?BUSY_DATA_SIZE,
	    Data
    end.

set_busy_data(SetData) ->
    case get(?BUSY_DATA_KEY) of
	undefined ->
	    Data = case SetData of
		       D when is_binary(D), size(D) == ?BUSY_DATA_SIZE ->
			   SetData;
		       _ ->
			   list_to_binary(lists:duplicate(?BUSY_DATA_SIZE, 253))
		   end,
	    put(?BUSY_DATA_KEY, Data),
	    Data;
	OldData ->
	    OldData
    end.

freeze_node(Node, MS) ->
    Own = 500,
    DoingIt = make_ref(),
    Freezer = self(),
    spawn_link(Node,
	       fun () ->
		       erts_debug:set_internal_state(available_internal_state,
						     true),
		       dport_send(Freezer, DoingIt),
		       receive after Own -> ok end,
		       erts_debug:set_internal_state(block, MS+Own)
	       end),
    receive DoingIt -> ok end,
    receive after Own -> ok end.

make_busy(Node, Time) when is_integer(Time) ->
    Own = 500,
    freeze_node(Node, Time+Own), 
    Data = busy_data(),
    %% first make port busy
    Pid = spawn_link(fun () ->
			     forever(fun () ->
					     dport_reg_send(Node,
							    '__noone__',
							    Data)
				     end)
		     end),
    receive after Own -> ok end,
    wait_until(fun () ->
		       case process_info(Pid, status) of
			   {status, suspended} -> true;
			   _ -> false
		       end
	       end),
    %% then dist entry
    make_busy(Node, [nosuspend], Data),
    Pid.

make_busy(Node, Opts, Data) ->
    case erlang:send({'__noone__', Node}, Data, Opts) of
	nosuspend -> nosuspend;
	_ -> make_busy(Node, Opts, Data)
    end.

unmake_busy(Pid) ->
    unlink(Pid),
    exit(Pid, bang).

suspend_on_busy_test(Node, Doing, Fun) ->
    Tester = self(),
    DoIt = make_ref(),
    Done = make_ref(),
    Data = busy_data(),
    spawn_link(fun () ->
		       set_busy_data(Data),
		       Busy = make_busy(Node, 1000),
		       Tester ! DoIt,
		       receive after 100 -> ok end,
		       Info = process_info(Tester, [status, current_function]),
		       unmake_busy(Busy),
		       io:format("~p doing ~s: ~p~n", [Tester, Doing, Info]),
		       Tester ! {Done, Info}
	       end),
    receive DoIt -> ok end,
    Res = Fun(),
    receive
	{Done, MyInfo} ->
	    %% Don't match arity; it is different in
	    %% debug and optimized emulator
	    [{status, suspended},
	     {current_function, {erlang, bif_return_trap, _}}] = MyInfo,
	    ok
    end,
    Res.

% get_node(Name) when is_atom(Name) ->
%     ?line node();
% get_node({Name, Node}) when is_atom(Name) ->
%     ?line Node;
% get_node(NC) when is_pid(NC); is_port(NC); is_reference(NC) ->
%     ?line node(NC).

get_down_object(Item, _) when is_pid(Item) ->
    Item;
get_down_object({Name, Node} = Item, _) when is_atom(Name); is_atom(Node) ->
    Item;
get_down_object(Item, Watcher) when is_atom(Item), is_pid(Watcher) ->
    {Item, node(Watcher)};
get_down_object(Item, {_,Node}) when is_atom(Item), is_atom(Node) ->
    {Item, Node};
get_down_object(Item, Watcher) when is_atom(Item), is_atom(Watcher) ->
    {Item, node()}.

is_proc_dead(P) ->
    case is_proc_alive(P) of
	true -> false;
	false -> true
    end.

is_proc_alive(Pid) when is_pid(Pid), node(Pid) == node() ->
    ?line is_process_alive(Pid);
is_proc_alive(Name) when is_atom(Name) ->
    ?line case catch whereis(Name) of
	      Pid when is_pid(Pid) ->
		  ?line is_proc_alive(Pid);
	      _ ->
		  ?line false
	  end;
is_proc_alive({Name, Node}) when is_atom(Name), Node == node() ->
    ?line is_proc_alive(Name);
is_proc_alive(Proc) ->
    ?line is_remote_proc_alive(Proc).

is_remote_proc_alive({Name, Node}) when is_atom(Name), is_atom(Node) ->
    ?line is_remote_proc_alive(Name, Node);
is_remote_proc_alive(Pid) when is_pid(Pid) ->
    ?line is_remote_proc_alive(Pid, node(Pid));
is_remote_proc_alive(_) ->
    ?line false.

is_remote_proc_alive(PN, Node) ->		 
    ?line S = self(),
    ?line R = make_ref(),
    ?line monitor_node(Node, true),
    ?line _P = spawn(Node, fun () -> S ! {R, is_proc_alive(PN)} end),
    ?line receive
	      {R, Bool} ->
		  ?line monitor_node(Node, false),
		  ?line Bool;
	      {nodedown, Node} ->
		  ?line false
	  end.

wait_until(Fun) ->
    ?line case Fun() of
	      true ->
		  ?line ok;
	      _ ->
		  ?line receive
			after 100 ->
				?line wait_until(Fun)
			end
	  end.

forever(Fun) ->
    Fun(),
    forever(Fun).

tp_call(Tp, Fun) ->
    ?line R = make_ref(),
    ?line Tp ! {call, self(), R, Fun},
    ?line receive
	      {R, Res} ->
		  ?line Res
	  end.

tp_cast(Tp, Fun) ->
    ?line Tp ! {cast, Fun}.

test_proc() ->
    ?line receive
	      {call, From, Ref, Fun} ->
		  ?line From ! {Ref, Fun()};
	      {cast, Fun} ->
		  ?line Fun()
	  end,
    ?line test_proc().

expand_link_list([#erl_link{type = ?LINK_NODE, targets = N} = Rec | T]) ->
    lists:duplicate(N,Rec#erl_link{targets = []}) ++ expand_link_list(T);
expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}]} = Rec | T]) ->
    [Rec#erl_link{targets = [Pid]} | expand_link_list(T)];
expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}|TT]} = Rec | T]) ->
    [ Rec#erl_link{targets = [Pid]} | expand_link_list( 
				       [Rec#erl_link{targets = TT} | T])]; 
expand_link_list([#erl_link{targets = []} = Rec | T]) ->
    [Rec | expand_link_list(T)];
expand_link_list([]) ->
    [].

get_local_link_list(Obj) ->
    case catch erts_debug:get_internal_state({link_list, Obj}) of
	LL when is_list(LL) ->
	    expand_link_list(LL);
	_ ->
	    []
    end.

get_remote_link_list(Node, Obj) ->
    case catch rpc:call(Node, erts_debug, get_internal_state,
			[{link_list, Obj}]) of
	LL when is_list(LL) ->
	    expand_link_list(LL);
	_ ->
	    []
    end.


get_link_list({Node, DistEntry}) when Node == node(), is_atom(DistEntry) ->
    get_local_link_list(DistEntry);
get_link_list({Node, DistEntry}) when is_atom(Node), is_atom(DistEntry) ->
    get_remote_link_list(Node, DistEntry);
get_link_list(P) when is_pid(P); is_port(P) ->
    case node(P) of
	      Node when Node == node() ->
		  get_local_link_list(P);
	      Node ->
		  get_remote_link_list(Node, P)
	  end;
get_link_list(undefined) ->
    [].

get_local_monitor_list(Obj) ->
    case catch erts_debug:get_internal_state({monitor_list, Obj}) of
	      LL when is_list(LL) ->
		  LL;
	      _ ->
		  []
	  end.

get_remote_monitor_list(Node, Obj) ->
    case catch rpc:call(Node, erts_debug, get_internal_state,
			[{monitor_list, Obj}]) of
	      LL when is_list(LL) ->
		  LL;
	      _ ->
		  []
	  end.


get_monitor_list({Node, DistEntry}) when Node == node(), is_atom(DistEntry) ->
    get_local_monitor_list(DistEntry);
get_monitor_list({Node, DistEntry}) when is_atom(Node), is_atom(DistEntry) ->
    get_remote_monitor_list(Node, DistEntry);
get_monitor_list(P) when is_pid(P) ->
    case node(P) of
	      Node when Node == node() ->
		  get_local_monitor_list(P);
	      Node ->
		  get_remote_monitor_list(Node, P)
	  end;
get_monitor_list(undefined) ->
    [].


find_erl_monitor(Pid, Ref) when is_reference(Ref) ->
    lists:foldl(fun (#erl_monitor{ref = R} = EL, Acc) when R == Ref ->
			      [EL|Acc];
			  (_, Acc) ->
			      Acc
		      end,
		      [],
		      get_monitor_list(Pid)).

% find_erl_link(Obj, Ref) when is_reference(Ref) -> 
%     ?line lists:foldl(fun (#erl_link{ref = R} = EL, Acc) when R == Ref ->
% 			      ?line [EL|Acc];
% 			  (_, Acc) ->
% 			      ?line Acc
% 		      end,
% 		      [],
% 		      get_link_list(Obj)).

find_erl_link(Obj, Type, [Item, Data]) when is_pid(Item);
					    is_port(Item);
					    is_atom(Item) -> 
    lists:foldl(fun (#erl_link{type = T, pid = I, targets = D} = EL,
			   Acc) when T == Type, I == Item ->
			      case Data of
					D ->
					    [EL|Acc];
					[] ->
					    [EL|Acc];
					_ ->
					    Acc
				    end;
			  (_, Acc) ->
			      Acc
		      end,
		      [],
		      get_link_list(Obj));
find_erl_link(Obj, Type, Item) when is_pid(Item); is_port(Item); is_atom(Item) ->
    find_erl_link(Obj, Type, [Item, []]). 

	

check_link(A, B) ->
    ?line [#erl_link{type = ?LINK_PID,
		      pid = B,
		      targets = []}] = find_erl_link(A, ?LINK_PID, B),
    ?line [#erl_link{type = ?LINK_PID,
		      pid = A,
		      targets = []}] = find_erl_link(B, ?LINK_PID, A),
    ?line case node(A) == node(B) of
	      false ->
		  ?line [#erl_link{type = ?LINK_PID,
				   pid = A,
				   targets = [B]}] = find_erl_link({node(A),
							       node(B)},
							      ?LINK_PID,
							      [A, [B]]),
		  ?line [#erl_link{type = ?LINK_PID,
				   pid = B,
				   targets = [A]}] = find_erl_link({node(B),
								    node(A)},
								   ?LINK_PID,
								   [B, [A]]);
	      true ->
		  ?line [] = find_erl_link({node(A), node(B)},
					   ?LINK_PID,
					   [A, [B]]),
		  ?line [] = find_erl_link({node(B), node(A)},
					   ?LINK_PID,
					   [B, [A]])
	  end,
    ?line ok.

check_unlink(A, B) ->
    ?line [] = find_erl_link(A, ?LINK_PID, B),
    ?line [] = find_erl_link(B, ?LINK_PID, A),
    ?line [] = find_erl_link({node(A), node(B)}, ?LINK_PID, [A, [B]]),
    ?line [] = find_erl_link({node(B), node(A)}, ?LINK_PID, [B, [A]]),
    ?line ok.

check_process_monitor(From, {Name, Node}, Ref) when is_pid(From),
						    is_atom(Name),
						    Node == node(From),
						    is_reference(Ref) ->
    ?line check_process_monitor(From, Name, Ref);
check_process_monitor(From, {Name, Node}, Ref) when is_pid(From),
						    is_atom(Name),
						    is_atom(Node),
						    is_reference(Ref) ->
    ?line MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
    ?line [#erl_monitor{type = ?MON_ORIGIN,
			ref = Ref,
			pid = Node,
			name = Name}] = find_erl_monitor(From, Ref),
    ?line [#erl_monitor{type = ?MON_TARGET,
			ref = Ref,
			pid = From,
			name = Name}] = 	find_erl_monitor({node(From), Node}, Ref),
    ?line [#erl_monitor{type = ?MON_ORIGIN,
			ref = Ref,
			pid = MonitoredPid,
			name = Name}] = find_erl_monitor({Node, node(From)}, Ref),
    ?line [#erl_monitor{type = ?MON_TARGET,
			ref = Ref,
			pid = From,
			name = Name}] = find_erl_monitor(MonitoredPid, Ref),
    ?line ok;
check_process_monitor(From, Name, Ref) when is_pid(From),
					    is_atom(Name),
					    undefined /= Name,
					    is_reference(Ref) ->
    ?line MonitoredPid = rpc:call(node(From), erlang, whereis, [Name]),
    
    ?line [#erl_monitor{type = ?MON_ORIGIN,
			ref = Ref,
			pid = MonitoredPid,
			name = Name}] = find_erl_monitor(From, Ref),


    ?line [#erl_monitor{type = ?MON_TARGET,
			ref = Ref,
			pid = From,
			name = Name}] = find_erl_monitor(MonitoredPid,Ref),
    ok;
check_process_monitor(From, To, Ref) when is_pid(From),
					  is_pid(To),
					  is_reference(Ref) ->
    ?line OriMon = [#erl_monitor{type = ?MON_ORIGIN,
				 ref = Ref,
				 pid = To}],

    ?line OriMon = find_erl_monitor(From, Ref), 

    ?line TargMon = [#erl_monitor{type = ?MON_TARGET,
				 ref = Ref,
				 pid = From}],
    ?line TargMon = find_erl_monitor(To, Ref),
			

    ?line case node(From) == node(To) of
	      false ->
		  ?line TargMon = find_erl_monitor({node(From), node(To)}, Ref),
		  ?line OriMon = find_erl_monitor({node(To), node(From)}, Ref);
	      true ->
		  ?line [] = find_erl_monitor({node(From), node(From)}, Ref)
	  end,
    ?line ok.


check_process_demonitor(From, {undefined, Node}, Ref) when is_pid(From),
							   is_reference(Ref) ->
    ?line [] = find_erl_monitor(From, Ref),
    ?line case node(From) == Node of
	      false ->
		  ?line [] = find_erl_monitor({node(From), Node}, Ref),
		  ?line [] = find_erl_monitor({Node, node(From)}, Ref);
	      true ->
		  ?line [] = find_erl_monitor({Node, Node}, Ref)
	  end,
    ?line ok;
check_process_demonitor(From, {Name, Node}, Ref) when is_pid(From),
						      is_atom(Name),
						      Node == node(From),
						      is_reference(Ref) ->
    ?line MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
    ?line case rpc:call(Node, erlang, whereis, [Name]) of
	      undefined ->
		  ?line check_process_demonitor(From, {undefined, Node}, Ref);
	      MonitoredPid ->
		  ?line check_process_demonitor(From, MonitoredPid, Ref)
	  end;
check_process_demonitor(From, {Name, Node}, Ref) when is_pid(From),
						      is_atom(Name),
						      is_atom(Node),
						      is_reference(Ref) ->
    ?line MonitoredPid = rpc:call(Node, erlang, whereis, [Name]),
    ?line [] = find_erl_monitor(From, Ref),
    ?line [] = find_erl_monitor({node(From), Node}, Ref),
    ?line [] = find_erl_monitor({Node, node(From)}, Ref),
    ?line [] = find_erl_monitor(MonitoredPid, Ref),
    ?line ok;
check_process_demonitor(From, undefined, Ref) when is_pid(From),
						   is_reference(Ref) ->
    ?line [] = find_erl_monitor(From, Ref),
    ?line case node(From) == node() of
	      false ->
		  ?line [] = find_erl_monitor({node(From), node()}, Ref),
		  ?line [] = find_erl_monitor({node(), node(From)}, Ref);
	      true ->
		  ?line [] = find_erl_monitor({node(), node()}, Ref)
	  end,
    ?line ok;
check_process_demonitor(From, Name, Ref) when is_pid(From),
					      is_atom(Name),
					      undefined /= Name,
					      is_reference(Ref) ->
    ?line check_process_demonitor(From, {Name, node()}, Ref);
check_process_demonitor(From, To, Ref) when is_pid(From),
					    is_pid(To),
					    is_reference(Ref) ->
    ?line [] = find_erl_monitor(From, Ref),
    ?line [] = find_erl_monitor(To, Ref),
    ?line case node(From) == node(To) of
	      false ->
		  ?line [] = find_erl_monitor({node(From), node(To)}, Ref),
		  ?line [] = find_erl_monitor({node(To), node(From)}, Ref);
	      true ->
		  ?line [] = find_erl_monitor({node(From), node(From)}, Ref)
	  end,
    ?line ok.

no_of_monitor_node(From, Node) when is_pid(From), is_atom(Node) ->
    ?line length(find_erl_link(From, ?LINK_NODE, Node)).

check_monitor_node(From, Node, No) when is_pid(From),
					is_atom(Node),
					is_integer(No),
					No >= 0 ->
    ?line LL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = Node}),
    ?line DLL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = From}),
    ?line LL = find_erl_link(From, ?LINK_NODE, Node),
    ?line DLL = find_erl_link({node(From), Node}, ?LINK_NODE, From),
    ?line ok.



hostname() ->
    ?line from($@, atom_to_list(node())).

from(H, [H | T]) -> T;
from(H, [_ | T]) -> from(H, T);
from(_H, []) -> [].

get_names(N, T) when is_atom(T) ->
    get_names(N, T, []).
get_names(0, _, Acc) ->
    Acc;
get_names(N, T, Acc) ->
    get_names(N-1, T, [list_to_atom(atom_to_list(?MODULE)
				    ++ "-"
				    ++ atom_to_list(T)
				    ++ "-"
				    ++ integer_to_list(erlang:system_time(seconds))
				    ++ "-"
				    ++ integer_to_list(erlang:unique_integer([positive]))) | Acc]).

start_node(Name) ->
    ?line start_node(Name, "").

start_node(Name, Args) ->
    ?line Pa = filename:dirname(code:which(?MODULE)),
    ?line Res = test_server:start_node(Name, slave, [{args,  Args ++ " -pa " ++ Pa}]),
    ?line {ok, Node} = Res,
    ?line rpc:call(Node, erts_debug, set_internal_state,
		   [available_internal_state, true]),
    ?line Res.
    

stop_node(Node) ->
    ?line test_server:stop_node(Node).

-define(COOKIE, '').
-define(DOP_LINK,		1).
-define(DOP_SEND,		2).
-define(DOP_EXIT,		3).
-define(DOP_UNLINK,		4).
-define(DOP_REG_SEND,		6).
-define(DOP_GROUP_LEADER,	7).
-define(DOP_EXIT2,		8).

-define(DOP_SEND_TT,		12).
-define(DOP_EXIT_TT,		13).
-define(DOP_REG_SEND_TT,	16).
-define(DOP_EXIT2_TT,		18).

-define(DOP_MONITOR_P,		19).
-define(DOP_DEMONITOR_P,	20).
-define(DOP_MONITOR_P_EXIT,	21).

dport_send(To, Msg) ->
    Node = node(To),
    DPrt = case dport(Node) of
	       undefined ->
		   pong = net_adm:ping(Node),
		   dport(Node);
	       Prt ->
		   Prt
	   end,
    port_command(DPrt, [dmsg_hdr(),
			dmsg_ext({?DOP_SEND,
				  ?COOKIE,
				  To}),
			dmsg_ext(Msg)]).

dport_reg_send(Node, Name, Msg) ->
    DPrt = case dport(Node) of
	       undefined ->
		   pong = net_adm:ping(Node),
		   dport(Node);
	       Prt ->
		   Prt
	   end,
    port_command(DPrt, [dmsg_hdr(),
			dmsg_ext({?DOP_REG_SEND,
				  self(),
				  ?COOKIE,
				  Name}),
			dmsg_ext(Msg)]).

dport(Node) when is_atom(Node) ->
    case catch erts_debug:get_internal_state(available_internal_state) of
	true -> true;
	_ -> erts_debug:set_internal_state(available_internal_state, true)
    end,
    erts_debug:get_internal_state({dist_port, Node}).

dmsg_hdr() ->
    [131, % Version Magic
     $D,  % Dist header
     0].  % No atom cache referenses

dmsg_ext(Term) ->	
    <<131, Res/binary>> = term_to_binary(Term),
    Res.

start_busy_dist_port_tracer() ->
    Tracer = spawn_link(fun () -> busy_dist_port_tracer() end),
    erlang:system_monitor(Tracer, [busy_dist_port]),
    Tracer.

stop_busy_dist_port_tracer(Tracer) when is_pid(Tracer) ->
    unlink(Tracer),
    exit(Tracer, bye);
stop_busy_dist_port_tracer(_) ->
    true.

busy_dist_port_tracer() ->
    receive
	{monitor, _SuspendedProcess, busy_dist_port, _Port} = M ->
	    erlang:display(M),
	    busy_dist_port_tracer()
    end.