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