1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2007-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(sensitive_SUITE).
22
23-include_lib("common_test/include/ct.hrl").
24
25-export([all/0, suite/0,
26         stickiness/1,send_trace/1,recv_trace/1,proc_trace/1,call_trace/1,
27         meta_trace/1,running_trace/1,gc_trace/1,seq_trace/1,
28         t_process_info/1,t_process_display/1,save_calls/1]).
29
30-export([remote_process_display/0,an_exported_function/1]).
31
32-import(lists, [keysearch/3,foreach/2,sort/1]).
33
34suite() ->
35    [{ct_hooks,[ts_install_cth]},
36     {timetrap, {minutes, 5}}].
37
38all() ->
39    [stickiness, send_trace, recv_trace, proc_trace,
40     call_trace, meta_trace, running_trace, gc_trace,
41     seq_trace, t_process_info, t_process_display,
42     save_calls].
43
44stickiness(Config) when is_list(Config) ->
45    {Tracer,Mref} = spawn_monitor(fun() ->
46                                          receive after infinity -> ok end
47                                  end),
48    false = process_flag(sensitive, true),
49    put(foo, bar),
50
51    Flags = sort([send,'receive',procs,call,running,garbage_collection,
52                  set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link]),
53    foreach(fun(F) ->
54                    1 = erlang:trace(self(), true, [F,{tracer,Tracer}])
55            end, Flags),
56    foreach(fun(F) ->
57                    1 = erlang:trace(self(), false, [F,{tracer,Tracer}])
58            end, Flags),
59    1 = erlang:trace(self(), true, [{tracer,Tracer}|Flags]),
60    1 = erlang:trace(self(), false, [{tracer,Tracer}|Flags]),
61
62    {messages,[]} = process_info(Tracer, messages),
63    exit(Tracer, kill),
64    receive {'DOWN',Mref,_,_,_} -> ok end,
65
66    case process_info(self(), dictionary) of
67        {dictionary,[]} -> ok;
68        {dictionary,_} -> ct:fail(sensitive_flag_cleared)
69    end,
70
71    NewTracer = spawn_link(fun() -> receive after infinity -> ok end end),
72    1 = erlang:trace(self(), true, [{tracer,NewTracer}|Flags]),
73    Flags = sort(element(2, erlang:trace_info(self(), flags))),
74    {tracer,NewTracer} = erlang:trace_info(self(), tracer),
75
76    %% Process still sensitive. Tracer should disappear when we clear
77    %% all trace flags.
78    1 = erlang:trace(self(), false, [{tracer,NewTracer}|Flags]),
79    {tracer,[]} = erlang:trace_info(self(), tracer),
80
81    unlink(NewTracer), exit(NewTracer, kill),
82    ok.
83
84send_trace(Config) when is_list(Config) ->
85    {Dead,Mref} = spawn_monitor(fun() -> ok end),
86    receive {'DOWN',Mref,_,_,_} -> ok end,
87    Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
88    Sink = spawn_link(fun() -> receive after infinity -> ok end end),
89    Self = self(),
90
91    1 = erlang:trace(self(), true, [send,{tracer,Tracer}]),
92    Dead ! before,
93    Sink ! before,
94    false = process_flag(sensitive, true),
95    Sink ! {blurf,lists:seq(1, 50)},
96    true = process_flag(sensitive, true),
97    Sink ! lists:seq(1, 100),
98    Dead ! forget_me,
99    true = process_flag(sensitive, false),
100    Sink ! after1,
101    false = process_flag(sensitive, false),
102    Sink ! after2,
103    Dead ! after2,
104    wait_trace(Self),
105
106    {messages,Messages} = process_info(Tracer, messages),
107    [{trace,Self,send_to_non_existing_process,before,Dead},
108     {trace,Self,send,before,Sink},
109     {trace,Self,send,after1,Sink},
110     {trace,Self,send,after2,Sink},
111     {trace,Self,send_to_non_existing_process,after2,Dead}] = Messages,
112
113    unlink(Tracer), exit(Tracer, kill),
114    unlink(Sink), exit(Sink, kill),
115    ok.
116
117recv_trace(Config) when is_list(Config) ->
118    Parent = self(),
119    Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
120    Sender = spawn_link(fun() -> recv_trace_sender(Parent) end),
121
122    1 = erlang:trace(self(), true, ['receive',{tracer,Tracer}]),
123
124    Sender ! 1,
125    receive a -> wait_trace(Sender) end,
126
127    false = process_flag(sensitive, true),
128
129    Sender ! 2,
130    receive {b,[x,y,z]} -> wait_trace(Sender) end,
131
132    true = process_flag(sensitive, false),
133
134    Sender ! 3,
135    receive c -> wait_trace(Sender) end,
136
137    {messages,Messages} = process_info(Tracer, messages),
138    [{trace,Parent,'receive',a},
139     {trace,Parent,'receive',{trace_delivered,_,_}},
140     {trace,Parent,'receive',c}|_] = Messages,
141
142    unlink(Tracer), exit(Tracer, kill),
143    unlink(Sender), exit(Sender, kill),
144    ok.
145
146recv_trace_sender(Pid) ->
147    receive
148        1 -> Pid ! a;
149        2 -> Pid ! {b,[x,y,z]};
150        3 -> Pid ! c
151    end,
152    recv_trace_sender(Pid).
153
154proc_trace(Config) when is_list(Config) ->
155    Self = self(),
156    Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
157
158    1 = erlang:trace(self(), true, [procs,{tracer,Tracer}]),
159    false = process_flag(sensitive, true),
160
161    spawn(fun() -> ok end),
162    register(nisse, self()),
163    unregister(nisse),
164    link(Tracer),
165    unlink(Tracer),
166    Linker0 = spawn_link(fun() -> ok end),
167    Mref0 = erlang:monitor(process, Linker0),
168
169    {_,Mref} = spawn_monitor(fun() -> link(Self), unlink(Self) end),
170
171    receive {'DOWN',Mref0,_,_,_} -> ok end,
172
173    receive {'DOWN',Mref,_,_,_} -> ok end,
174
175    true = process_flag(sensitive, false),
176
177    Dead = spawn(fun() -> ok end),
178    register(arne, self()),
179    unregister(arne),
180    {Linker,Mref2} = spawn_monitor(fun() -> link(Self), unlink(Self) end),
181    receive {'DOWN',Mref2,_,_,_} -> ok end,
182    Last = spawn_link(fun() -> ok end),
183    receive after 10 -> ok end,
184    wait_trace(all),
185    {messages,Messages} = process_info(Tracer, messages),
186    [{trace,Self,spawn,Dead,{erlang,apply,_}},
187     {trace,Self,register,arne},
188     {trace,Self,unregister,arne},
189     {trace,Self,spawn,Linker,_},
190     {trace,Self,getting_linked,Linker},
191     {trace,Self,getting_unlinked,Linker},
192     {trace,Self,spawn,Last,_},
193     {trace,Self,link,Last},
194     {trace,Self,getting_unlinked,Last}] = Messages,
195
196    unlink(Tracer), exit(Tracer, kill),
197    ok.
198
199call_trace(Config) when is_list(Config) ->
200    Self = self(),
201    Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
202
203    1 = erlang:trace(self(), true, [call,{tracer,Tracer}]),
204    1 = erlang:trace_pattern({?MODULE,an_exported_function,1},
205                             true, [global]),
206    1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [global]),
207    1 = erlang:trace_pattern({erlang,binary_to_list,1}, true, [local]),
208    Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [local]),
209
210    false = process_flag(sensitive, true),
211    {ok,42} = a_local_function(42),
212    7 = an_exported_function(6),
213    <<7,8,9,10>> = list_to_binary(id([7,8,9,10])),
214    [42,43] = binary_to_list(id(<<42,43>>)),
215    true = process_flag(sensitive, false),
216
217    {ok,{a,b}} = a_local_function({a,b}),
218    1 = an_exported_function(0),
219    <<1,2,3>> = list_to_binary(id([1,2,3])),
220    [42,43,44] = binary_to_list(id(<<42,43,44>>)),
221
222    wait_trace(Self),
223
224    {messages,Messages} = process_info(Tracer, messages),
225    [{trace,Self,call,{?MODULE,a_local_function,[{a,b}]}},
226     {trace,Self,call,{?MODULE,an_exported_function,[0]}},
227     {trace,Self,call,{?MODULE,id,[_]}},
228     {trace,Self,call,{erlang,list_to_binary,[[1,2,3]]}},
229     {trace,Self,call,{sensitive_SUITE,id,[<<42,43,44>>]}},
230     {trace,Self,call,{erlang,binary_to_list,[<<42,43,44>>]}},
231     {trace,Self,call,{?MODULE,wait_trace,[Self]}}] = Messages,
232
233    Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [local]),
234    erlang:trace_pattern({erlang,'_','_'}, false, [local]),
235    erlang:trace_pattern({'_','_','_'}, false, [global]),
236
237    unlink(Tracer), exit(Tracer, kill),
238    ok.
239
240meta_trace(Config) when is_list(Config) ->
241    Self = self(),
242    Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
243
244    Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [{meta,Tracer}]),
245    1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [{meta,Tracer}]),
246
247    false = process_flag(sensitive, true),
248    {ok,blurf} = a_local_function(blurf),
249    100 = an_exported_function(99),
250    <<8,9,10>> = list_to_binary(id([8,9,10])),
251    true = process_flag(sensitive, false),
252
253    {ok,{x,y}} = a_local_function({x,y}),
254    1 = an_exported_function(0),
255    <<10>> = list_to_binary(id([10])),
256    wait_trace(Self),
257
258    Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [meta]),
259    1 = erlang:trace_pattern({erlang,list_to_binary,1}, false, [meta]),
260    a_local_function(0),
261
262    {messages,Messages} = process_info(Tracer, messages),
263    [{trace_ts,Self,call,{?MODULE,a_local_function,[{x,y}]},{_,_,_}},
264     {trace_ts,Self,call,{?MODULE,an_exported_function,[0]},{_,_,_}},
265     {trace_ts,Self,call,{?MODULE,id,[_]},{_,_,_}},
266     {trace_ts,Self,call,{erlang,list_to_binary,[[10]]},{_,_,_}},
267     {trace_ts,Self,call,{?MODULE,wait_trace,[Self]},{_,_,_}}] = Messages,
268
269    unlink(Tracer), exit(Tracer, kill),
270    ok.
271
272a_local_function(A) ->
273    {ok,A}.
274
275an_exported_function(X) ->
276    X+1.
277
278running_trace(Config) when is_list(Config) ->
279    Self = self(),
280    Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
281
282    false = process_flag(sensitive, true),
283    1 = erlang:trace(Self, true, [running,{tracer,Tracer}]),
284    erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(),
285    erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(),
286    true = process_flag(sensitive, false),
287    erlang:yield(),
288    1 = erlang:trace(Self, false, [running,{tracer,Tracer}]),
289
290    wait_trace(Self),
291    {messages,Messages} = process_info(Tracer, messages),
292    [{trace,Self,out,{sensitive_SUITE,running_trace,1}},
293     {trace,Self,in,{sensitive_SUITE,running_trace,1}}] = Messages,
294
295    unlink(Tracer), exit(Tracer, kill),
296    ok.
297
298gc_trace(Config) when is_list(Config) ->
299    Self = self(),
300    Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
301
302    false = process_flag(sensitive, true),
303    1 = erlang:trace(Self, true, [garbage_collection,{tracer,Tracer}]),
304    erlang:garbage_collect(), erlang:garbage_collect(),
305    erlang:garbage_collect(), erlang:garbage_collect(),
306    erlang:garbage_collect(), erlang:garbage_collect(),
307    erlang:garbage_collect(), erlang:garbage_collect(),
308    true = process_flag(sensitive, false),
309    erlang:garbage_collect(),
310    1 = erlang:trace(Self, false, [garbage_collection,{tracer,Tracer}]),
311
312    wait_trace(Self),
313    {messages,Messages} = process_info(Tracer, messages),
314    [{trace,Self,gc_major_start,_},{trace,Self,gc_major_end,_}] = Messages,
315
316    unlink(Tracer), exit(Tracer, kill),
317    ok.
318
319seq_trace(Config) when is_list(Config) ->
320    Self = self(),
321    Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
322    seq_trace:set_system_tracer(Tracer),
323
324    false = process_flag(sensitive, true),
325
326    Echo = spawn_link(fun() ->
327                              receive
328                                  {Pid,Message} ->
329                                      Pid ! {reply,Message}
330                              end
331                      end),
332    Sender = spawn_link(fun() ->
333                                seq_trace:set_token(label, 42),
334                                seq_trace:set_token('receive', true),
335                                seq_trace:set_token(send, true),
336                                seq_trace:set_token(print, true),
337                                seq_trace:print(42, "trace started"),
338                                Self ! blurf
339                        end),
340    seq_trace:set_token(label, 17),
341    seq_trace:set_token('receive', true),
342    seq_trace:set_token(send, true),
343    seq_trace:set_token(print, true),
344    seq_trace:print(17, "trace started"),
345    Echo ! {Self,hello},
346    receive {reply,hello} -> ok end,
347    receive blurf -> ok end,
348
349    wait_trace(all),
350
351    {messages,Messages} = process_info(Tracer, messages),
352    [{seq_trace,17,{'receive',{0,2},Self,Echo,{Self,hello}}},
353     {seq_trace,17,{send,{2,3},Echo,Self,{reply,hello}}}] =
354    [M || {seq_trace,17,_}=M <- Messages],
355
356    [{seq_trace,42,{print,{0,1},Sender,[],"trace started"}},
357     {seq_trace,42,{send,{0,2},Sender,Self,blurf}}] =
358    [M || {seq_trace,42,_}=M <- Messages],
359
360    unlink(Tracer), exit(Tracer, kill),
361    unlink(Echo), exit(Echo, kill),
362    unlink(Sender), exit(Sender, kill),
363    ok.
364
365t_process_info(Config) when is_list(Config) ->
366    Parent = self(),
367    Pid = spawn_link(fun() ->
368                             put(foo, bar),
369                             false = process_flag(sensitive, true),
370                             Parent ! go,
371                             receive
372                                 revert ->
373                                     true = process_flag(sensitive, false),
374                                     Parent ! go_again,
375                                     receive never -> ok end
376                             end end),
377    receive go -> ok end,
378
379    put(foo, bar),
380    self() ! Pid ! {i,am,a,message},
381
382    false = process_flag(sensitive, true),
383    t_process_info_suppressed(self()),
384    t_process_info_suppressed(Pid),
385
386    true = process_flag(sensitive, false),
387    Pid ! revert,
388    receive go_again -> ok end,
389
390    t_process_info_normal(self()),
391    t_process_info_normal(Pid),
392    ok.
393
394t_process_info_suppressed(Pid) ->
395    [] = my_process_info(Pid, dictionary),
396    <<>> = my_process_info(Pid, backtrace),
397    [] = my_process_info(Pid, messages).
398
399t_process_info_normal(Pid) ->
400    {value,{foo,bar}} = keysearch(foo, 1, my_process_info(Pid, dictionary)),
401    case process_info(Pid, backtrace) of
402        {backtrace,Bin} when size(Bin) > 20 -> ok
403    end,
404    [{i,am,a,message}] = my_process_info(Pid, messages).
405
406my_process_info(Pid, Tag) ->
407    {Tag,Value} = process_info(Pid, Tag),
408    All = process_info(Pid),
409    case keysearch(Tag, 1, All) of
410        false -> Value;
411        {value,{Tag,Value}} -> Value
412    end.
413
414t_process_display(Config) when is_list(Config) ->
415    Dir = filename:dirname(code:which(?MODULE)),
416    Cmd = ct:get_progname() ++ " -noinput -pa " ++ Dir ++
417    " -run " ++ ?MODULE_STRING ++ " remote_process_display",
418    io:put_chars(Cmd),
419    P = open_port({spawn,Cmd}, [in,stderr_to_stdout,eof]),
420    <<"done",_/binary>> = get_all(P),
421    ok.
422
423remote_process_display() ->
424    false = process_flag(sensitive, true),
425    erlang:process_display(self(), backtrace),
426    erlang:display(done),
427    receive after 10 -> ok end,
428    init:stop().
429
430get_all(P) ->
431    get_all(P, []).
432
433get_all(P, Acc) ->
434    receive
435        {P,{data,S}} ->
436            get_all(P, [Acc|S]);
437        {P,eof} ->
438            iolist_to_binary(Acc)
439    end.
440
441save_calls(Config) when is_list(Config) ->
442    process_flag(save_calls, 10),
443
444    false = process_flag(sensitive, true),
445    {last_calls,LastCalls} = process_info(self(), last_calls),
446    [{erlang,process_flag,2}] = LastCalls,
447    [2,4,6] = lists:map(fun(E) -> 2*E end, [1,2,3]),
448    {last_calls,LastCalls} = process_info(self(), last_calls),
449    ok.
450
451wait_trace(Pid) ->
452    Ref = erlang:trace_delivered(Pid),
453    receive
454        {trace_delivered,Pid,Ref} -> ok
455    end.
456
457id(I) -> I.
458