aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/trace_SUITE.erl
blob: d9974bd6f7e8085a24f37f886c8f4b2924111344 (plain) (tree)
1
2
3
4
5
6
7
8
9

                   
  
                                                        
  


                                                                   
  






                                                                           
  








                        

                                                                       








                                                                       
                                        

                                         
                                           



                     


                                 
 
         

                                                                 






                                                           
                                  
                                                             
 



                                                                   
                                                                  






                                                              
                          


                                                                           




                                                 

                                         

                                                                       


                                                  
                                                                

                                   
                                                                 
                      

                                                            

                                                                                 

                                                           



                                                   

       









































































































                                                                                        

                                                   
                                         



















                                                                  



                                             


                                                 

                                                                        
                      




                                            


                                         

                                                              

                                                  
                                                                         
                      
 


                                                                         
                                                                        


                        
                                                           


                                              
                                                                   
                      
 



                                                           
                                                                                        






                                                                          
                                                                                    

                      
                                                          

                                                                          


                                                                       




                                                    



                                           


                                                               
      



                                                 
      

                                           

                  

                                                   
                                                              
                                        
                                                        
                      

                                 



                                                         
                                                                    

                                      

           
                                 
                                                        
                      

             
                                   
                                                          
                      

                     
                                 
                                                                  
                      

                       
                                   
                                                                    
                      

               
                                 
                                                           
                      

                 
                            
                                                             
                      

                                               


                                                   
                                                              
                                        
                                                        
                                           
                                                           

                                               

                                                             
                      

                                           


                                           
                                                          
                      



                                                
                               



                                                           
      



                                                       
      

                                           

                                 



                                                      
                                                                  


                                                         
                                                                    

                                      

           
                                 
                                                        
                      

             
                                   
                                                          
                      

                     
                                 
                                                                  
                      

                       
                                   
                                                                    
                      

                                               


                                   
                                                        
                                           
                                                           

                                               

                                                             
                      

            
                                







                                            
                                        


                                                             


                                                             


                                                                


                                                     


                                                   

                                                      




                                                  
                               
                                        


                                                                   


                                                               


                                                                  




                                                         


       
                                                 
                                                   
                  
      



































                                                                      
      

















                                                                     


       
                                                 
                                                        




                       






                       
                                     
 
                                                                                     
 

                                                                                        
 

                                                                
 

                      










                                                                         








                                                                  
                                                           
                                                            
                                                 

                                               



                                                                       






                                                  

                                   
                 
                                      



                                          

                                     
                 
                                       





                                     

                            
                                                     

                                                                  
        












                                                                 
         

                                                                        

        
                                                     


                                                                  






















                                                                 
         

                                                                      


                                 
















                                                                


                                   
































                                                                                   
              
                    

        
                                                        
                                                           







                                           
 
                                                        
                                                           












                                                       

                                    
                               
      

















                                                                   



                                      



























                                                      
              
                    













                                       


                                                    
                 
                   












                                                                 
           




                                                            

        
                                             
                                       
                             
                                     
                                                       
                                   


                      
       

                                                       

                                                    

                                                             








                                           


 
                                              
                       
                                        

































                                                                  
                             
       

                                                       

                                                    
                             

                                                
 
                                            
                              
                                       

                          

                     
       
                     
                            































                                                               










                                                                            
                                              
                              







































































































                                                                    

                                                    
                 
                                         






                                                                           


                                
                                        


                                      
















                                                         

                                    


                                                   
                                    









                                              

                              


                                     
 
                                            
                              





















































                                                                           
                                         
























































                                                               





                                                              
 
                                                











                                                                           
        
 






                                         
 


                                              
                                                
                                               



                                                        


       
                                                                      
                                         





                                                          
           
                                 
        

                                                  



       
                                                                
                                              










                                                                   

                                                                      

       
                                         

                                                             



                                                                     
 
                                
                                               
                              



















                                                           





                                                  










                                                                 















                                                                  
                  

        






                                                                             



                                                         

                                              
                
                

        
 



                                          






























                                                        
                 
                           






                            










                                             






                                  


                                  






                                                 
                          



















                                                                   
                                       
                                                                              






                                 




                                                          









                                                            









                                                              



                                                
















                                                                                   
                                                                                   



                                                       
                                 


                                             



                                                              
        
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2016. 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%
%%

-module(trace_SUITE).

%%%
%%% Tests the trace BIF.
%%%

-export([all/0, suite/0, link_receive_call_correlation/0,
         receive_trace/1, link_receive_call_correlation/1, self_send/1,
	 timeout_trace/1, send_trace/1,
	 procs_trace/1, dist_procs_trace/1,
	 suspend/1, mutual_suspend/1, suspend_exit/1, suspender_exit/1,
	 suspend_system_limit/1, suspend_opts/1, suspend_waiting/1,
	 new_clear/1, existing_clear/1,
	 set_on_spawn/1, set_on_first_spawn/1, cpu_timestamp/1,
	 system_monitor_args/1, more_system_monitor_args/1,
	 system_monitor_long_gc_1/1, system_monitor_long_gc_2/1, 
	 system_monitor_large_heap_1/1, system_monitor_large_heap_2/1,
	 system_monitor_long_schedule/1,
	 bad_flag/1, trace_delivered/1]).

-include_lib("common_test/include/ct.hrl").

%%% Internal exports
-export([process/1]).

suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap, {seconds, 5}}].

all() -> 
    [cpu_timestamp, receive_trace, link_receive_call_correlation,
     self_send, timeout_trace,
     send_trace, procs_trace, dist_procs_trace, suspend,
     mutual_suspend, suspend_exit, suspender_exit,
     suspend_system_limit, suspend_opts, suspend_waiting,
     new_clear, existing_clear, set_on_spawn,
     set_on_first_spawn, system_monitor_args,
     more_system_monitor_args, system_monitor_long_gc_1,
     system_monitor_long_gc_2, system_monitor_large_heap_1,
     system_monitor_long_schedule,
     system_monitor_large_heap_2, bad_flag, trace_delivered].


%% No longer testing anything, just reporting whether cpu_timestamp
%% is enabled or not.
cpu_timestamp(Config) when is_list(Config) ->
    %% Test whether cpu_timestamp is implemented on this platform.
    Works = try erlang:trace(all, true, [cpu_timestamp]) of
                _ ->
                    erlang:trace(all, false, [cpu_timestamp]),
                    true
            catch
                error:badarg -> false
            end,
    {comment,case Works of
                 false -> "cpu_timestamp is NOT implemented/does not work";
                 true -> "cpu_timestamp works"
             end}.


%% Tests that trace(Pid, How, ['receive']) works.

receive_trace(Config) when is_list(Config) ->
    Receiver = fun_spawn(fun receiver/0),
    process_flag(trap_exit, true),

    %% Trace the process; make sure that we receive the trace messages.
    1 = erlang:trace(Receiver, true, ['receive']),
    Hello = {hello, world},
    Receiver ! Hello,
    {trace, Receiver, 'receive', Hello} = receive_first_trace(),
    Hello2 = {hello, again, world},
    Receiver ! Hello2,
    {trace, Receiver, 'receive', Hello2} = receive_first_trace(),
    receive_nothing(),

    %% Another process should not be able to trace Receiver.
    Intruder = fun_spawn(fun() -> erlang:trace(Receiver, true, ['receive']) end),
    {'EXIT', Intruder, {badarg, _}} = receive_first(),

    %% Untrace the process; we should not receive anything.
    1 = erlang:trace(Receiver, false, ['receive']),
    Receiver ! {hello, there},
    Receiver ! any_garbage,
    receive_nothing(),
    ok.

