1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(tracer_SUITE).
22
23%%%
24%%% Tests the tracer module interface
25%%%
26
27-export([all/0, suite/0,groups/0, init_per_suite/1, end_per_suite/1,
28	 init_per_group/2,end_per_group/2, init_per_testcase/2,
29         end_per_testcase/2]).
30-export([load/1, unload/1, reload/1, invalid_tracers/1]).
31-export([send/1, recv/1, call/1, call_return/1, spawn/1, exit/1,
32         link/1, unlink/1, getting_linked/1, getting_unlinked/1,
33         register/1, unregister/1, in/1, out/1, gc_start/1, gc_end/1,
34         seq_trace/1]).
35
36suite() -> [{ct_hooks,[ts_install_cth]},
37            {timetrap, {minutes, 1}}].
38
39all() ->
40    [load, unload, reload, invalid_tracers, {group, basic}].
41
42groups() ->
43    [{ basic, [], [send, recv, call, call_return, spawn, exit,
44                   link, unlink, getting_linked, getting_unlinked,
45                   register, unregister, in, out, gc_start, gc_end,
46                   seq_trace]}].
47
48init_per_suite(Config) ->
49    erlang:trace_pattern({'_','_','_'}, false, [local]),
50    erlang:trace_pattern({'_','_','_'}, false, []),
51    purge(),
52    Config.
53
54end_per_suite(_Config) ->
55    ok.
56
57init_per_group(_GroupName, Config) ->
58    Config.
59
60end_per_group(_GroupName, Config) ->
61    Config.
62
63init_per_testcase(TC, Config) when TC =:= load; TC =:= reload ->
64
65    DataDir = proplists:get_value(data_dir, Config),
66
67    Pid = erlang:spawn(fun F() ->
68                               receive
69                                   {get, Pid} ->
70                                       Pid ! DataDir,
71                                       F()
72                               end
73                       end),
74    register(tracer_test_config, Pid),
75    common_init_per_testcase(Config);
76init_per_testcase(_, Config) ->
77    DataDir = proplists:get_value(data_dir, Config),
78    case catch tracer_test:enabled(trace_status, self(), self()) of
79        discard ->
80            ok;
81        _ ->
82            tracer_test:load(DataDir)
83    end,
84    common_init_per_testcase(Config).
85
86common_init_per_testcase(Config) ->
87    Killer = erlang:spawn(fun() -> killer_loop([]) end),
88    register(killer_process, Killer),
89    Config.
90
91end_per_testcase(TC, _Config) when TC =:= load; TC =:= reload ->
92    purge(),
93    exit(whereis(tracer_test_config), kill),
94    kill_processes();
95end_per_testcase(_, _Config) ->
96    purge(),
97    kill_processes().
98
99kill_processes() ->
100    killer_process ! {get_pids,self()},
101    receive
102        {pids_to_kill,Pids} -> ok
103    end,
104    _ = [begin
105             case erlang:is_process_alive(P) of
106                 true ->
107                     io:format("Killing ~p\n", [P]);
108                 false ->
109                     ok
110             end,
111             erlang:unlink(P),
112             exit(P, kill)
113         end || P <- Pids],
114    ok.
115
116killer_loop(Pids) ->
117    receive
118        {add_pid,Pid} ->
119            killer_loop([Pid|Pids]);
120        {get_pids,To} ->
121            To ! {pids_to_kill,Pids}
122    end.
123
124kill_me(Pid) ->
125    killer_process ! {add_pid,Pid},
126    Pid.
127
128%%% Test cases follow.
129
130load(_Config) ->
131    purge(),
132    1 = erlang:trace(self(), true, [{tracer, tracer_test, []}, call]),
133    purge(),
134    1 = erlang:trace_pattern({?MODULE, all, 0}, [],
135                             [{meta, tracer_test, []}]),
136    ok.
137
138unload(_Config) ->
139
140    ServerFun = fun F(0, undefined) ->
141                        receive
142                            {N, Pid} -> F(N, Pid)
143                        end;
144                    F(0, Pid) ->
145                        Pid ! done,
146                        F(0, undefined);
147                    F(N, Pid) ->
148                        ?MODULE:all(),
149                        F(N-1, Pid)
150                end,
151
152    Pid = erlang:spawn_link(fun() -> ServerFun(0, undefined) end),
153
154    Tc = fun(N) ->
155                 Pid ! {N, self()},
156                 receive done -> ok after 1000 -> ct:fail(timeout) end,
157                 trace_delivered(Pid)
158         end,
159
160    1 = erlang:trace(Pid, true, [{tracer, tracer_test,
161                                  {#{ call => trace }, self(), []}},
162                                 call]),
163    1 = erlang:trace_pattern({?MODULE, all, 0}, [], []),
164
165    Tc(1),
166    receive _M -> ok after 0 -> ct:fail(timeout) end,
167    receive M0 -> ct:fail({unexpected_message0, M0}) after 0 -> ok end,
168
169    code:purge(tracer_test),
170    code:delete(tracer_test),
171
172    Tc(1),
173    receive M1 -> ct:fail({unexpected_message1, M1}) after 0 -> ok end,
174
175    code:purge(tracer_test),
176
177    Tc(1),
178    receive M2 -> ct:fail({unexpected_message2, M2}) after 0 -> ok end,
179
180    ok.
181
182%% This testcase is here to make sure there are not
183%% segfaults when reloading the current nifs.
184reload(_Config) ->
185
186    Tracer = spawn_opt(fun F() -> receive _M -> F() end end,
187                       [{message_queue_data, off_heap}]),
188    erlang:link(Tracer),
189    Tracee = spawn_link(fun reload_loop/0),
190
191    [begin
192         Ref = make_ref(),
193         State = {#{ call => trace }, Tracer, [Ref]},
194         erlang:trace(Tracee, true, [{tracer, tracer_test,State}, call]),
195         erlang:trace_pattern({?MODULE, all, 0}, []),
196
197         false = code:purge(tracer_test),
198         {module, _} = code:load_file(tracer_test),
199
200         %% There is a race involved in between when the internal nif cache
201         %% is purged and when the reload_loop needs the tracer module
202         %% so the tracer may be removed or still there.
203         case erlang:trace_info(Tracee, tracer) of
204             {tracer, []} -> ok;
205             {tracer, {tracer_test, State}} -> ok
206         end,
207
208         false = code:purge(tracer_test),
209         true = code:delete(tracer_test),
210         false = code:purge(tracer_test),
211         timer:sleep(10)
212     end || _ <- lists:seq(1,15)],
213
214    ok.
215
216reload_loop() ->
217    ?MODULE:all(),
218    ?MODULE:all(),
219    ?MODULE:all(),
220    ?MODULE:all(),
221    ?MODULE:all(),
222    timer:sleep(1),
223    reload_loop().
224
225invalid_tracers(_Config) ->
226    FailTrace = fun(A) ->
227                        try erlang:trace(self(), true, A) of
228                            _ -> ct:fail(A)
229                        catch _:_ -> ok end
230                end,
231
232    FailTrace([{tracer, foobar}, call]),
233    FailTrace([{tracer, foobar, []}, call]),
234    FailTrace([{tracer, make_ref(), []}, call]),
235    FailTrace([{tracer, lists, []}, call]),
236
237    FailTP = fun(MS,FL) ->
238                     try erlang:trace_pattern({?MODULE,all,0}, MS, FL) of
239                            _ -> ct:fail({MS, FL})
240                        catch _:_ -> ok end
241                end,
242
243    FailTP([],[{meta, foobar}]),
244    FailTP([],[{meta, foobar, []}]),
245    FailTP([],[{meta, make_ref(), []}]),
246    FailTP([],[{meta, lists, []}]),
247
248    ok.
249
250
251
252send(_Config) ->
253
254    Self = self(),
255    Tc = fun(Pid) ->
256                 Pid ! fun() -> Self ! ok end,
257                 receive ok -> ok after 100 -> ct:fail(timeout) end
258         end,
259
260    Expect = fun(Pid, State, EOpts) ->
261                     receive
262                         Msg ->
263                             {send, State, Pid, ok, Opts} = Msg,
264                             check_opts(EOpts, Opts, Self)
265                     end
266             end,
267    test(send, Tc, Expect).
268
269
270recv(_Config) ->
271
272    Tc = fun(Pid) ->
273                 Pid ! ok
274         end,
275
276    Expect = fun(Pid, State, EOpts) ->
277                     receive
278                         Msg ->
279                             {'receive', State, Pid, ok, Opts} = Msg,
280                             check_opts(EOpts, Opts)
281                     end
282             end,
283
284    test('receive', Tc, Expect, false).
285
286call(_Config) ->
287
288    Self = self(),
289    Tc = fun(Pid) ->
290                 Pid ! fun() -> call_test(Self), Self ! ok end,
291                 receive ok -> ok after 100 -> ct:fail(timeout) end
292         end,
293
294    erlang:trace_pattern({?MODULE, call_test, 1}, [], [local]),
295
296    Expect = fun(Pid, State, EOpts) ->
297                     receive
298                         Msg ->
299                             {call, State, Pid, {?MODULE, call_test, [Self]}, Opts} = Msg,
300                             check_opts(EOpts, Opts)
301                     end
302             end,
303    test(call, Tc, Expect).
304
305call_return(_Config) ->
306
307    Self = self(),
308    Tc = fun(Pid) ->
309                 Pid ! fun() -> call_test(undefined), Self ! ok end,
310                 receive ok -> ok after 100 -> ct:fail(timeout) end
311         end,
312
313    1 = erlang:trace_pattern({?MODULE, call_test, 1}, [{'_',[],[{return_trace}]}], [local]),
314
315    Expect = fun(Pid, State, EOpts) ->
316                     receive
317                         CallMsg ->
318                             {call, State, Pid, {?MODULE, call_test, [undefined]}, COpts} = CallMsg,
319                             check_opts(EOpts, COpts)
320                     end,
321                     receive
322                         RetMsg ->
323                             {return_from, State, Pid, {?MODULE, call_test, 1}, ROpts} = RetMsg,
324                             check_opts(EOpts, ROpts, undefined)
325                     end
326             end,
327    test(call, Tc, Expect).
328
329call_test(Arg) ->
330    Arg.
331
332spawn(_Config) ->
333
334    Tc = fun(Pid) ->
335                 Pid ! fun() -> kill_me(erlang:spawn(lists,seq,[1,10])), ok end
336         end,
337
338    Expect =
339        fun(Pid, State, EOpts) ->
340                receive
341                    Msg ->
342                        {spawn, State, Pid, NewPid, Opts} = Msg,
343                        check_opts(EOpts, Opts, {lists,seq,[1,10]}),
344                        true = is_pid(NewPid) andalso NewPid /= Pid
345                end
346             end,
347
348    test(spawn, procs, Tc, Expect, false).
349
350exit(_Config) ->
351    Tc = fun(Pid) ->
352                 Pid ! fun() -> exit end
353         end,
354
355    Expect =
356        fun(Pid, State, EOpts) ->
357                receive
358                    Msg ->
359                        {exit, State, Pid, normal, Opts} = Msg,
360                        check_opts(EOpts, Opts)
361                end
362             end,
363
364    test(exit, procs, Tc, Expect, true, true).
365
366link(_Config) ->
367
368    Tc = fun(Pid) ->
369                 Pid ! fun() ->
370                               SPid = erlang:spawn(fun() -> receive _ -> ok end end),
371                               erlang:link(SPid),
372                               ok
373                       end
374         end,
375
376    Expect =
377        fun(Pid, State, EOpts) ->
378                receive
379                    Msg ->
380                        {link, State, Pid, NewPid, Opts} = Msg,
381                        check_opts(EOpts, Opts),
382                        true = is_pid(NewPid) andalso NewPid /= Pid
383                end
384             end,
385
386    test(link, procs, Tc, Expect, false).
387
388unlink(_Config) ->
389
390    Tc = fun(Pid) ->
391                 Pid ! fun() ->
392                               SPid = erlang:spawn(fun() -> receive _ -> ok end end),
393                               erlang:link(SPid),
394                               erlang:unlink(SPid),
395                               kill_me(SPid),
396                               ok
397                       end
398         end,
399
400    Expect =
401        fun(Pid, State, EOpts) ->
402                receive
403                    Msg ->
404                        {unlink, State, Pid, NewPid, Opts} = Msg,
405                        check_opts(EOpts, Opts),
406                        true = is_pid(NewPid) andalso NewPid /= Pid
407                end
408             end,
409
410    test(unlink, procs, Tc, Expect, false).
411
412getting_linked(_Config) ->
413
414    Tc = fun(Pid) ->
415                 Pid ! fun() ->
416                               Self = self(),
417                               erlang:spawn(fun() -> erlang:link(Self) end),
418                               ok
419                       end
420         end,
421
422    Expect =
423        fun(Pid, State, EOpts) ->
424                receive
425                    Msg ->
426                        {getting_linked, State, Pid, NewPid, Opts} = Msg,
427                        check_opts(EOpts, Opts),
428                        true = is_pid(NewPid) andalso NewPid /= Pid
429                end
430             end,
431
432    test(getting_linked, procs, Tc, Expect, false).
433
434getting_unlinked(_Config) ->
435    Tc = fun(Pid) ->
436                 Pid ! fun() ->
437                               Self = self(),
438                               erlang:spawn(fun() ->
439                                                    erlang:link(Self),
440                                                    erlang:unlink(Self)
441                                            end),
442                               ok
443                       end
444         end,
445
446    Expect =
447        fun(Pid, State, EOpts) ->
448                receive
449                    Msg ->
450                        {getting_unlinked, State, Pid, NewPid, Opts} = Msg,
451                        check_opts(EOpts, Opts),
452                        true = is_pid(NewPid) andalso NewPid /= Pid
453                end
454             end,
455
456    test(getting_unlinked, procs, Tc, Expect, false).
457
458register(_Config) ->
459
460    Tc = fun(Pid) ->
461                 Pid ! fun() ->
462                               erlang:register(?MODULE, self()),
463                               erlang:unregister(?MODULE),
464                               ok
465                       end
466         end,
467
468    Expect =
469        fun(Pid, State, EOpts) ->
470                receive
471                    Msg ->
472                        {register, State, Pid, ?MODULE, Opts} = Msg,
473                        check_opts(EOpts, Opts)
474                end
475             end,
476
477    test(register, procs, Tc, Expect, false).
478
479unregister(_Config) ->
480
481    Tc = fun(Pid) ->
482                 Pid ! fun() ->
483                               erlang:register(?MODULE, self()),
484                               erlang:unregister(?MODULE),
485                               ok
486                       end
487         end,
488
489    Expect =
490        fun(Pid, State, EOpts) ->
491                receive
492                    Msg ->
493                        {unregister, State, Pid, ?MODULE, Opts} = Msg,
494                        check_opts(EOpts, Opts)
495                end
496             end,
497
498    test(unregister, procs, Tc, Expect, false).
499
500in(_Config) ->
501
502    Tc = fun(Pid) ->
503                 Self = self(),
504                 Pid ! fun() -> receive after 10 -> Self ! ok end end,
505                 receive ok -> ok end
506         end,
507
508    Expect =
509        fun(Pid, State, EOpts) ->
510                N = (fun F(N) ->
511                             receive
512                                 Msg ->
513                                     {in, State, Pid, _, Opts} = Msg,
514                                     check_opts(EOpts, Opts),
515                                     F(N+1)
516                             after 0 -> N
517                             end
518                     end)(0),
519                true = N > 0
520             end,
521
522    test(in, running, Tc, Expect, false).
523
524out(_Config) ->
525    Tc = fun(Pid) ->
526                 Pid ! fun() -> receive after 10 -> exit end end,
527                 Ref = erlang:monitor(process, Pid),
528                 receive {'DOWN', Ref, _, _, _} -> ok end
529         end,
530
531    Expect =
532        fun(Pid, State, EOpts) ->
533                %% We cannot predict how many out schedules there will be
534                N = (fun F(N) ->
535                             receive
536                                 Msg ->
537                                     {out, State, Pid, _, Opts} = Msg,
538                                     check_opts(EOpts, Opts),
539                                     F(N+1)
540                             after 0 -> N
541                             end
542                     end)(0),
543                true = N > 0
544             end,
545
546    test(out, running, Tc, Expect, false, true).
547
548gc_start(_Config) ->
549
550    Tc = fun(Pid) ->
551                 Pid ! fun() ->
552                               erlang:garbage_collect(),
553                               ok
554                       end
555         end,
556
557    Expect =
558        fun(Pid, State, EOpts) ->
559                receive
560                    Msg ->
561                        {gc_major_start, State, Pid, _, Opts} = Msg,
562                        check_opts(EOpts, Opts)
563                end
564             end,
565
566    test(gc_major_start, garbage_collection, Tc, Expect, false).
567
568gc_end(_Config) ->
569
570    Tc = fun(Pid) ->
571                 Pid ! fun() ->
572                               erlang:garbage_collect(),
573                               ok
574                       end
575         end,
576
577    Expect =
578        fun(Pid, State, EOpts) ->
579                receive
580                    Msg ->
581                        {gc_major_end, State, Pid, _, Opts} = Msg,
582                        check_opts(EOpts, Opts)
583                end
584             end,
585
586    test(gc_major_end, garbage_collection, Tc, Expect, false).
587
588seq_trace(_Config) ->
589
590    seq_trace:set_system_tracer({tracer_test,
591                                 {#{ seq_trace => trace }, self(), []}}),
592    erlang:spawn(fun() ->
593                         seq_trace:set_token(label,17),
594                         seq_trace:set_token(print,true),
595                         seq_trace:print(17,"**** Trace Started ****")
596                 end),
597    receive
598        {seq_trace, _, 17, {print, _, _, _, _}, _} ->
599            ok;
600        M ->
601            ct:fail("~p~n",[M])
602    after 100 ->
603            ct:fail(timeout)
604    end.
605
606test(Event, Tc, Expect) ->
607    test(Event, Tc, Expect, false).
608test(Event, Tc, Expect, Removes) ->
609    test(Event, Event, Tc, Expect, Removes).
610test(Event, TraceFlag, Tc, Expect, Removes) ->
611    test(Event, TraceFlag, Tc, Expect, Removes, false).
612test(Event, TraceFlag, Tc, Expect, _Removes, Dies) ->
613
614    ComplexState = {fun() -> ok end, <<0:(128*8)>>},
615    Opts = #{ },
616
617    %% Test that trace works
618    State1 = {#{ Event => trace }, self(), ComplexState},
619    Pid1 = start_tracee(),
620    1 = erlang:trace(Pid1, true, [TraceFlag, {tracer, tracer_test, State1}]),
621    Tc(Pid1),
622    ok = trace_delivered(Pid1),
623
624    Expect(Pid1, State1, Opts),
625    receive M11 -> ct:fail({unexpected, M11}) after 0 -> ok end,
626    if not Dies andalso Event /= in ->
627            {flags, [TraceFlag]} = erlang:trace_info(Pid1, flags),
628            {tracer, {tracer_test, State1}} = erlang:trace_info(Pid1, tracer),
629            erlang:trace(Pid1, false, [TraceFlag]);
630       true -> ok
631    end,
632
633    %% Test that trace works with scheduler id and timestamp
634    Pid1T = start_tracee(),
635    1 = erlang:trace(Pid1T, true, [TraceFlag, {tracer, tracer_test, State1},
636                                   timestamp, scheduler_id]),
637    Tc(Pid1T),
638    ok = trace_delivered(Pid1T),
639
640    Expect(Pid1T, State1, Opts#{ scheduler_id => number,
641                                 timestamp => timestamp}),
642    receive M11T -> ct:fail({unexpected, M11T}) after 0 -> ok end,
643    if not Dies andalso Event /= in ->
644            {flags, [scheduler_id, TraceFlag, timestamp]}
645                = erlang:trace_info(Pid1T, flags),
646            {tracer, {tracer_test, State1}} = erlang:trace_info(Pid1T, tracer),
647            erlang:trace(Pid1T, false, [TraceFlag]);
648       true -> ok
649    end,
650
651    %% Test that  discard works
652    Pid2 = start_tracee(),
653    State2 = {#{ Event => discard }, self(), ComplexState},
654    1 = erlang:trace(Pid2, true, [TraceFlag, {tracer, tracer_test, State2}]),
655    Tc(Pid2),
656    ok = trace_delivered(Pid2),
657    receive M2 -> ct:fail({unexpected, M2}) after 0 -> ok end,
658    if not Dies andalso Event /= in ->
659            {flags, [TraceFlag]} = erlang:trace_info(Pid2, flags),
660            {tracer, {tracer_test, State2}} = erlang:trace_info(Pid2, tracer),
661            erlang:trace(Pid2, false, [TraceFlag]);
662       true ->
663            ok
664    end,
665
666    ok.
667
668check_opts(E, O, Extra) ->
669    check_opts(E#{ extra => Extra }, O).
670check_opts(#{ scheduler_id := number } = E, #{ scheduler_id := N } = O)
671  when is_integer(N) ->
672    E1 = maps:remove(scheduler_id, E),
673    O1 = maps:remove(scheduler_id, O),
674    if E1 == O1 -> ok;
675       true -> ct:fail({invalid_opts, E, O})
676    end;
677check_opts(Opts, Opts) ->
678    ok;
679check_opts(E,O) ->
680    ct:fail({invalid_opts, E, O}).
681
682start_tracee() ->
683    spawn_link(
684      fun F() ->
685              receive
686                  Action when is_function(Action) ->
687                      case Action() of
688                          ok ->
689                              F();
690                          Err ->
691                              Err
692                      end;
693                  _ ->
694                      F()
695              end
696      end).
697
698trace_delivered(Pid) ->
699    Ref = erlang:trace_delivered(Pid),
700    receive
701        {trace_delivered, Pid, Ref} ->
702            ok
703    after 1000 ->
704            timeout
705    end.
706
707purge() ->
708    %% Make sure module is not loaded
709    case erlang:module_loaded(tracer_test) of
710        true ->
711            code:purge(tracer_test),
712            true = code:delete(tracer_test),
713            code:purge(tracer_test);
714        _ ->
715            ok
716    end.
717