%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2007-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(sensitive_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,
stickiness/1,send_trace/1,recv_trace/1,proc_trace/1,call_trace/1,
meta_trace/1,running_trace/1,gc_trace/1,seq_trace/1,
t_process_info/1,t_process_display/1,save_calls/1]).
-export([remote_process_display/0,an_exported_function/1]).
-import(lists, [keysearch/3,foreach/2,sort/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 5}}].
all() ->
[stickiness, send_trace, recv_trace, proc_trace,
call_trace, meta_trace, running_trace, gc_trace,
seq_trace, t_process_info, t_process_display,
save_calls].
stickiness(Config) when is_list(Config) ->
{Tracer,Mref} = spawn_monitor(fun() ->
receive after infinity -> ok end
end),
false = process_flag(sensitive, true),
put(foo, bar),
Flags = sort([send,'receive',procs,call,running,garbage_collection,
set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link]),
foreach(fun(F) ->
1 = erlang:trace(self(), true, [F,{tracer,Tracer}])
end, Flags),
foreach(fun(F) ->
1 = erlang:trace(self(), false, [F,{tracer,Tracer}])
end, Flags),
1 = erlang:trace(self(), true, [{tracer,Tracer}|Flags]),
1 = erlang:trace(self(), false, [{tracer,Tracer}|Flags]),
{messages,[]} = process_info(Tracer, messages),
exit(Tracer, kill),
receive {'DOWN',Mref,_,_,_} -> ok end,
case process_info(self(), dictionary) of
{dictionary,[]} -> ok;
{dictionary,_} -> ct:fail(sensitive_flag_cleared)
end,
NewTracer = spawn_link(fun() -> receive after infinity -> ok end end),
1 = erlang:trace(self(), true, [{tracer,NewTracer}|Flags]),
Flags = sort(element(2, erlang:trace_info(self(), flags))),
{tracer,NewTracer} = erlang:trace_info(self(), tracer),
%% Process still sensitive. Tracer should disappear when we clear
%% all trace flags.
1 = erlang:trace(self(), false, [{tracer,NewTracer}|Flags]),
{tracer,[]} = erlang:trace_info(self(), tracer),
unlink(NewTracer), exit(NewTracer, kill),
ok.
send_trace(Config) when is_list(Config) ->
{Dead,Mref} = spawn_monitor(fun() -> ok end),
receive {'DOWN',Mref,_,_,_} -> ok end,
Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
Sink = spawn_link(fun() -> receive after infinity -> ok end end),
Self = self(),
1 = erlang:trace(self(), true, [send,{tracer,Tracer}]),
Dead ! before,
Sink ! before,
false = process_flag(sensitive, true),
Sink ! {blurf,lists:seq(1, 50)},
true = process_flag(sensitive, true),
Sink ! lists:seq(1, 100),
Dead ! forget_me,
true = process_flag(sensitive, false),
Sink ! after1,
false = process_flag(sensitive, false),
Sink ! after2,
Dead ! after2,
wait_trace(Self),
{messages,Messages} = process_info(Tracer, messages),
[{trace,Self,send_to_non_existing_process,before,Dead},
{trace,Self,send,before,Sink},
{trace,Self,send,after1,Sink},
{trace,Self,send,after2,Sink},
{trace,Self,send_to_non_existing_process,after2,Dead}] = Messages,
unlink(Tracer), exit(Tracer, kill),
unlink(Sink), exit(Sink, kill),
ok.
recv_trace(Config) when is_list(Config) ->
Parent = self(),
Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
Sender = spawn_link(fun() -> recv_trace_sender(Parent) end),
1 = erlang:trace(self(), true, ['receive',{tracer,Tracer}]),
Sender ! 1,
receive a -> wait_trace(Sender) end,
false = process_flag(sensitive, true),
Sender ! 2,
receive {b,[x,y,z]} -> wait_trace(Sender) end,
true = process_flag(sensitive, false),
Sender ! 3,
receive c -> wait_trace(Sender) end,
{messages,Messages} = process_info(Tracer, messages),
[{trace,Parent,'receive',a},
{trace,Parent,'receive',{trace_delivered,_,_}},
{trace,Parent,'receive',c}|_] = Messages,
unlink(Tracer), exit(Tracer, kill),
unlink(Sender), exit(Sender, kill),
ok.
recv_trace_sender(Pid) ->
receive
1 -> Pid ! a;
2 -> Pid ! {b,[x,y,z]};
3 -> Pid ! c
end,
recv_trace_sender(Pid).
proc_trace(Config) when is_list(Config) ->
Self = self(),
Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
1 = erlang:trace(self(), true, [procs,{tracer,Tracer}]),
false = process_flag(sensitive, true),
spawn(fun() -> ok end),
register(nisse, self()),
unregister(nisse),
link(Tracer),
unlink(Tracer),
Linker0 = spawn_link(fun() -> ok end),
Mref0 = erlang:monitor(process, Linker0),
{_,Mref} = spawn_monitor(fun() -> link(Self), unlink(Self) end),
receive {'DOWN',Mref0,_,_,_} -> ok end,
receive {'DOWN',Mref,_,_,_} -> ok end,
true = process_flag(sensitive, false),
Dead = spawn(fun() -> ok end),
register(arne, self()),
unregister(arne),
{Linker,Mref2} = spawn_monitor(fun() -> link(Self), unlink(Self) end),
receive {'DOWN',Mref2,_,_,_} -> ok end,
Last = spawn_link(fun() -> ok end),
receive after 10 -> ok end,
wait_trace(all),
{messages,Messages} = process_info(Tracer, messages),
[{trace,Self,spawn,Dead,{erlang,apply,_}},
{trace,Self,register,arne},
{trace,Self,unregister,arne},
{trace,Self,spawn,Linker,_},
{trace,Self,getting_linked,Linker},
{trace,Self,getting_unlinked,Linker},
{trace,Self,spawn,Last,_},
{trace,Self,link,Last},
{trace,Self,getting_unlinked,Last}] = Messages,
unlink(Tracer), exit(Tracer, kill),
ok.
call_trace(Config) when is_list(Config) ->
Self = self(),
Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
1 = erlang:trace(self(), true, [call,{tracer,Tracer}]),
1 = erlang:trace_pattern({?MODULE,an_exported_function,1},
true, [global]),
1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [global]),
1 = erlang:trace_pattern({erlang,binary_to_list,1}, true, [local]),
Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [local]),
false = process_flag(sensitive, true),
{ok,42} = a_local_function(42),
7 = an_exported_function(6),
<<7,8,9,10>> = list_to_binary(id([7,8,9,10])),
[42,43] = binary_to_list(id(<<42,43>>)),
true = process_flag(sensitive, false),
{ok,{a,b}} = a_local_function({a,b}),
1 = an_exported_function(0),
<<1,2,3>> = list_to_binary(id([1,2,3])),
[42,43,44] = binary_to_list(id(<<42,43,44>>)),
wait_trace(Self),
{messages,Messages} = process_info(Tracer, messages),
[{trace,Self,call,{?MODULE,a_local_function,[{a,b}]}},
{trace,Self,call,{?MODULE,an_exported_function,[0]}},
{trace,Self,call,{?MODULE,id,[_]}},
{trace,Self,call,{erlang,list_to_binary,[[1,2,3]]}},
{trace,Self,call,{sensitive_SUITE,id,[<<42,43,44>>]}},
{trace,Self,call,{erlang,binary_to_list,[<<42,43,44>>]}},
{trace,Self,call,{?MODULE,wait_trace,[Self]}}] = Messages,
Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [local]),
erlang:trace_pattern({erlang,'_','_'}, false, [local]),
erlang:trace_pattern({'_','_','_'}, false, [global]),
unlink(Tracer), exit(Tracer, kill),
ok.
meta_trace(Config) when is_list(Config) ->
Self = self(),
Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [{meta,Tracer}]),
1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [{meta,Tracer}]),
false = process_flag(sensitive, true),
{ok,blurf} = a_local_function(blurf),
100 = an_exported_function(99),
<<8,9,10>> = list_to_binary(id([8,9,10])),
true = process_flag(sensitive, false),
{ok,{x,y}} = a_local_function({x,y}),
1 = an_exported_function(0),
<<10>> = list_to_binary(id([10])),
wait_trace(Self),
Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [meta]),
1 = erlang:trace_pattern({erlang,list_to_binary,1}, false, [meta]),
a_local_function(0),
{messages,Messages} = process_info(Tracer, messages),
[{trace_ts,Self,call,{?MODULE,a_local_function,[{x,y}]},{_,_,_}},
{trace_ts,Self,call,{?MODULE,an_exported_function,[0]},{_,_,_}},
{trace_ts,Self,call,{?MODULE,id,[_]},{_,_,_}},
{trace_ts,Self,call,{erlang,list_to_binary,[[10]]},{_,_,_}},
{trace_ts,Self,call,{?MODULE,wait_trace,[Self]},{_,_,_}}] = Messages,
unlink(Tracer), exit(Tracer, kill),
ok.
a_local_function(A) ->
{ok,A}.
an_exported_function(X) ->
X+1.
running_trace(Config) when is_list(Config) ->
Self = self(),
Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
false = process_flag(sensitive, true),
1 = erlang:trace(Self, true, [running,{tracer,Tracer}]),
erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(),
erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(),
true = process_flag(sensitive, false),
erlang:yield(),
1 = erlang:trace(Self, false, [running,{tracer,Tracer}]),
wait_trace(Self),
{messages,Messages} = process_info(Tracer, messages),
[{trace,Self,out,{sensitive_SUITE,running_trace,1}},
{trace,Self,in,{sensitive_SUITE,running_trace,1}}] = Messages,
unlink(Tracer), exit(Tracer, kill),
ok.
gc_trace(Config) when is_list(Config) ->
Self = self(),
Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
false = process_flag(sensitive, true),
1 = erlang:trace(Self, true, [garbage_collection,{tracer,Tracer}]),
erlang:garbage_collect(), erlang:garbage_collect(),
erlang:garbage_collect(), erlang:garbage_collect(),
erlang:garbage_collect(), erlang:garbage_collect(),
erlang:garbage_collect(), erlang:garbage_collect(),
true = process_flag(sensitive, false),
erlang:garbage_collect(),
1 = erlang:trace(Self, false, [garbage_collection,{tracer,Tracer}]),
wait_trace(Self),
{messages,Messages} = process_info(Tracer, messages),
[{trace,Self,gc_major_start,_},{trace,Self,gc_major_end,_}] = Messages,
unlink(Tracer), exit(Tracer, kill),
ok.
seq_trace(Config) when is_list(Config) ->
Self = self(),
Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
seq_trace:set_system_tracer(Tracer),
false = process_flag(sensitive, true),
Echo = spawn_link(fun() ->
receive
{Pid,Message} ->
Pid ! {reply,Message}
end
end),
Sender = spawn_link(fun() ->
seq_trace:set_token(label, 42),
seq_trace:set_token('receive', true),
seq_trace:set_token(send, true),
seq_trace:set_token(print, true),
seq_trace:print(42, "trace started"),
Self ! blurf
end),
seq_trace:set_token(label, 17),
seq_trace:set_token('receive', true),
seq_trace:set_token(send, true),
seq_trace:set_token(print, true),
seq_trace:print(17, "trace started"),
Echo ! {Self,hello},
receive {reply,hello} -> ok end,
receive blurf -> ok end,
wait_trace(all),
{messages,Messages} = process_info(Tracer, messages),
[{seq_trace,17,{'receive',{0,2},Self,Echo,{Self,hello}}},
{seq_trace,17,{send,{2,3},Echo,Self,{reply,hello}}}] =
[M || {seq_trace,17,_}=M <- Messages],
[{seq_trace,42,{print,{0,1},Sender,[],"trace started"}},
{seq_trace,42,{send,{0,2},Sender,Self,blurf}}] =
[M || {seq_trace,42,_}=M <- Messages],
unlink(Tracer), exit(Tracer, kill),
unlink(Echo), exit(Echo, kill),
unlink(Sender), exit(Sender, kill),
ok.
t_process_info(Config) when is_list(Config) ->
Parent = self(),
Pid = spawn_link(fun() ->
put(foo, bar),
false = process_flag(sensitive, true),
Parent ! go,
receive
revert ->
true = process_flag(sensitive, false),
Parent ! go_again,
receive never -> ok end
end end),
receive go -> ok end,
put(foo, bar),
self() ! Pid ! {i,am,a,message},
false = process_flag(sensitive, true),
t_process_info_suppressed(self()),
t_process_info_suppressed(Pid),
true = process_flag(sensitive, false),
Pid ! revert,
receive go_again -> ok end,
t_process_info_normal(self()),
t_process_info_normal(Pid),
ok.
t_process_info_suppressed(Pid) ->
[] = my_process_info(Pid, dictionary),
<<>> = my_process_info(Pid, backtrace),
[] = my_process_info(Pid, messages).
t_process_info_normal(Pid) ->
{value,{foo,bar}} = keysearch(foo, 1, my_process_info(Pid, dictionary)),
case process_info(Pid, backtrace) of
{backtrace,Bin} when size(Bin) > 20 -> ok
end,
[{i,am,a,message}] = my_process_info(Pid, messages).
my_process_info(Pid, Tag) ->
{Tag,Value} = process_info(Pid, Tag),
All = process_info(Pid),
case keysearch(Tag, 1, All) of
false -> Value;
{value,{Tag,Value}} -> Value
end.
t_process_display(Config) when is_list(Config) ->
Dir = filename:dirname(code:which(?MODULE)),
Cmd = ct:get_progname() ++ " -noinput -pa " ++ Dir ++
" -run " ++ ?MODULE_STRING ++ " remote_process_display",
io:put_chars(Cmd),
P = open_port({spawn,Cmd}, [in,stderr_to_stdout,eof]),
<<"done",_/binary>> = get_all(P),
ok.
remote_process_display() ->
false = process_flag(sensitive, true),
erlang:process_display(self(), backtrace),
erlang:display(done),
receive after 10 -> ok end,
init:stop().
get_all(P) ->
get_all(P, []).
get_all(P, Acc) ->
receive
{P,{data,S}} ->
get_all(P, [Acc|S]);
{P,eof} ->
iolist_to_binary(Acc)
end.
save_calls(Config) when is_list(Config) ->
process_flag(save_calls, 10),
false = process_flag(sensitive, true),
{last_calls,LastCalls} = process_info(self(), last_calls),
[{erlang,process_flag,2}] = LastCalls,
[2,4,6] = lists:map(fun(E) -> 2*E end, [1,2,3]),
{last_calls,LastCalls} = process_info(self(), last_calls),
ok.
wait_trace(Pid) ->
Ref = erlang:trace_delivered(Pid),
receive
{trace_delivered,Pid,Ref} -> ok
end.
id(I) -> I.