%% Tests that receive of a message always happens before a call with
%% that message and that links/unlinks are ordered together with the
%% 'receive'.
link_receive_call_correlation() ->
    [{timetrap, {minutes, 5}}].
link_receive_call_correlation(Config) when is_list(Config) ->
    Receiver = fun_spawn(fun F() ->
                                 receive
                                     stop -> ok;
                                     M -> receive_msg(M), F()
                                 end
                         end),
    process_flag(trap_exit, true),

    %% Trace the process; make sure that we receive the trace messages.
    1 = erlang:trace(Receiver, true, ['receive', procs, call, timestamp, scheduler_id]),
    1 = erlang:trace_pattern({?MODULE, receive_msg, '_'}, [], [local]),

    Num = 100000,

    (fun F(0) -> [];
         F(N) ->
             if N rem 2 == 0 ->
                     link(Receiver);
                true ->
                     unlink(Receiver)
             end,
             [Receiver ! N | F(N-1)]
     end)(Num),

    Receiver ! stop,
    MonRef = erlang:monitor(process, Receiver),
    receive {'DOWN', MonRef, _, _, _} -> ok end,
    Ref = erlang:trace_delivered(Receiver),
    receive {trace_delivered, _, Ref} -> ok end,

    Msgs = (fun F() -> receive M -> [M | F()] after 1 -> [] end end)(),

    case check_consistent(Receiver, Num, Num, Num, Msgs) of
        ok ->
            ok;
        {error, Reason} ->
            ct:log("~p", [Msgs]),
            ct:fail({error, Reason})
    end.

-define(schedid, , _).

check_consistent(_Pid, Recv, Call, _LU, [Msg | _]) when Recv > Call ->
    {error, Msg};
check_consistent(Pid, Recv, Call, LU, [Msg | Msgs]) ->

    case Msg of
        {trace, Pid, 'receive', Recv ?schedid} ->
            check_consistent(Pid,Recv - 1, Call, LU, Msgs);
        {trace_ts, Pid, 'receive', Recv ?schedid, _} ->
            check_consistent(Pid,Recv - 1, Call, LU, Msgs);

        {trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} ->
            check_consistent(Pid,Recv, Call - 1, LU, Msgs);
        {trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} ->
            check_consistent(Pid,Recv, Call - 1, LU, Msgs);

        %% We check that for each receive we have gotten a
        %% getting_linked or getting_unlinked message. Also
        %% if we receive a getting_linked, then the next
        %% message we expect to receive is an even number
        %% and odd number for getting_unlinked.
        {trace, Pid, getting_linked, _Self ?schedid}
          when Recv rem 2 == 0, Recv == LU ->
            check_consistent(Pid, Recv, Call, LU - 1, Msgs);
        {trace_ts, Pid, getting_linked, _Self ?schedid, _}
          when Recv rem 2 == 0, Recv == LU ->
            check_consistent(Pid, Recv, Call, LU - 1, Msgs);

        {trace, Pid, getting_unlinked, _Self ?schedid}
          when Recv rem 2 == 1, Recv == LU ->
            check_consistent(Pid, Recv, Call, LU - 1, Msgs);
        {trace_ts, Pid, getting_unlinked, _Self ?schedid, _}
          when Recv rem 2 == 1, Recv == LU ->
            check_consistent(Pid, Recv, Call, LU - 1, Msgs);

        {trace,Pid,'receive',Ignore ?schedid}
          when Ignore == stop; Ignore == timeout ->
            check_consistent(Pid, Recv, Call, LU, Msgs);
        {trace_ts,Pid,'receive',Ignore ?schedid,_}
          when Ignore == stop; Ignore == timeout ->
            check_consistent(Pid, Recv, Call, LU, Msgs);

        {trace, Pid, exit, normal ?schedid} ->
            check_consistent(Pid, Recv, Call, LU, Msgs);
        {trace_ts, Pid, exit, normal  ?schedid, _} ->
            check_consistent(Pid, Recv, Call, LU, Msgs);
        {'EXIT', Pid, normal} ->
            check_consistent(Pid, Recv, Call, LU, Msgs);
        Msg ->
            {error, Msg}
    end;
check_consistent(_, 0, 0, 0, []) ->
    ok;
check_consistent(_, Recv, Call, LU, []) ->
    {error,{Recv, Call, LU}}.

receive_msg(M) ->
    M.

%% Test that traces are generated for messages sent
%% and received to/from self().
self_send(Config) when is_list(Config) ->
    Fun =
    fun(Self, Parent) -> receive
                             go_ahead ->
                                 self() ! from_myself,
                                 Self(Self, Parent);
                             from_myself ->
                                 Parent ! done
                         end
    end,
    Self = self(),
    SelfSender = fun_spawn(Fun, [Fun, Self]),
    erlang:trace(SelfSender, true, ['receive', 'send']),
    SelfSender ! go_ahead,
    receive {trace, SelfSender, 'receive', go_ahead} -> ok end,
    receive {trace, SelfSender, 'receive', from_myself} -> ok end,
    receive
        {trace,SelfSender,send,from_myself,SelfSender} -> ok
    end,
    receive {trace,SelfSender,send,done,Self} -> ok end,
    receive done -> ok end,
    ok.

%% Test that we can receive timeout traces.
timeout_trace(Config) when is_list(Config) ->
    Process = fun_spawn(fun process/0),
    1 = erlang:trace(Process, true, ['receive']),
    Process ! timeout_please,
    {trace, Process, 'receive', timeout_please} = receive_first_trace(),
    {trace, Process, 'receive', timeout} = receive_first_trace(),
    receive_nothing(),
    ok.

%% Tests that trace(Pid, How, [send]) works.

send_trace(Config) when is_list(Config) ->
    process_flag(trap_exit, true),
    Sender = fun_spawn(fun sender/0),
    Receiver = fun_spawn(fun receiver/0),

    %% Check that a message sent to another process is traced.
    1 = erlang:trace(Sender, true, [send]),
    Sender ! {send_please, Receiver, to_receiver},
    {trace, Sender, send, to_receiver, Receiver} = receive_first_trace(),
    receive_nothing(),

    %% Check that a message sent to another registered process is traced.
    register(?MODULE,Receiver),
    Sender ! {send_please, ?MODULE, to_receiver},
    {trace, Sender, send, to_receiver, ?MODULE} = receive_first_trace(),
    receive_nothing(),
    unregister(?MODULE),

    %% Check that a message sent to this process is traced.
    Sender ! {send_please, self(), to_myself},
    receive to_myself -> ok end,
    Self = self(),
    {trace, Sender, send, to_myself, Self} = receive_first_trace(),
    receive_nothing(),

    %% Check that a message sent to dead process is traced.
    {Pid,Ref} = spawn_monitor(fun() -> ok end),
    receive {'DOWN',Ref,_,_,_} -> ok end,
    Sender ! {send_please, Pid, to_dead},
    {trace, Sender, send_to_non_existing_process, to_dead, Pid} = receive_first_trace(),
    receive_nothing(),

    %% Check that a message sent to unknown registrated process is traced.
    BadargSender = fun_spawn(fun sender/0),
    1 = erlang:trace(BadargSender, true, [send]),
    unlink(BadargSender),
    BadargSender ! {send_please, not_registered, to_unknown},
    {trace, BadargSender, send, to_unknown, not_registered} = receive_first_trace(),
    receive_nothing(),

    %% Another process should not be able to trace Sender.
    Intruder = fun_spawn(fun() -> erlang:trace(Sender, true, [send]) end),
    {'EXIT', Intruder, {badarg, _}} = receive_first(),

    %% Untrace the sender process and make sure that we receive no more
    %% trace messages.
    1 = erlang:trace(Sender, false, [send]),
    Sender ! {send_please, Receiver, to_receiver},
    Sender ! {send_please, self(), to_myself_again},
    receive to_myself_again -> ok end,
    receive_nothing(),
    ok.

