aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/tracer_SUITE.erl
blob: ab7d047bc3d8d0d08388b6b40d33485ec5f6378a (plain) (tree)
1
2
3
4


                   
                                                        

























                                                                           


                                                                       







                                                            


                                                                      

                         

                                                        























                                                                
                                     







                                                                   




                                                        




                                                                
                     

                               
















                                                    

       













                                    























                                                                      






                                                                       
                                                                    



                                                        

                                                                       




                             
                                                                       



                            
                                                                       






                                                   



                                                            



















                                                                           

                                         



                                  

                  




                   

                  





































                                                                         

                                                                













                                      
                                                                     





                                                    













































                                                                                                    


                    
                                                                               





                                 

                                                                    



                                                                   
                                          









                                        
                                                               



















                                                                                     
                                                               




                                                                   
                                         







                                                                                     
                                             







                                 
                                                                 




                                                                   
                                           














                                                                            
                                                                         






















                                                                       
                                                                           




















                                                                   
                                                                    



                                               
                                             














                                                                
                                                                      



                                               
                                               




                               
                                                                      







                                       
                                                                     







                                                             
                                         













                                                                         
                                                                      







                                                             
                                                













                                                        
                                                                    



                                               
                                                                













                                                        
                                                                  



                                               
                                                              

                          
                                   



                                                       
                                                     

                                                    
                























                                                                              

                                                          























                                                                               

       

                                        














































                                                                       
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2017. 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(tracer_SUITE).

%%%
%%% Tests the tracer module interface
%%%

-export([all/0, suite/0,groups/0, init_per_suite/1, end_per_suite/1,
	 init_per_group/2,end_per_group/2, init_per_testcase/2,
         end_per_testcase/2]).
-export([load/1, unload/1, reload/1, invalid_tracers/1]).
-export([send/1, recv/1, call/1, call_return/1, spawn/1, exit/1,
         link/1, unlink/1, getting_linked/1, getting_unlinked/1,
         register/1, unregister/1, in/1, out/1, gc_start/1, gc_end/1]).

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

all() ->
    [load, unload, reload, invalid_tracers, {group, basic}].

groups() ->
    [{ basic, [], [send, recv, call, call_return, spawn, exit,
                   link, unlink, getting_linked, getting_unlinked,
                   register, unregister, in, out, gc_start, gc_end]}].

init_per_suite(Config) ->
    erlang:trace_pattern({'_','_','_'}, false, [local]),
    erlang:trace_pattern({'_','_','_'}, false, []),
    purge(),
    Config.

end_per_suite(_Config) ->
    ok.

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.

init_per_testcase(TC, Config) when TC =:= load; TC =:= reload ->

    DataDir = proplists:get_value(data_dir, Config),

    Pid = erlang:spawn(fun F() ->
                               receive
                                   {get, Pid} ->
                                       Pid ! DataDir,
                                       F()
                               end
                       end),
    register(tracer_test_config, Pid),
    common_init_per_testcase(Config);
init_per_testcase(_, Config) ->
    DataDir = proplists:get_value(data_dir, Config),
    case catch tracer_test:enabled(trace_status, self(), self()) of
        discard ->
            ok;
        _ ->
            tracer_test:load(DataDir)
    end,
    common_init_per_testcase(Config).

common_init_per_testcase(Config) ->
    Killer = erlang:spawn(fun() -> killer_loop([]) end),
    register(killer_process, Killer),
    Config.

end_per_testcase(TC, _Config) when TC =:= load; TC =:= reload ->
    purge(),
    exit(whereis(tracer_test_config), kill),
    kill_processes();
end_per_testcase(_, _Config) ->
    purge(),
    kill_processes().

kill_processes() ->
    killer_process ! {get_pids,self()},
    receive
        {pids_to_kill,Pids} -> ok
    end,
    _ = [begin
             case erlang:is_process_alive(P) of
                 true ->
                     io:format("Killing ~p\n", [P]);
                 false ->
                     ok
             end,
             erlang:unlink(P),
             exit(P, kill)
         end || P <- Pids],
    ok.

