<commit_before>%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2013. 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, 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, spawn, exit, link, unlink, getting_linked,
                   getting_unlinked, register, unregister, in, out,
                   gc_start, gc_end]}].

init_per_suite(Config) ->
    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),
    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,
    Config.

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

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 _ -> ok after 0 -> ct:fail(timeout) end,

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

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

    code:purge(tracer_test),

    Tc(1),
    receive M2 -> ct:fail({unexpected_message, 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_link(fun F() -> receive _M -> F() end end),
    Tracee = spawn_link(fun F() -> ?MODULE:all(), F() end),


    [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)
     end || _ <- lists:seq(1,15)],

    ok.

invalid_tracers(_Config) ->
    FailTrace = fun(A) ->
                        try erlang:trace(self(), true, A) of
                            _ -> ct:fail(A)
                        catch _:_ -> ok end
                end,
<commit_msg>erts: Optimize tracer reload test<commit_after>%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2013. 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, 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, spawn, exit, link, unlink, getting_linked,
                   getting_unlinked, register, unregister, in, out,
                   gc_start, gc_end]}].

init_per_suite(Config) ->
    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),
    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,
    Config.

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

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 _ -> ok after 0 -> ct:fail(timeout) end,

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

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

    code:purge(tracer_test),

    Tc(1),
    receive M2 -> ct:fail({unexpected_message, 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)
     end || _ <- lists:seq(1,15)],

    ok.

reload_loop() ->
    ?MODULE:all(),
    reload_loop().

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

{'fraction_non_alphanumeric': 0.13753925734343248, 'fraction_numerical': 0.012839460557916129, 'mean_word_length': 1.934941718622933, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5983169', 'n_tokens_mistral': 3559, 'n_tokens_neox': 3357, 'n_words': 863}