%% Test trace(Pid, How, [procs]).
procs_trace(Config) when is_list(Config) ->
    Name = list_to_atom(atom_to_list(?MODULE)++"_procs_trace"),
    Self = self(),
    process_flag(trap_exit, true),
    %%
    Proc1 = spawn_link(?MODULE, process, [Self]),
    io:format("Proc1 = ~p ~n", [Proc1]),
    Proc2 = spawn(?MODULE, process, [Self]),
    io:format("Proc2 = ~p ~n", [Proc2]),
    %%
    1 = erlang:trace(Proc1, true, [procs]),
    MFA = {?MODULE, process, [Self]},
    %%
    %% spawn, link
    Proc1 ! {spawn_link_please, Self, MFA},
    Proc3 = receive {spawned, Proc1, P3} -> P3 end,
    {trace, Proc1, spawn, Proc3, MFA} = receive_first_trace(),
    io:format("Proc3 = ~p ~n", [Proc3]),
    {trace, Proc1, link, Proc3} = receive_first_trace(),
    receive_nothing(),
    %%
    %% getting_unlinked by exit()
    Proc1 ! {trap_exit_please, true},
    Reason3 = make_ref(),
    Proc1 ! {send_please, Proc3, {exit_please, Reason3}},
    receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end,
    {trace, Proc1, getting_unlinked, Proc3} = receive_first_trace(),
    Proc1 ! {trap_exit_please, false},
    receive_nothing(),
    %%
    %% link
    Proc1 ! {link_please, Proc2},
    {trace, Proc1, link, Proc2} = receive_first_trace(),
    receive_nothing(),
    %%
    %% unlink
    Proc1 ! {unlink_please, Proc2},
    {trace, Proc1, unlink, Proc2} = receive_first_trace(),
    receive_nothing(),
    %%
    %% getting_linked
    Proc2 ! {link_please, Proc1},
    {trace, Proc1, getting_linked, Proc2} = receive_first_trace(),
    receive_nothing(),
    %%
    %% getting_unlinked
    Proc2 ! {unlink_please, Proc1},
    {trace, Proc1, getting_unlinked, Proc2} = receive_first_trace(),
    receive_nothing(),
    %%
    %% register
    true = register(Name, Proc1),
    {trace, Proc1, register, Name} = receive_first_trace(),
    receive_nothing(),
    %%
    %% unregister
    true = unregister(Name),
    {trace, Proc1, unregister, Name} = receive_first_trace(),
    receive_nothing(),
    %%
    %% exit (with registered name, due to link)
    Reason4 = make_ref(),
    Proc1 ! {spawn_link_please, Self, MFA},
    Proc4 = receive {spawned, Proc1, P4} -> P4 end,
    {trace, Proc1, spawn, Proc4, MFA} = receive_first_trace(),
    io:format("Proc4 = ~p ~n", [Proc4]),
    {trace, Proc1, link, Proc4} = receive_first_trace(),
    Proc1 ! {register_please, Name, Proc1},
    {trace, Proc1, register, Name} = receive_first_trace(),
    Proc4 ! {exit_please, Reason4},
    receive {'EXIT', Proc1, Reason4} -> ok end,
    {trace, Proc1, exit, Reason4} = receive_first_trace(),
    {trace, Proc1, unregister, Name} = receive_first_trace(),
    receive_nothing(),
    %%
    %% exit (not linked to tracing process)
    1 = erlang:trace(Proc2, true, [procs]),
    Reason2 = make_ref(),
    Proc2 ! {exit_please, Reason2},
    {trace, Proc2, exit, Reason2} = receive_first_trace(),
    receive_nothing(),
    ok.


dist_procs_trace(Config) when is_list(Config) ->
    ct:timetrap({seconds, 15}),
    OtherName = atom_to_list(?MODULE)++"_dist_procs_trace",
    {ok, OtherNode} = start_node(OtherName),
    Self = self(),
    process_flag(trap_exit, true),
    %%
    Proc1 = spawn_link(?MODULE, process, [Self]),
    io:format("Proc1 = ~p ~n", [Proc1]),
    Proc2 = spawn(OtherNode, ?MODULE, process, [Self]),
    io:format("Proc2 = ~p ~n", [Proc2]),
    %%
    1 = erlang:trace(Proc1, true, [procs]),
    MFA = {?MODULE, process, [Self]},
    %%
    %% getting_unlinked by exit()
    Proc1 ! {spawn_link_please, Self, OtherNode, MFA},
    Proc1 ! {trap_exit_please, true},
    Proc3 = receive {spawned, Proc1, P3} -> P3 end,
    io:format("Proc3 = ~p ~n", [Proc3]),
    {trace, Proc1, getting_linked, Proc3} = receive_first_trace(),
    Reason3 = make_ref(),
    Proc1 ! {send_please, Proc3, {exit_please, Reason3}},
    receive {Proc1, {'EXIT', Proc3, Reason3}} -> ok end,
    {trace, Proc1, getting_unlinked, Proc3} = receive_first_trace(),
    Proc1 ! {trap_exit_please, false},
    receive_nothing(),
    %%
    %% link
    Proc1 ! {link_please, Proc2},
    {trace, Proc1, link, Proc2} = receive_first_trace(),
    receive_nothing(),
    %%
    %% unlink
    Proc1 ! {unlink_please, Proc2},
    {trace, Proc1, unlink, Proc2} = receive_first_trace(),
    receive_nothing(),
    %%
    %% getting_linked
    Proc2 ! {link_please, Proc1},
    {trace, Proc1, getting_linked, Proc2} = receive_first_trace(),
    receive_nothing(),
    %%
    %% getting_unlinked
    Proc2 ! {unlink_please, Proc1},
    {trace, Proc1, getting_unlinked, Proc2} = receive_first_trace(),
    receive_nothing(),
    %%
    %% exit (with registered name, due to link)
    Name = list_to_atom(OtherName),
    Reason2 = make_ref(),
    Proc1 ! {link_please, Proc2},
    {trace, Proc1, link, Proc2} = receive_first_trace(),
    Proc1 ! {register_please, Name, Proc1},
    {trace, Proc1, register, Name} = receive_first_trace(),
    Proc2 ! {exit_please, Reason2},
    receive {'EXIT', Proc1, Reason2} -> ok end,
    {trace, Proc1, exit, Reason2} = receive_first_trace(),
    {trace, Proc1, unregister, Name} = receive_first_trace(),
    receive_nothing(),
    %%
    %% Done.
    true = stop_node(OtherNode),
    ok.




%% Tests trace(Pid, How, [set_on_spawn]).

set_on_spawn(Config) when is_list(Config) ->
    Listener = fun_spawn(fun process/0),

    %% Create and trace a process with the set_on_spawn flag.
    %% Make sure it is traced.
    Father_SOS = fun_spawn(fun process/0),
    1 = erlang:trace(Father_SOS, true, [send, set_on_spawn]),
    true = is_send_traced(Father_SOS, Listener, sos_father),

    %% Have the process spawn of two children and test that they
    %% are traced.
    [Child1, Child2] = spawn_children(Father_SOS, 2),
    true = is_send_traced(Child1, Listener, child1),
    true = is_send_traced(Child2, Listener, child2),

    %% Second generation.
    [Child11, Child12] = spawn_children(Child1, 2),
    true = is_send_traced(Child11, Listener, child11),
    true = is_send_traced(Child12, Listener, child12),
    ok.

