%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
-module(sensitive_SUITE).
-include("test_server.hrl").
-export([all/1,init_per_testcase/2,fin_per_testcase/2,
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]).
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog = ?t:timetrap(?t:minutes(5)),
[{watchdog,Dog}|Config].
fin_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog).
all(suite) ->
[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) ->
?line {Tracer,Mref} = spawn_monitor(fun() ->
receive after infinity -> ok end
end),
?line 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]),
?line foreach(fun(F) ->
1 = erlang:trace(self(), true, [F,{tracer,Tracer}])
end, Flags),
?line foreach(fun(F) ->
1 = erlang:trace(self(), false, [F,{tracer,Tracer}])
end, Flags),
?line 1 = erlang:trace(self(), true, [{tracer,Tracer}|Flags]),
?line 1 = erlang:trace(self(), false, [{tracer,Tracer}|Flags]),
?line {messages,[]} = process_info(Tracer, messages),
exit(Tracer, kill),
receive {'DOWN',Mref,_,_,_} -> ok end,
case process_info(self(), dictionary) of
{dictionary,[]} -> ok;
{dictionary,_} -> ?line ?t:fail(sensitive_flag_cleared)
end,
NewTracer = spawn_link(fun() -> receive after infinity -> ok end end),
?line 1 = erlang:trace(self(), true, [{tracer,NewTracer}|Flags]),
?line Flags = sort(element(2, erlang:trace_info(self(), flags))),
?line {tracer,NewTracer} = erlang:trace_info(self(), tracer),
%% Process still sensitive. Tracer should disappear when we clear
%% all trace flags.
?line 1 = erlang:trace(self(), false, [{tracer,NewTracer}|Flags]),
?line {tracer,[]} = erlang:trace_info(self(), tracer),
?line unlink(NewTracer), exit(NewTracer, kill),
ok.
send_trace(Config) when is_list(Config) ->
?line {Dead,Mref} = spawn_monitor(fun() -> ok end),
receive {'DOWN',Mref,_,_,_} -> ok end,
?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
?line Sink = spawn_link(fun() -> receive after infinity -> ok end end),
Self = self(),
?line 1 = erlang:trace(self(), true, [send,{tracer,Tracer}]),
?line Dead ! before,
?line Sink ! before,
?line false = process_flag(sensitive, true),
?line Sink ! {blurf,lists:seq(1, 50)},
?line true = process_flag(sensitive, true),
?line Sink ! lists:seq(1, 100),
?line Dead ! forget_me,
?line true = process_flag(sensitive, false),
?line Sink ! after1,
?line false = process_flag(sensitive, false),
?line Sink ! after2,
?line Dead ! after2,
?line wait_trace(Self),
?line {messages,Messages} = process_info(Tracer, messages),
?line [{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,
?line unlink(Tracer), exit(Tracer, kill),
?line unlink(Sink), exit(Sink, kill),
ok.
recv_trace(Config) when is_list(Config) ->
Parent = self(),
?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
?line Sender = spawn_link(fun() -> recv_trace_sender(Parent) end),
?line 1 = erlang:trace(self(), true, ['receive',{tracer,Tracer}]),
Sender ! 1,
receive a -> wait_trace(Sender) end,
?line false = process_flag(sensitive, true),
Sender ! 2,
receive {b,[x,y,z]} -> wait_trace(Sender) end,
?line true = process_flag(sensitive, false),
Sender ! 3,
receive c -> wait_trace(Sender) end,
?line {messages,Messages} = process_info(Tracer, messages),
[{trace,Parent,'receive',a},
{trace,Parent,'receive',{trace_delivered,_,_}},
{trace,Parent,'receive',c},
{trace,Parent,'receive',{trace_delivered,_,_}}] = Messages,
?line unlink(Tracer), exit(Tracer, kill),
?line 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(),
?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
?line 1 = erlang:trace(self(), true, [procs,{tracer,Tracer}]),
?line false = process_flag(sensitive, true),
spawn(fun() -> ok end),
?line register(nisse, self()),
?line unregister(nisse),
?line link(Tracer),
?line unlink(Tracer),
?line Linker0 = spawn_link(fun() -> ok end),
Mref0 = erlang:monitor(process, Linker0),
?line {_,Mref} = spawn_monitor(fun() -> link(Self), unlink(Self) end),
receive {'DOWN',Mref0,_,_,_} -> ok end,
receive {'DOWN',Mref,_,_,_} -> ok end,
?line true = process_flag(sensitive, false),
Dead = spawn(fun() -> ok end),
?line register(arne, self()),
?line unregister(arne),
?line {Linker,Mref2} = spawn_monitor(fun() -> link(Self), unlink(Self) end),
receive {'DOWN',Mref2,_,_,_} -> ok end,
?line Last = spawn_link(fun() -> ok end),
receive after 10 -> ok end,
?line wait_trace(all),
?line {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,
?line unlink(Tracer), exit(Tracer, kill),
ok.
call_trace(Config) when is_list(Config) ->
Self = self(),
?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
?line 1 = erlang:trace(self(), true, [call,{tracer,Tracer}]),
?line 1 = erlang:trace_pattern({?MODULE,an_exported_function,1},
true, [global]),
?line 1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [global]),
?line 1 = erlang:trace_pattern({erlang,binary_to_list,1}, true, [local]),
?line Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [local]),
?line false = process_flag(sensitive, true),
?line {ok,42} = a_local_function(42),
?line 7 = an_exported_function(6),
?line <<7,8,9,10>> = list_to_binary(id([7,8,9,10])),
?line [42,43] = binary_to_list(id(<<42,43>>)),
?line true = process_flag(sensitive, false),
?line {ok,{a,b}} = a_local_function({a,b}),
?line 1 = an_exported_function(0),
?line <<1,2,3>> = list_to_binary(id([1,2,3])),
?line [42,43,44] = binary_to_list(id(<<42,43,44>>)),
?line wait_trace(Self),
?line {messages,Messages} = process_info(Tracer, messages),
?line [{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,
?line Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [local]),
?line erlang:trace_pattern({erlang,'_','_'}, false, [local]),
?line erlang:trace_pattern({'_','_','_'}, false, [global]),
?line unlink(Tracer), exit(Tracer, kill),
ok.
meta_trace(Config) when is_list(Config) ->
Self = self(),
?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
?line Local = erlang:trace_pattern({?MODULE,'_','_'}, true, [{meta,Tracer}]),
?line 1 = erlang:trace_pattern({erlang,list_to_binary,1}, true, [{meta,Tracer}]),
?line false = process_flag(sensitive, true),
?line {ok,blurf} = a_local_function(blurf),
?line 100 = an_exported_function(99),
?line <<8,9,10>> = list_to_binary(id([8,9,10])),
?line true = process_flag(sensitive, false),
?line {ok,{x,y}} = a_local_function({x,y}),
?line 1 = an_exported_function(0),
?line <<10>> = list_to_binary(id([10])),
?line wait_trace(Self),
?line Local = erlang:trace_pattern({?MODULE,'_','_'}, false, [meta]),
?line 1 = erlang:trace_pattern({erlang,list_to_binary,1}, false, [meta]),
?line a_local_function(0),
?line {messages,Messages} = process_info(Tracer, messages),
?line [{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,
?line 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(),
?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
?line false = process_flag(sensitive, true),
?line 1 = erlang:trace(Self, true, [running,{tracer,Tracer}]),
erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(),
erlang:yield(), erlang:yield(), erlang:yield(), erlang:yield(),
?line true = process_flag(sensitive, false),
erlang:yield(),
?line 1 = erlang:trace(Self, false, [running,{tracer,Tracer}]),
?line wait_trace(Self),
?line {messages,Messages} = process_info(Tracer, messages),
?line [{trace,Self,out,{sensitive_SUITE,running_trace,1}},
{trace,Self,in,{sensitive_SUITE,running_trace,1}}] = Messages,
?line unlink(Tracer), exit(Tracer, kill),
ok.
gc_trace(Config) when is_list(Config) ->
Self = self(),
?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
?line false = process_flag(sensitive, true),
?line 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(),
?line true = process_flag(sensitive, false),
erlang:garbage_collect(),
?line 1 = erlang:trace(Self, false, [garbage_collection,{tracer,Tracer}]),
?line wait_trace(Self),
?line {messages,Messages} = process_info(Tracer, messages),
?line [{trace,Self,gc_start,_},{trace,Self,gc_end,_}] = Messages,
?line unlink(Tracer), exit(Tracer, kill),
ok.
seq_trace(Config) when is_list(Config) ->
Self = self(),
?line Tracer = spawn_link(fun() -> receive after infinity -> ok end end),
?line seq_trace:set_system_tracer(Tracer),
?line false = process_flag(sensitive, true),
?line Echo = spawn_link(fun() ->
receive
{Pid,Message} ->
Pid ! {reply,Message}
end
end),
?line 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,
?line wait_trace(all),
?line {messages,Messages} = process_info(Tracer, messages),
?line [{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],
?line [{seq_trace,42,{print,{0,1},Sender,[],"trace started"}},
{seq_trace,42,{send,{0,2},Sender,Self,blurf}}] =
[M || {seq_trace,42,_}=M <- Messages],
?line unlink(Tracer), exit(Tracer, kill),
?line unlink(Echo), exit(Echo, kill),
?line unlink(Sender), exit(Sender, kill),
ok.
t_process_info(Config) when is_list(Config) ->
Parent = self(),
?line 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,
?line put(foo, bar),
?line self() ! Pid ! {i,am,a,message},
?line false = process_flag(sensitive, true),
?line t_process_info_suppressed(self()),
?line t_process_info_suppressed(Pid),
?line true = process_flag(sensitive, false),
Pid ! revert,
receive go_again -> ok end,
?line t_process_info_normal(self()),
?line 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) ->
?line Dir = filename:dirname(code:which(?MODULE)),
?line Cmd = atom_to_list(lib:progname()) ++ " -noinput -pa " ++ Dir ++
" -run " ++ ?MODULE_STRING ++ " remote_process_display",
?line io:put_chars(Cmd),
?line 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),
?line {last_calls,LastCalls} = process_info(self(), last_calls),
?line [{erlang,process_flag,2}] = LastCalls,
?line [2,4,6] = lists:map(fun(E) -> 2*E end, [1,2,3]),
?line {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.