%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2007-2011. 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_lib("test_server/include/test_server.hrl"). -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, 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]. end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Dog = ?config(watchdog, Config), ?t:timetrap_cancel(Dog). suite() -> [{ct_hooks,[ts_install_cth]}]. 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]. groups() -> []. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. 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}|_] = 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.