%% Tests trace(Pid, How, [set_on_first_spawn]).

set_on_first_spawn(Config) when is_list(Config) ->
    ct:timetrap({seconds, 10}),
    Listener = fun_spawn(fun process/0),

    %% Create and trace a process with the set_on_first_spawn flag.
    %% Make sure it is traced.
    Parent = fun_spawn(fun process/0),
    1 = erlang:trace(Parent, true, [send, set_on_first_spawn]),
    is_send_traced(Parent, Listener, sos_father),

    %% Have the process spawn off three children and test that the
    %% first is traced.
    [Child1, Child2, Child3] = spawn_children(Parent, 3),
    true = is_send_traced(Child1, Listener, child1),
    false = is_send_traced(Child2, Listener, child2),
    false = is_send_traced(Child3, Listener, child3),
    receive_nothing(),
    ok.


%% Tests arguments to erlang:system_monitor/0,1,2
system_monitor_args(Config) when is_list(Config) ->
    Self = self(),
    %%
    OldMonitor = erlang:system_monitor(undefined),
    undefined = erlang:system_monitor(Self, [{long_gc,0}]),
    MinT = case erlang:system_monitor() of
               {Self,[{long_gc,T}]} when is_integer(T), T > 0 -> T;
               Other1 -> test_server:fault(Other1)
           end,
    {Self,[{long_gc,MinT}]} = erlang:system_monitor(),
    {Self,[{long_gc,MinT}]} = 
    erlang:system_monitor({Self,[{large_heap,0}]}),
    MinN = case erlang:system_monitor() of
               {Self,[{large_heap,N}]} when is_integer(N), N > 0 -> N;
               Other2 -> test_server:fault(Other2)
           end,
    {Self,[{large_heap,MinN}]} = erlang:system_monitor(),
    {Self,[{large_heap,MinN}]} = 
    erlang:system_monitor(Self, [busy_port]),
    {Self,[busy_port]} = erlang:system_monitor(),
    {Self,[busy_port]} = 
    erlang:system_monitor({Self,[busy_dist_port]}),
    {Self,[busy_dist_port]} = erlang:system_monitor(),
    All = lists:sort([busy_port,busy_dist_port,
                      {long_gc,1},{large_heap,65535}]),
    {Self,[busy_dist_port]} = erlang:system_monitor(Self, All),
    {Self,A1} = erlang:system_monitor(),
    All = lists:sort(A1),
    {Self,A1} = erlang:system_monitor(Self, []),
    Pid = spawn(fun () -> receive {Self,die} -> exit(die) end end),
    Mref = erlang:monitor(process, Pid),
    undefined = erlang:system_monitor(Pid, All),
    {Pid,A2} = erlang:system_monitor(),
    All = lists:sort(A2),
    Pid ! {Self,die},
    receive {'DOWN',Mref,_,_,_} -> ok end,
    undefined = erlang:system_monitor(OldMonitor),
    erlang:yield(),
    OldMonitor = erlang:system_monitor(),
    %%
    {'EXIT',{badarg,_}} = (catch erlang:system_monitor(atom)),
    {'EXIT',{badarg,_}} = (catch erlang:system_monitor({})),
    {'EXIT',{badarg,_}} = (catch erlang:system_monitor({1})),
    {'EXIT',{badarg,_}} = (catch erlang:system_monitor({1,2,3})),
    {'EXIT',{badarg,_}} = 
    (catch erlang:system_monitor({Self,atom})),
    {'EXIT',{badarg,_}} = 
    (catch erlang:system_monitor(atom, atom)),
    {'EXIT',{badarg,_}} = 
    (catch erlang:system_monitor({Self,[busy_port|busy_dist_port]})),
    {'EXIT',{badarg,_}} = 
    (catch erlang:system_monitor(Self, [{long_gc,-1}])),
    {'EXIT',{badarg,_}} = 
    (catch erlang:system_monitor({Self,[{long_gc,atom}]})),
    {'EXIT',{badarg,_}} = 
    (catch erlang:system_monitor(Self,[{large_heap,-1}])),
    {'EXIT',{badarg,_}} = 
    (catch erlang:system_monitor({Self,[{large_heap,atom}]})),
    ok.