killer_loop(Pids) ->
    receive
        {add_pid,Pid} ->
            killer_loop([Pid|Pids]);
        {get_pids,To} ->
            To ! {pids_to_kill,Pids}
    end.

kill_me(Pid) ->
    killer_process ! {add_pid,Pid},
    Pid.

%%% Test cases follow.

load(_Config) ->
    purge(),
    1 = erlang:trace(self(), true, [{tracer, tracer_test, []}, call]),
    purge(),
    1 = erlang:trace_pattern({?MODULE, all, 0}, [],
                             [{meta, tracer_test, []}]),
    ok.

unload(_Config) ->

    ServerFun = fun F(0, undefined) ->
                        receive
                            {N, Pid} -> F(N, Pid)
                        end;
                    F(0, Pid) ->
                        Pid ! done,
                        F(0, undefined);
                    F(N, Pid) ->
                        ?MODULE:all(),
                        F(N-1, Pid)
                end,

    Pid = erlang:spawn_link(fun() -> ServerFun(0, undefined) end),

    Tc = fun(N) ->
                 Pid ! {N, self()},
                 receive done -> ok after 1000 -> ct:fail(timeout) end,
                 trace_delivered(Pid)
         end,

    1 = erlang:trace(Pid, true, [{tracer, tracer_test,
                                  {#{ call => trace }, self(), []}},
                                 call]),
    1 = erlang:trace_pattern({?MODULE, all, 0}, [], []),

    Tc(1),
    receive _M -> ok after 0 -> ct:fail(timeout) end,
    receive M0 -> ct:fail({unexpected_message0, M0}) after 0 -> ok end,

    code:purge(tracer_test),
    code:delete(tracer_test),

    Tc(1),
    receive M1 -> ct:fail({unexpected_message1, M1}) after 0 -> ok end,

    code:purge(tracer_test),

    Tc(1),
    receive M2 -> ct:fail({unexpected_message2, M2}) after 0 -> ok end,

    ok.

%% This testcase is here to make sure there are not
%% segfaults when reloading the current nifs.
reload(_Config) ->

    Tracer = spawn_opt(fun F() -> receive _M -> F() end end,
                       [{message_queue_data, off_heap}]),
    erlang:link(Tracer),
    Tracee = spawn_link(fun reload_loop/0),

    [begin
         Ref = make_ref(),
         State = {#{ call => trace }, Tracer, [Ref]},
         erlang:trace(Tracee, true, [{tracer, tracer_test,State}, call]),
         erlang:trace_pattern({?MODULE, all, 0}, []),

         false = code:purge(tracer_test),
         {module, _} = code:load_file(tracer_test),

         %% There is a race involved in between when the internal nif cache
         %% is purged and when the reload_loop needs the tracer module
         %% so the tracer may be removed or still there.
         case erlang:trace_info(Tracee, tracer) of
             {tracer, []} -> ok;
             {tracer, {tracer_test, State}} -> ok
         end,

         false = code:purge(tracer_test),
         true = code:delete(tracer_test),
         false = code:purge(tracer_test),
         timer:sleep(10)
     end || _ <- lists:seq(1,15)],

    ok.

reload_loop() ->
    ?MODULE:all(),
    ?MODULE:all(),
    ?MODULE:all(),
    ?MODULE:all(),
    ?MODULE:all(),
    timer:sleep(1),
    reload_loop().

invalid_tracers(_Config) ->
    FailTrace = fun(A) ->
                        try erlang:trace(self(), true, A) of
                            _ -> ct:fail(A)
                        catch _:_ -> ok end
                end,

    FailTrace([{tracer, foobar}, call]),
    FailTrace([{tracer, foobar, []}, call]),
    FailTrace([{tracer, make_ref(), []}, call]),
    FailTrace([{tracer, lists, []}, call]),

    FailTP = fun(MS,FL) ->
                     try erlang:trace_pattern({?MODULE,all,0}, MS, FL) of
                            _ -> ct:fail({MS, FL})
                        catch _:_ -> ok end
                end,

    FailTP([],[{meta, foobar}]),
    FailTP([],[{meta, foobar, []}]),
    FailTP([],[{meta, make_ref(), []}]),
    FailTP([],[{meta, lists, []}]),

    ok.



send(_Config) ->

    Self = self(),
    Tc = fun(Pid) ->
                 Pid ! fun() -> Self ! ok end,
                 receive ok -> ok after 100 -> ct:fail(timeout) end
         end,

    Expect = fun(Pid, State, EOpts) ->
                     receive
                         Msg ->
                             {send, State, Pid, ok, Opts} = Msg,
                             check_opts(EOpts, Opts, Self)
                     end
             end,
    test(send, Tc, Expect).


recv(_Config) ->

    Tc = fun(Pid) ->
                 Pid ! ok
         end,

    Expect = fun(Pid, State, EOpts) ->
                     receive
                         Msg ->
                             {'receive', State, Pid, ok, Opts} = Msg,
                             check_opts(EOpts, Opts)
                     end
             end,

    test('receive', Tc, Expect, false).

call(_Config) ->

    Self = self(),
    Tc = fun(Pid) ->
                 Pid ! fun() -> call_test(Self), Self ! ok end,
                 receive ok -> ok after 100 -> ct:fail(timeout) end
         end,

    erlang:trace_pattern({?MODULE, call_test, 1}, [], [local]),

    Expect = fun(Pid, State, EOpts) ->
                     receive
                         Msg ->
                             {call, State, Pid, {?MODULE, call_test, [Self]}, Opts} = Msg,
                             check_opts(EOpts, Opts)
                     end
             end,
    test(call, Tc, Expect).

call_return(_Config) ->

    Self = self(),
    Tc = fun(Pid) ->
                 Pid ! fun() -> call_test(undefined), Self ! ok end,
                 receive ok -> ok after 100 -> ct:fail(timeout) end
         end,

    1 = erlang:trace_pattern({?MODULE, call_test, 1}, [{'_',[],[{return_trace}]}], [local]),

    Expect = fun(Pid, State, EOpts) ->
                     receive
                         CallMsg ->
                             {call, State, Pid, {?MODULE, call_test, [undefined]}, COpts} = CallMsg,
                             check_opts(EOpts, COpts)
                     end,
                     receive
                         RetMsg ->
                             {return_from, State, Pid, {?MODULE, call_test, 1}, ROpts} = RetMsg,
                             check_opts(EOpts, ROpts, undefined)
                     end
             end,
    test(call, Tc, Expect).

call_test(Arg) ->
    Arg.

spawn(_Config) ->

    Tc = fun(Pid) ->
                 Pid ! fun() -> kill_me(erlang:spawn(lists,seq,[1,10])), ok end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                receive
                    Msg ->
                        {spawn, State, Pid, NewPid, Opts} = Msg,
                        check_opts(EOpts, Opts, {lists,seq,[1,10]}),
                        true = is_pid(NewPid) andalso NewPid /= Pid
                end
             end,

    test(spawn, procs, Tc, Expect, false).

exit(_Config) ->
    Tc = fun(Pid) ->
                 Pid ! fun() -> exit end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                receive
                    Msg ->
                        {exit, State, Pid, normal, Opts} = Msg,
                        check_opts(EOpts, Opts)
                end
             end,

    test(exit, procs, Tc, Expect, true, true).

link(_Config) ->

    Tc = fun(Pid) ->
                 Pid ! fun() ->
                               SPid = erlang:spawn(fun() -> receive _ -> ok end end),
                               erlang:link(SPid),
                               ok
                       end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                receive
                    Msg ->
                        {link, State, Pid, NewPid, Opts} = Msg,
                        check_opts(EOpts, Opts),
                        true = is_pid(NewPid) andalso NewPid /= Pid
                end
             end,

    test(link, procs, Tc, Expect, false).

unlink(_Config) ->

    Tc = fun(Pid) ->
                 Pid ! fun() ->
                               SPid = erlang:spawn(fun() -> receive _ -> ok end end),
                               erlang:link(SPid),
                               erlang:unlink(SPid),
                               kill_me(SPid),
                               ok
                       end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                receive
                    Msg ->
                        {unlink, State, Pid, NewPid, Opts} = Msg,
                        check_opts(EOpts, Opts),
                        true = is_pid(NewPid) andalso NewPid /= Pid
                end
             end,

    test(unlink, procs, Tc, Expect, false).

getting_linked(_Config) ->

    Tc = fun(Pid) ->
                 Pid ! fun() ->
                               Self = self(),
                               erlang:spawn(fun() -> erlang:link(Self) end),
                               ok
                       end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                receive
                    Msg ->
                        {getting_linked, State, Pid, NewPid, Opts} = Msg,
                        check_opts(EOpts, Opts),
                        true = is_pid(NewPid) andalso NewPid /= Pid
                end
             end,

    test(getting_linked, procs, Tc, Expect, false).

getting_unlinked(_Config) ->
    Tc = fun(Pid) ->
                 Pid ! fun() ->
                               Self = self(),
                               erlang:spawn(fun() ->
                                                    erlang:link(Self),
                                                    erlang:unlink(Self)
                                            end),
                               ok
                       end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                receive
                    Msg ->
                        {getting_unlinked, State, Pid, NewPid, Opts} = Msg,
                        check_opts(EOpts, Opts),
                        true = is_pid(NewPid) andalso NewPid /= Pid
                end
             end,

    test(getting_unlinked, procs, Tc, Expect, false).

register(_Config) ->

    Tc = fun(Pid) ->
                 Pid ! fun() ->
                               erlang:register(?MODULE, self()),
                               erlang:unregister(?MODULE),
                               ok
                       end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                receive
                    Msg ->
                        {register, State, Pid, ?MODULE, Opts} = Msg,
                        check_opts(EOpts, Opts)
                end
             end,

    test(register, procs, Tc, Expect, false).

unregister(_Config) ->

    Tc = fun(Pid) ->
                 Pid ! fun() ->
                               erlang:register(?MODULE, self()),
                               erlang:unregister(?MODULE),
                               ok
                       end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                receive
                    Msg ->
                        {unregister, State, Pid, ?MODULE, Opts} = Msg,
                        check_opts(EOpts, Opts)
                end
             end,

    test(unregister, procs, Tc, Expect, false).

in(_Config) ->

    Tc = fun(Pid) ->
                 Self = self(),
                 Pid ! fun() -> receive after 10 -> Self ! ok end end,
                 receive ok -> ok end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                N = (fun F(N) ->
                             receive
                                 Msg ->
                                     {in, State, Pid, _, Opts} = Msg,
                                     check_opts(EOpts, Opts),
                                     F(N+1)
                             after 0 -> N
                             end
                     end)(0),
                true = N > 0
             end,

    test(in, running, Tc, Expect, false).

out(_Config) ->
    Tc = fun(Pid) ->
                 Pid ! fun() -> receive after 10 -> exit end end,
                 Ref = erlang:monitor(process, Pid),
                 receive {'DOWN', Ref, _, _, _} -> ok end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                %% We cannot predict how many out schedules there will be
                N = (fun F(N) ->
                             receive
                                 Msg ->
                                     {out, State, Pid, _, Opts} = Msg,
                                     check_opts(EOpts, Opts),
                                     F(N+1)
                             after 0 -> N
                             end
                     end)(0),
                true = N > 0
             end,

    test(out, running, Tc, Expect, false, true).

gc_start(_Config) ->

    Tc = fun(Pid) ->
                 Pid ! fun() ->
                               erlang:garbage_collect(),
                               ok
                       end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                receive
                    Msg ->
                        {gc_major_start, State, Pid, _, Opts} = Msg,
                        check_opts(EOpts, Opts)
                end
             end,

    test(gc_major_start, garbage_collection, Tc, Expect, false).

gc_end(_Config) ->

    Tc = fun(Pid) ->
                 Pid ! fun() ->
                               erlang:garbage_collect(),
                               ok
                       end
         end,

    Expect =
        fun(Pid, State, EOpts) ->
                receive
                    Msg ->
                        {gc_major_end, State, Pid, _, Opts} = Msg,
                        check_opts(EOpts, Opts)
                end
             end,

    test(gc_major_end, garbage_collection, Tc, Expect, false).

test(Event, Tc, Expect) ->
    test(Event, Tc, Expect, false).
test(Event, Tc, Expect, Removes) ->
    test(Event, Event, Tc, Expect, Removes).
test(Event, TraceFlag, Tc, Expect, Removes) ->
    test(Event, TraceFlag, Tc, Expect, Removes, false).
test(Event, TraceFlag, Tc, Expect, _Removes, Dies) ->

    ComplexState = {fun() -> ok end, <<0:(128*8)>>},
    Opts = #{ },

    %% Test that trace works
    State1 = {#{ Event => trace }, self(), ComplexState},
    Pid1 = start_tracee(),
    1 = erlang:trace(Pid1, true, [TraceFlag, {tracer, tracer_test, State1}]),
    Tc(Pid1),
    ok = trace_delivered(Pid1),

    Expect(Pid1, State1, Opts),
    receive M11 -> ct:fail({unexpected, M11}) after 0 -> ok end,
    if not Dies ->
            {flags, [TraceFlag]} = erlang:trace_info(Pid1, flags),
            {tracer, {tracer_test, State1}} = erlang:trace_info(Pid1, tracer),
            erlang:trace(Pid1, false, [TraceFlag]);
       true -> ok
    end,

    %% Test that trace works with scheduler id and timestamp
    Pid1T = start_tracee(),
    1 = erlang:trace(Pid1T, true, [TraceFlag, {tracer, tracer_test, State1},
                                   timestamp, scheduler_id]),
    Tc(Pid1T),
    ok = trace_delivered(Pid1T),

    Expect(Pid1T, State1, Opts#{ scheduler_id => number,
                                 timestamp => timestamp}),
    receive M11T -> ct:fail({unexpected, M11T}) after 0 -> ok end,
    if not Dies ->
            {flags, [scheduler_id, TraceFlag, timestamp]}
                = erlang:trace_info(Pid1T, flags),
            {tracer, {tracer_test, State1}} = erlang:trace_info(Pid1T, tracer),
            erlang:trace(Pid1T, false, [TraceFlag]);
       true -> ok
    end,

    %% Test that  discard works
    Pid2 = start_tracee(),
    State2 = {#{ Event => discard }, self(), ComplexState},
    1 = erlang:trace(Pid2, true, [TraceFlag, {tracer, tracer_test, State2}]),
    Tc(Pid2),
    ok = trace_delivered(Pid2),
    receive M2 -> ct:fail({unexpected, M2}) after 0 -> ok end,
    if not Dies ->
            {flags, [TraceFlag]} = erlang:trace_info(Pid2, flags),
            {tracer, {tracer_test, State2}} = erlang:trace_info(Pid2, tracer),
            erlang:trace(Pid2, false, [TraceFlag]);
       true ->
            ok
    end,

    ok.

check_opts(E, O, Extra) ->
    check_opts(E#{ extra => Extra }, O).
check_opts(#{ scheduler_id := number } = E, #{ scheduler_id := N } = O)
  when is_integer(N) ->
    E1 = maps:remove(scheduler_id, E),
    O1 = maps:remove(scheduler_id, O),
    if E1 == O1 -> ok;
       true -> ct:fail({invalid_opts, E, O})
    end;
check_opts(Opts, Opts) ->
    ok;
check_opts(E,O) ->
    ct:fail({invalid_opts, E, O}).

start_tracee() ->
    spawn_link(
      fun F() ->
              receive
                  Action when is_function(Action) ->
                      case Action() of
                          ok ->
                              F();
                          Err ->
                              Err
                      end;
                  _ ->
                      F()
              end
      end).

trace_delivered(Pid) ->
    Ref = erlang:trace_delivered(Pid),
    receive
        {trace_delivered, Pid, Ref} ->
            ok
    after 1000 ->
            timeout
    end.

purge() ->
    %% Make sure module is not loaded
    case erlang:module_loaded(tracer_test) of
        true ->
            code:purge(tracer_test),
            true = code:delete(tracer_test),
            code:purge(tracer_test);
        _ ->
            ok
    end.