diff options
Diffstat (limited to 'erts/emulator/test/sensitive_SUITE.erl')
-rw-r--r-- | erts/emulator/test/sensitive_SUITE.erl | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/erts/emulator/test/sensitive_SUITE.erl b/erts/emulator/test/sensitive_SUITE.erl new file mode 100644 index 0000000000..458275af81 --- /dev/null +++ b/erts/emulator/test/sensitive_SUITE.erl @@ -0,0 +1,461 @@ +%% +%% %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. + |