%% Tests arguments to erlang:system_monitor/0,1,2
more_system_monitor_args(Config) when is_list(Config) ->
    try_l(64000),
    try_l(16#7ffffff),
    try_l(16#3fffffff),
    try_l(16#7fffffff),
    try_l(16#ffffffff),
    ok.

try_l(Val) ->
    Self = self(),
    Arbitrary1 = 77777,
    Arbitrary2 = 88888,

    erlang:system_monitor(undefined),

    undefined = erlang:system_monitor(Self, [{long_gc,Val},{large_heap,Arbitrary1}]),

    {Self,Comb0} = erlang:system_monitor(Self, [{long_gc,Arbitrary2},{large_heap,Val}]),
    [{large_heap,Arbitrary1},{long_gc,Val}] = lists:sort(Comb0),

    {Self,Comb1} = erlang:system_monitor(undefined),
    [{large_heap,Val},{long_gc,Arbitrary2}] = lists:sort(Comb1).

monitor_sys(Parent) ->
    receive 
        {monitor,Pid,long_schedule,Data} when is_pid(Pid) -> 
            io:format("Long schedule of ~w: ~w~n",[Pid,Data]),
            Parent ! {Pid,Data},
            monitor_sys(Parent);
        {monitor,Port,long_schedule,Data} when is_port(Port) -> 
            {name,Name} = erlang:port_info(Port,name),
            io:format("Long schedule of ~w (~p): ~w~n",[Port,Name,Data]),
            Parent ! {Port,Data},
            monitor_sys(Parent);
        Other ->
            erlang:display(Other)
    end.

start_monitor() ->
    Parent = self(),
    Mpid = spawn_link(fun() -> monitor_sys(Parent) end),
    erlang:system_monitor(Mpid,[{long_schedule,100}]),
    erlang:yield(), % Need to be rescheduled for the trace to take
    ok.

%% Tests erlang:system_monitor(Pid, [{long_schedule,Time}])
system_monitor_long_schedule(Config) when is_list(Config) ->
    Path = proplists:get_value(data_dir, Config),
    erl_ddll:start(),
    case (catch load_driver(Path, slow_drv)) of
        ok ->
            do_system_monitor_long_schedule();
        _Error ->
            {skip, "Unable to load slow_drv (windows or no usleep()?)"}
    end.
do_system_monitor_long_schedule() ->
    start_monitor(),
    Port = open_port({spawn_driver,slow_drv}, []),
    "ok" = erlang:port_control(Port,0,[]),
    Self = self(),
    receive
        {Self,L} when is_list(L) ->
            ok
    after 1000 ->
              ct:fail(no_trace_of_pid)
    end,
    "ok" = erlang:port_control(Port,1,[]),
    "ok" = erlang:port_control(Port,2,[]),
    receive
        {Port,LL} when is_list(LL) ->
            ok
    after 1000 ->
              ct:fail(no_trace_of_port)
    end,
    port_close(Port),
    erlang:system_monitor(undefined),
    ok.


-define(LONG_GC_SLEEP, 670).

%% Tests erlang:system_monitor(Pid, [{long_gc,Time}])
system_monitor_long_gc_1(Config) when is_list(Config) ->
    erts_debug:set_internal_state(available_internal_state, true),
    try 
        case erts_debug:get_internal_state(force_heap_frags) of
            true ->
                {skip,"emulator with FORCE_HEAP_FRAGS defined"};
            false ->
                %% Add ?LONG_GC_SLEEP ms to all gc
                erts_debug:set_internal_state(test_long_gc_sleep,
                                              ?LONG_GC_SLEEP),
                LoadFun = fun () -> 
                                  garbage_collect(),
                                  self() 
                          end,
                long_gc(LoadFun, false)
        end
    after
        erts_debug:set_internal_state(test_long_gc_sleep, 0),
        erts_debug:set_internal_state(available_internal_state, false)	
    end.

%% Tests erlang:system_monitor(Pid, [{long_gc,Time}])
system_monitor_long_gc_2(Config) when is_list(Config) ->
    erts_debug:set_internal_state(available_internal_state, true),
    try
        case erts_debug:get_internal_state(force_heap_frags) of
            true ->
                {skip,"emulator with FORCE_HEAP_FRAGS defined"};
            false ->
                %% Add ?LONG_GC_SLEEP ms to all gc
                erts_debug:set_internal_state(test_long_gc_sleep,
                                              ?LONG_GC_SLEEP),
                Parent = self(),
                LoadFun =
                fun () ->
                        Ref = make_ref(),
                        Pid = 
                        spawn_link(
                          fun () ->
                                  garbage_collect(),
                                  Parent ! {Ref, self()}
                          end),
                        receive {Ref, Pid} -> Pid end
                end,
                long_gc(LoadFun, true),
                long_gc(LoadFun, true),
                long_gc(LoadFun, true)
        end
    after
        erts_debug:set_internal_state(test_long_gc_sleep, 0),
        erts_debug:set_internal_state(available_internal_state, false)
    end.

long_gc(LoadFun, ExpectMonMsg) ->
    Self = self(),
    Time = 1,
    OldMonitor = erlang:system_monitor(Self, [{long_gc,Time}]),
    Pid = LoadFun(),
    Ref = erlang:trace_delivered(Pid),
    receive {trace_delivered, Pid, Ref} -> ok end,
    {Self,[{long_gc,Time}]} = erlang:system_monitor(OldMonitor),
    case {long_gc_check(Pid, Time, undefined), ExpectMonMsg} of
        {ok, true} when Pid =/= Self ->
            ok;
        {ok, false} ->
            ct:fail(unexpected_system_monitor_message_received);
        {undefined, false} ->
            ok;
        {undefined, true} ->
            ct:fail(no_system_monitor_message_received)
    end.

long_gc_check(Pid, Time, Result) ->
    receive
        {monitor,Pid,long_gc,L} = Monitor ->
            case lists:foldl(
                   fun (_, error) ->
                           error;
                       ({timeout,T}, N) when is_integer(T),
                                             Time =< T, T =< 10*?LONG_GC_SLEEP ->
                           %% OTP-7622. The time T must be within reasonable limits
                           %% for the test to pass.
                           N-1;
                       ({heap_size,_}, N) ->
                           N-1;
                       ({old_heap_size,_}, N) ->
                           N-1;
                       ({stack_size,_}, N) ->
                           N-1;
                       ({mbuf_size,_}, N) ->
                           N-1;
                       ({heap_block_size,_}, N) ->
                           N-1;
                       ({old_heap_block_size,_}, N) ->
                           N-1;
                       (_, _) ->
                           error
                   end, 7, L) of
                0 ->
                    long_gc_check(Pid, Time, ok);
                error ->
                    {error,Monitor}
            end;
        {monitor,_,long_gc,_} ->
            long_gc_check(Pid, Time, Result);
        Other ->
            {error,Other}
    after 0 ->
              Result
    end.

%% Tests erlang:system_monitor(Pid, [{large_heap,Size}])
system_monitor_large_heap_1(Config) when is_list(Config) ->
    LoadFun =
    fun (Size) -> 
            List = seq(1,2*Size),
            garbage_collect(),
            true = lists:prefix([1], List),
            self() 
    end,
    large_heap(LoadFun, false).

%% Tests erlang:system_monitor(Pid, [{large_heap,Size}])
system_monitor_large_heap_2(Config) when is_list(Config) ->
    Parent = self(),
    LoadFun =
    fun (Size) ->
            Ref = make_ref(),
            Pid = 
            spawn_opt(fun () ->
                              garbage_collect(),
                              Parent ! {Ref, self()}
                      end,
                      [link, {min_heap_size, 2*Size}]),
            receive {Ref, Pid} -> Pid end
    end,
    large_heap(LoadFun, true).

large_heap(LoadFun, ExpectMonMsg) ->
    ct:timetrap({seconds, 20}),
    %%
    Size = 65535,
    Self = self(),
    NewMonitor = {Self,[{large_heap,Size}]},
    OldMonitor = erlang:system_monitor(NewMonitor),
    Pid = LoadFun(Size),
    Ref = erlang:trace_delivered(Pid),
    receive {trace_delivered, Pid, Ref} -> ok end,
    {Self,[{large_heap,Size}]} = erlang:system_monitor(OldMonitor),
    case {large_heap_check(Pid, Size, undefined), ExpectMonMsg} of
        {ok, true} when Pid =/= Self ->
            ok;
        {ok, false} ->
            ct:fail(unexpected_system_monitor_message_received);
        {undefined, false} ->
            ok;
        {undefined, true} ->
            ct:fail(no_system_monitor_message_received)
    end,
    ok.

large_heap_check(Pid, Size, Result) ->
    receive
        {monitor,Pid,large_heap,L} = Monitor ->
            case lists:foldl(
                   fun (_, error) ->
                           error;
                       ({heap_size,_}, N) ->
                           N-1;
                       ({old_heap_size,_}, N) ->
                           N-1;
                       ({stack_size,_}, N) ->
                           N-1;
                       ({mbuf_size,_}, N) ->
                           N-1;
                       ({heap_block_size,_}, N) ->
                           N-1;
                       ({old_heap_block_size,_}, N) ->
                           N-1;
                       (_, _) ->
                           error
                   end, 6, L) of
                0 ->
                    large_heap_check(Pid, Size, ok);
                error ->
                    {error,Monitor}
            end;
        {monitor,_,large_heap,_} ->
            large_heap_check(Pid, Size, Result);
        Other ->
            {error,Other}
    after 0 ->
              Result
    end.

seq(N, M) ->
    seq(N, M, []).

seq(M, M, R) ->
    lists:reverse(R);
seq(N, M, R) ->
    seq(N+1, M, [N|R]).


is_send_traced(Pid, Listener, Msg) ->
    Pid ! {send_please, Listener, Msg},
    receive
        Any ->
            {trace, Pid, send, Msg, Listener} = Any,
            true
    after 1000 ->
              false
    end.

%% This procedure assumes that the Parent process is send traced.

spawn_children(Parent, Number) ->
    spawn_children(Parent, Number, []).

spawn_children(_Parent, 0, Result) ->
    lists:reverse(Result);
spawn_children(Parent, Number, Result) ->
    Self = self(),
    Parent ! {spawn_please, Self, fun process/0},
    Child = 
    receive
        {trace, Parent, send, {spawned, Pid}, Self} -> Pid
    end,
    receive
        {spawned, Child} ->
            spawn_children(Parent, Number-1, [Child|Result])
    end.

%% Test erlang:suspend/1 and erlang:resume/1.
suspend(Config) when is_list(Config) ->
    ct:timetrap({minutes,2}),
    Worker = fun_spawn(fun worker/0),
    %% Suspend a process and test that it is suspended.
    ok = do_suspend(Worker, 10000),
    ok.

do_suspend(_Pid, 0) ->
    ok;
do_suspend(Pid, N) ->
    %% Suspend a process and test that it is suspended.
    true = erlang:suspend_process(Pid),
    {status, suspended} = process_info(Pid, status),

    %% Unsuspend the process and make sure it starts working.
    true = erlang:resume_process(Pid),
    case process_info(Pid, status) of
        {status, runnable} -> ok;
        {status, running} -> ok;
        {status, garbage_collecting} -> ok;
        ST -> ct:fail(ST)
    end,
    erlang:yield(),
    do_suspend(Pid, N-1).



mutual_suspend(Config) when is_list(Config) ->
    TimeoutSecs = 5*60,
    ct:timetrap({seconds, TimeoutSecs}),
    Parent = self(),
    Fun = fun () ->
                  receive
                      {go, Pid} ->
                          do_mutual_suspend(Pid, 100000)
                  end,
                  Parent ! {done, self()},
                  receive after infinity -> ok end
          end,
    P1 = spawn_link(Fun),
    P2 = spawn_link(Fun),
    T1 = erlang:start_timer((TimeoutSecs - 5)*1000, self(), oops),
    T2 = erlang:start_timer((TimeoutSecs - 5)*1000, self(), oops),
    P1 ! {go, P2},
    P2 ! {go, P1},
    Res1 = receive
               {done, P1} -> done;
               {timeout,T1,_} -> timeout
           end,
    Res2 = receive
               {done, P2} -> done;
               {timeout,T2,_} -> timeout
           end,
    P1S = process_info(P1, status),
    P2S = process_info(P2, status),
    io:format("P1S=~p P2S=~p", [P1S, P2S]),
    false = {status, suspended} == P1S,
    false = {status, suspended} == P2S,
    unlink(P1), exit(P1, bang),
    unlink(P2), exit(P2, bang),
    done = Res1,
    done = Res2,
    ok.

do_mutual_suspend(_Pid, 0) ->
    ok;
do_mutual_suspend(Pid, N) ->
    %% Suspend a process and test that it is suspended.
    true = erlang:suspend_process(Pid),
    {status, suspended} = process_info(Pid, status),
    %% Unsuspend the process.
    true = erlang:resume_process(Pid),
    do_mutual_suspend(Pid, N-1).		

suspend_exit(Config) when is_list(Config) ->
    ct:timetrap({minutes, 2}),
    rand:seed(exsplus, {4711,17,4711}),
    do_suspend_exit(5000),
    ok.

do_suspend_exit(0) ->
    ok;
do_suspend_exit(N) ->
    Work = rand:uniform(50),
    Parent = self(),
    {Suspendee, Mon2}
    = spawn_monitor(fun () ->
                            suspend_exit_work(Work),
                            exit(normal)
                    end),
    {Suspender, Mon1}
    = spawn_monitor(
        fun () ->
                suspend_exit_work(Work div 2),
                Parent ! {doing_suspend, self()},
                case catch erlang:suspend_process(Suspendee) of
                    {'EXIT', _} ->
                        ok;
                    true ->
                        erlang:resume_process(Suspendee)
                end
        end),
    receive
        {doing_suspend, Suspender} ->
            case N rem 2 of
                0 -> exit(Suspender, bang);
                1 -> ok
            end
    end,
    receive {'DOWN', Mon1, process, Suspender, _} -> ok end,
    receive {'DOWN', Mon2, process, Suspendee, _} -> ok end,
    do_suspend_exit(N-1).




suspend_exit_work(0) ->
    ok;
suspend_exit_work(N) ->
    process_info(self()),
    suspend_exit_work(N-1).

-define(CHK_SUSPENDED(P,B), chk_suspended(P, B, ?LINE)).

chk_suspended(P, Bool, Line) ->
    {Bool, Line} = {({status, suspended} == process_info(P, status)), Line}.

suspender_exit(Config) when is_list(Config) ->
    ct:timetrap({minutes, 3}),
    P1 = spawn_link(fun () -> receive after infinity -> ok end end),
    {'EXIT', _} = (catch erlang:resume_process(P1)),
    {P2, M2} = spawn_monitor(
                 fun () ->
                         ?CHK_SUSPENDED(P1, false),
                         erlang:suspend_process(P1),
                         ?CHK_SUSPENDED(P1, true),
                         erlang:suspend_process(P1),
                         erlang:suspend_process(P1),
                         erlang:suspend_process(P1),
                         ?CHK_SUSPENDED(P1, true),
                         erlang:resume_process(P1),
                         erlang:resume_process(P1),
                         erlang:resume_process(P1),
                         ?CHK_SUSPENDED(P1, true),
                         erlang:resume_process(P1),
                         ?CHK_SUSPENDED(P1, false),
                         erlang:suspend_process(P1),
                         erlang:suspend_process(P1),
                         erlang:suspend_process(P1),
                         ?CHK_SUSPENDED(P1, true),
                         exit(bang)
                 end),
    receive
        {'DOWN', M2,process,P2,R2} ->
            bang = R2,
            ?CHK_SUSPENDED(P1, false)
    end,
    Parent = self(),
    {P3, M3} = spawn_monitor(
                 fun () ->
                         erlang:suspend_process(P1),
                         ?CHK_SUSPENDED(P1, true),
                         Parent ! self(),
                         receive after infinity -> ok end
                 end),
    {P4, M4} = spawn_monitor(
                 fun () ->
                         erlang:suspend_process(P1),
                         ?CHK_SUSPENDED(P1, true),
                         Parent ! self(),
                         receive after infinity -> ok end
                 end),
    {P5, M5} = spawn_monitor(
                 fun () ->
                         erlang:suspend_process(P1),
                         ?CHK_SUSPENDED(P1, true),
                         Parent ! self(),
                         receive after infinity -> ok end
                 end),
    {P6, M6} = spawn_monitor(
                 fun () ->
                         erlang:suspend_process(P1),
                         ?CHK_SUSPENDED(P1, true),
                         Parent ! self(),
                         receive after infinity -> ok end
                 end),
    {P7, M7} = spawn_monitor(
                 fun () ->
                         erlang:suspend_process(P1),
                         ?CHK_SUSPENDED(P1, true),
                         Parent ! self(),
                         receive after infinity -> ok end
                 end),
    receive P3 -> ok end,
    receive P4 -> ok end,
    receive P5 -> ok end,
    receive P6 -> ok end,
    receive P7 -> ok end,
    ?CHK_SUSPENDED(P1, true),
    exit(P3, bang),
    receive
        {'DOWN',M3,process,P3,R3} ->
            bang = R3,
            ?CHK_SUSPENDED(P1, true)
    end,
    exit(P4, bang),
    receive
        {'DOWN',M4,process,P4,R4} ->
            bang = R4,
            ?CHK_SUSPENDED(P1, true)
    end,
    exit(P5, bang),
    receive
        {'DOWN',M5,process,P5,R5} ->
            bang = R5,
            ?CHK_SUSPENDED(P1, true)
    end,
    exit(P6, bang),
    receive
        {'DOWN',M6,process,P6,R6} ->
            bang = R6,
            ?CHK_SUSPENDED(P1, true)
    end,
    exit(P7, bang),
    receive
        {'DOWN',M7,process,P7,R7} ->
            bang = R7,
            ?CHK_SUSPENDED(P1, false)
    end,
    unlink(P1),
    exit(P1, bong),
    ok.

suspend_system_limit(Config) when is_list(Config) ->
    case os:getenv("ERL_EXTREME_TESTING") of
        "true" ->
            ct:timetrap({minutes, 3*60}),
            P = spawn_link(fun () -> receive after infinity -> ok end end),
            suspend_until_system_limit(P),
            unlink(P),
            exit(P, bye),
            ok;
        _ ->
            {skip, "Takes too long time for normal testing"}
    end.

suspend_until_system_limit(P) ->
    suspend_until_system_limit(P, 0, 0).

suspend_until_system_limit(P, N, M) ->
    NewM = case M of
               1 ->
                   ?CHK_SUSPENDED(P, true), 2;
               1000000 ->
                   erlang:display(N), 1;
               _ ->
                   M+1
           end,
    case catch erlang:suspend_process(P) of
        true ->
            suspend_until_system_limit(P, N+1, NewM);
        {'EXIT', R} when R == system_limit;
                         element(1, R) == system_limit ->
            io:format("system limit at ~p~n", [N]),
            resume_from_system_limit(P, N, 0);
        Error ->
            ct:fail(Error)
    end.

resume_from_system_limit(P, 0, _) ->
    ?CHK_SUSPENDED(P, false),
    {'EXIT', _} = (catch erlang:resume_process(P)),
    ok;
resume_from_system_limit(P, N, M) ->
    NewM = case M of
               1 ->
                   ?CHK_SUSPENDED(P, true), 2;
               1000000 ->
                   erlang:display(N), 1;
               _ ->
                   M+1
           end,
    erlang:resume_process(P),
    resume_from_system_limit(P, N-1, NewM).

-record(susp_info, {async = 0,
                    dbl_async = 0,
                    synced = 0,
                    async_once = 0}).

suspend_opts(Config) when is_list(Config) ->
    ct:timetrap({minutes, 3}),
    Self = self(),
    wait_for_empty_runq(10),
    Tok = spawn_link(fun () ->
                             Self ! self(),
                             tok_trace_loop(Self, 0, 1000000000)
                     end),
    TC = 1000,
    receive Tok -> ok end,
    SF = fun (N, #susp_info {async = A,
                             dbl_async = AA,
                             synced = S,
                             async_once = AO} = Acc) ->
                 erlang:suspend_process(Tok, [asynchronous]),
                 Res = case {suspend_count(Tok), N rem 4} of
                           {0, 2} ->
                               erlang:suspend_process(Tok,
                                                      [asynchronous]),
                               case suspend_count(Tok) of
                                   2 ->
                                       erlang:resume_process(Tok),
                                       Acc#susp_info{async = A+1};
                                   0 ->
                                       erlang:resume_process(Tok),
                                       Acc#susp_info{async = A+1,
                                                     dbl_async = AA+1}
                               end;
                           {0, 1} ->
                               erlang:suspend_process(Tok,
                                                      [asynchronous,
                                                       unless_suspending]),
                               case suspend_count(Tok) of
                                   1 ->
                                       Acc#susp_info{async = A+1};
                                   0 ->
                                       Acc#susp_info{async = A+1,
                                                     async_once = AO+1}
                               end;
                           {0, 0} ->
                               erlang:suspend_process(Tok,
                                                      [unless_suspending]),
                               1 = suspend_count(Tok),
                               Acc#susp_info{async = A+1,
                                             synced = S+1};
                           {0, _} ->
                               Acc#susp_info{async = A+1};
                           _ ->
                               Acc
                       end,
                 erlang:resume_process(Tok),
                 erlang:yield(),
                 Res
         end,
    SI = repeat_acc(SF, TC, #susp_info{}),
    erlang:suspend_process(Tok, [asynchronous]),
    %% Verify that it eventually suspends
    WaitTime0 = 10,
    WaitTime1 = case {erlang:system_info(debug_compiled),
                      erlang:system_info(lock_checking)} of
                    {false, false} ->
                        WaitTime0;
                    {false, true} ->
                        WaitTime0*5;
                    _ ->
                        WaitTime0*10
                end,
    WaitTime = case {erlang:system_info(schedulers_online),
                     erlang:system_info(logical_processors)} of
                   {Schdlrs, CPUs} when is_integer(CPUs),
                                        Schdlrs =< CPUs ->
                       WaitTime1;
                   _ ->
                       WaitTime1*10
               end,
    receive after WaitTime -> ok end,
    1 = suspend_count(Tok),
    erlang:suspend_process(Tok, [asynchronous]),
    2 = suspend_count(Tok),
    erlang:suspend_process(Tok, [asynchronous]),
    3 = suspend_count(Tok),
    erlang:suspend_process(Tok),
    4 = suspend_count(Tok),
    erlang:suspend_process(Tok),
    5 = suspend_count(Tok),
    erlang:suspend_process(Tok, [unless_suspending]),
    5 = suspend_count(Tok),
    erlang:suspend_process(Tok, [unless_suspending,
                                 asynchronous]),
    5 = suspend_count(Tok),
    erlang:resume_process(Tok),
    erlang:resume_process(Tok),
    erlang:resume_process(Tok),
    erlang:resume_process(Tok),
    1 = suspend_count(Tok),
    io:format("Main suspends: ~p~n"
              "Main async: ~p~n"
              "Double async: ~p~n"
              "Async once: ~p~n"
              "Synced: ~p~n",
              [TC,
               SI#susp_info.async,
               SI#susp_info.dbl_async,
               SI#susp_info.async_once,
               SI#susp_info.synced]),
    case erlang:system_info(schedulers_online) of
        1 ->
            ok;
        _ ->
            true = SI#susp_info.async =/= 0
    end,
    unlink(Tok),
    exit(Tok, bang),
    ok.

suspend_count(Suspendee) ->
    suspend_count(self(), Suspendee).

suspend_count(Suspender, Suspendee) ->
    {suspending, SList} = process_info(Suspender, suspending),

    case lists:keysearch(Suspendee, 1, SList) of
        {value, {_Suspendee, 0, 0}} ->
            ct:fail({bad_suspendee_list, SList});
        {value, {Suspendee, Count, 0}} when is_integer(Count), Count > 0 ->
            {status, suspended} = process_info(Suspendee, status),
            Count;
        {value, {Suspendee, 0, Outstanding}} when is_integer(Outstanding),
                                                  Outstanding > 0 ->
            0;
        false ->
            0;
        Error ->
            ct:fail({bad_suspendee_list, Error, SList})
    end.

repeat_acc(Fun, N, Acc) ->
    repeat_acc(Fun, 0, N, Acc).

repeat_acc(_Fun, N, N, Acc) ->
    Acc;
repeat_acc(Fun, N, M, Acc) ->
    repeat_acc(Fun, N+1, M, Fun(N, Acc)).

%% Tests that waiting process can be suspended
%% (bug in R2D and earlier; see OTP-1488).

%% Test that a waiting process can be suspended.
suspend_waiting(Config) when is_list(Config) ->
    Process = fun_spawn(fun process/0),
    receive after 1 -> ok end,
    true = erlang:suspend_process(Process),
    {status, suspended} = process_info(Process, status),
    ok.


%% Test that erlang:trace(new, true, ...) is cleared when tracer dies.
new_clear(Config) when is_list(Config) ->
    Tracer = spawn(fun receiver/0),
    0 = erlang:trace(new, true, [send, {tracer, Tracer}]),
    {flags, [send]} = erlang:trace_info(new, flags),
    {tracer, Tracer} = erlang:trace_info(new, tracer),
    Mref = erlang:monitor(process, Tracer),
    true = exit(Tracer, done),
    receive
        {'DOWN',Mref,_,_,_} -> ok
    end,
    {flags, []} = erlang:trace_info(new, flags),
    {tracer, []} = erlang:trace_info(new, tracer),
    ok.



%% Test that erlang:trace(all, false, ...) works without tracer.
existing_clear(Config) when is_list(Config) ->
    Self = self(),

    Tracer = fun_spawn(fun receiver/0),
    N = erlang:trace(existing, true, [send, {tracer, Tracer}]),
    {flags, [send]} = erlang:trace_info(Self, flags),
    {tracer, Tracer} = erlang:trace_info(Self, tracer),
    M = erlang:trace(all, false, [all]),
    io:format("Started trace on ~p processes and stopped on ~p~n", 
              [N, M]),
    {flags, []} = erlang:trace_info(Self, flags),
    {tracer, []} = erlang:trace_info(Self, tracer),
    M = N, % Used to be N + 1, but from 19.0 the tracer is also traced

    ok.

%% Test that an invalid flag cause badarg
bad_flag(Config) when is_list(Config) ->
    %% A bad flag could deadlock the SMP emulator in erts-5.5
    {'EXIT', {badarg, _}} = (catch erlang:trace(new,
                                                true,
                                                [not_a_valid_flag])),
    ok.

%% Test erlang:trace_delivered/1
trace_delivered(Config) when is_list(Config) ->
    ct:timetrap({minutes, 1}),
    TokLoops = 10000,
    Go = make_ref(),
    Parent = self(),
    Tok = spawn(fun () ->
                        receive Go -> gone end,
                        tok_trace_loop(Parent, 0, TokLoops)
                end),
    1 = erlang:trace(Tok, true, [procs]),
    Mon = erlang:monitor(process, Tok),
    NoOfTraceMessages = 4*TokLoops + 1,
    io:format("Expect a total of ~p trace messages~n",
              [NoOfTraceMessages]),
    Tok ! Go,
    NoOfTraceMessages = drop_trace_until_down(Tok, Mon),
    receive
        Msg ->
            ct:fail({unexpected_message, Msg})
    after 1000 ->
              ok
    end.

drop_trace_until_down(Proc, Mon) ->
    drop_trace_until_down(Proc, Mon, false, 0, 0).

drop_trace_until_down(Proc, Mon, TDRef, N, D) ->
    case receive Msg -> Msg end of
        {trace_delivered, Proc, TDRef} ->
            io:format("~p trace messages on 'DOWN'~n", [D]),
            io:format("Got a total of ~p trace messages~n", [N]),
            N;
        {'DOWN', Mon, process, Proc, _} ->
            Ref = erlang:trace_delivered(Proc),
            drop_trace_until_down(Proc, Mon, Ref, N, N);
        Trace when is_tuple(Trace),
                   element(1, Trace) == trace,
                   element(2, Trace) == Proc ->
            drop_trace_until_down(Proc, Mon, TDRef, N+1, D)
    end.

tok_trace_loop(_, N, N) ->
    ok;
tok_trace_loop(Parent, N, M) ->
    Name = 'A really stupid name which I will unregister at once',
    link(Parent),
    register(Name, self()),
    unregister(Name),
    unlink(Parent),
    tok_trace_loop(Parent, N+1, M).

%% Waits for and returns the first message in the message queue.

receive_first() ->
    receive
        Any -> Any
    end.

%% Waits for and returns the first message in the message queue.

receive_first_trace() ->
    receive
	Any when element(1,Any) =:= trace; element(1,Any) =:= trace_ts -> Any
    end.

%% Ensures that there is no message in the message queue.

receive_nothing() ->
    receive
        Any ->
            ct:fail({unexpected_message, Any})
    after 200 ->
              ok
    end.


%%% Models for various kinds of processes.

process(Dest) ->
    receive
        {send_please, To, What} ->
            To ! What,
            process(Dest);
        {spawn_link_please, ReplyTo, {M, F, A}} ->
            Pid = spawn_link(M, F, A),
            ReplyTo ! {spawned, self(), Pid},
            process(Dest);
        {spawn_link_please, ReplyTo, Node, {M, F, A}} ->
            Pid = spawn_link(Node, M, F, A),
            ReplyTo ! {spawned, self(), Pid},
            process(Dest);
        {link_please, Pid} ->
            link(Pid),
            process(Dest);
        {unlink_please, Pid} ->
            unlink(Pid),
            process(Dest);
        {register_please, Name, Pid} ->
            register(Name, Pid),
            process(Dest);
        {unregister_please, Name} ->
            unregister(Name),
            process(Dest);
        {exit_please, Reason} ->
            exit(Reason);
        {trap_exit_please, State} ->
            process_flag(trap_exit, State),
            process(Dest);
        Other ->
            Dest ! {self(), Other},
            process(Dest)
    after 3000 ->
              exit(timeout)
    end.


%% A smart process template.

process() ->
    receive
        {spawn_please, ReplyTo, Fun} ->
            Pid = fun_spawn(Fun),
            ReplyTo ! {spawned, Pid},
            process();
        {send_please, To, What} ->
            To ! What,
            process();
        timeout_please ->
            receive after 1 -> process() end;
        _Other ->
            process()
    end.


%% Sends messages when ordered to.

sender() ->
    receive
        {send_please, To, What} ->
            To ! What,
            sender()
    end.


%% Just consumes messages from its message queue.

receiver() ->
    receive
        _Any -> receiver()
    end.

%% Works as long as it receives CPU time.  Will always be RUNNABLE.

worker() ->
    worker(0).

worker(Number) ->
    worker(Number+1).

fun_spawn(Fun) ->
    spawn_link(erlang, apply, [Fun, []]).

fun_spawn(Fun, Args) ->
    spawn_link(erlang, apply, [Fun, Args]).


start_node(Name) ->
    Pa = filename:dirname(code:which(?MODULE)),
    Cookie = atom_to_list(erlang:get_cookie()),
    test_server:start_node(Name, slave,
                           [{args, "-setcookie " ++ Cookie ++" -pa " ++ Pa}]).

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


wait_for_empty_runq(DeadLine) ->
    case statistics(run_queue) of
        0 -> true;
        RQLen ->
            erlang:display("Waiting for empty run queue"),
            MSDL = DeadLine*1000,
            wait_for_empty_runq(MSDL, MSDL, RQLen)
    end.

wait_for_empty_runq(DeadLine, Left, RQLen) when Left =< 0 ->
    issue_non_empty_runq_warning(DeadLine, RQLen),
    false;
wait_for_empty_runq(DeadLine, Left, _RQLen) ->
    Wait = 10,
    UntilDeadLine = Left - Wait,
    receive after Wait -> ok end,
    case statistics(run_queue) of
        0 ->
            erlang:display("Waited for "
                           ++ integer_to_list(DeadLine
                                              - UntilDeadLine)
                           ++ " ms for empty run queue."),
            true;
        NewRQLen ->
            wait_for_empty_runq(DeadLine,
                                UntilDeadLine,
                                NewRQLen)
    end.

issue_non_empty_runq_warning(DeadLine, RQLen) ->
    PIs = lists:foldl(
            fun (P, Acc) ->
                    case process_info(P,
                                      [status,
                                       initial_call,
                                       current_function,
                                       registered_name,
                                       reductions,
                                       message_queue_len]) of
                        [{status, Runnable} | _] = PI when Runnable /= waiting,
                                                           Runnable /= suspended ->
                            [[{pid, P} | PI] | Acc];
                        _ ->
                            Acc
                    end
            end,
            [],
            processes()),
    io:format("WARNING: Unexpected runnable processes in system (waited ~p sec).~n"
              "         Run queue length: ~p~n"
              "         Self: ~p~n"
              "         Processes info: ~p~n",
              [DeadLine div 1000, RQLen, self(), PIs]),
    receive after 1000 -> ok end.

load_driver(Dir, Driver) ->
    case erl_ddll:load_driver(Dir, Driver) of
        ok -> ok;
        {error, Error} = Res ->
            io:format("~s\n", [erl_ddll:format_error(Error)]),
            Res
    end.