1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 2007-2012. 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: 
   21: %%% Purpose : Tests system_profile BIF
   22: 
   23: -module(system_profile_SUITE).
   24: 
   25: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   26: 	 init_per_group/2,end_per_group/2,
   27: 	system_profile_on_and_off/1,
   28: 	runnable_procs/1,
   29: 	runnable_ports/1,
   30: 	dont_profile_profiler/1,
   31: 	scheduler/1
   32:         ]).
   33: 
   34: -export([init_per_testcase/2, end_per_testcase/2]).
   35: 
   36: -export([profiler_process/1, ring_loop/1, port_echo_start/0, 
   37: 	 list_load/0, run_load/2]).
   38: 
   39: -include_lib("test_server/include/test_server.hrl").
   40: 
   41: -define(default_timeout, ?t:minutes(1)).
   42: 
   43: init_per_testcase(_Case, Config) ->
   44:     Dog=?t:timetrap(?default_timeout),
   45:     [{watchdog, Dog}|Config].
   46: end_per_testcase(_Case, Config) ->
   47:     Dog=?config(watchdog, Config),
   48:     ?t:timetrap_cancel(Dog),
   49:     ok.
   50: 
   51: suite() -> [{ct_hooks,[ts_install_cth]}].
   52: 
   53: all() -> 
   54:     [system_profile_on_and_off, runnable_procs,
   55:      runnable_ports, scheduler, dont_profile_profiler].
   56: 
   57: groups() -> 
   58:     [].
   59: 
   60: init_per_suite(Config) ->
   61:     Config.
   62: 
   63: end_per_suite(_Config) ->
   64:     ok.
   65: 
   66: init_per_group(_GroupName, Config) ->
   67:     Config.
   68: 
   69: end_per_group(_GroupName, Config) ->
   70:     Config.
   71: 
   72: 
   73: %% No specification clause needed for an init function in a conf case!!!
   74: 
   75: %% Test switching system_profiling on and off.
   76: system_profile_on_and_off(suite) ->
   77:     [];
   78: system_profile_on_and_off(doc) ->
   79:     ["Tests switching system_profiling on and off."];
   80: system_profile_on_and_off(Config) when is_list(Config) ->
   81:     Pid = start_profiler_process(),
   82:     
   83:     % Test runnable_ports on and off
   84:     undefined = erlang:system_profile(Pid, [runnable_ports]),
   85:     {Pid, [runnable_ports]} = erlang:system_profile(),
   86:     {Pid, [runnable_ports]} = erlang:system_profile(undefined, []),
   87: 
   88:     % Test runnable_procs on and off
   89:     undefined = erlang:system_profile(Pid, [runnable_procs]),
   90:     {Pid, [runnable_procs]} = erlang:system_profile(),
   91:     {Pid, [runnable_procs]} = erlang:system_profile(undefined, []),
   92: 
   93:     % Test scheduler on and off
   94:     undefined = erlang:system_profile(Pid, [scheduler]),
   95:     {Pid, [scheduler]} = erlang:system_profile(),
   96:     {Pid, [scheduler]} = erlang:system_profile(undefined, []),
   97: 
   98:     % Test combined runnable_ports, runnable_procs, scheduler; on and off
   99:     undefined = erlang:system_profile(Pid, [scheduler, runnable_procs, runnable_ports]),
  100:     {Pid, [scheduler,runnable_procs,runnable_ports]} = erlang:system_profile(),
  101:     {Pid, [scheduler,runnable_procs,runnable_ports]} = erlang:system_profile(undefined, []),
  102: 
  103:     % Test turned off and kill process
  104:     undefined = erlang:system_profile(),
  105:     exit(Pid,kill),
  106:     ok.
  107: 
  108: %% Test runnable_procs
  109: 
  110: runnable_procs(suite) ->
  111:     [];
  112: runnable_procs(doc) ->
  113:     ["Tests system_profiling with runnable_procs."];
  114: runnable_procs(Config) when is_list(Config) ->
  115:     Pid = start_profiler_process(),
  116:     % start a ring of processes
  117:     % FIXME: Set #laps and #nodes in config file
  118:     Nodes = 10,
  119:     Laps = 10,
  120:     Master = ring(Nodes),
  121:     undefined = erlang:system_profile(Pid, [runnable_procs]),
  122:     % loop a message
  123:     ok = ring_message(Master, message, Laps),
  124:     Events = get_profiler_events(),
  125:     kill_em_all = kill_ring(Master),
  126:     erlang:system_profile(undefined, []),
  127:     put(master, Master),
  128:     put(laps, Laps),
  129:     true = has_runnable_event(Events),
  130:     Pids = sort_events_by_pid(Events),
  131:     ok = check_events(Pids),
  132:     erase(),
  133:     exit(Pid,kill),
  134:     ok.
  135: 
  136: runnable_ports(suite) ->
  137:     [];
  138: runnable_ports(doc) ->
  139:     ["Tests system_profiling with runnable_port."];
  140: runnable_ports(Config) when is_list(Config) ->
  141:     Pid = start_profiler_process(),
  142:     undefined = erlang:system_profile(Pid, [runnable_ports]),
  143:     EchoPid = echo(Config),
  144:     % FIXME: Set config to number_of_echos
  145:     Laps = 10,
  146:     put(laps, Laps),
  147:     ok = echo_message(EchoPid, Laps, message),
  148:     Events = get_profiler_events(),
  149:     kill_em_all = kill_echo(EchoPid),
  150:     erlang:system_profile(undefined, []),
  151:     true = has_runnable_event(Events),
  152:     Pids = sort_events_by_pid(Events),
  153:     ok = check_events(Pids),
  154:     erase(),
  155:     exit(Pid,kill),
  156:     ok.
  157: 
  158: scheduler(suite) ->
  159:     [];
  160: scheduler(doc) ->
  161:     ["Tests system_profiling with scheduler."];
  162: scheduler(Config) when is_list(Config) ->
  163:     case {erlang:system_info(smp_support), erlang:system_info(schedulers_online)} of
  164: 	{false,_} -> {skipped, "No need for scheduler test when smp support is disabled."};
  165: 	{_,    1} -> {skipped, "No need for scheduler test when only one scheduler online."};
  166: 	_ ->
  167: 	    Nodes = 10,
  168: 	    ok = check_block_system(Nodes),
  169: 	    ok = check_multi_scheduling_block(Nodes)
  170:     end.
  171: 
  172: % the profiler pid should not be profiled
  173: dont_profile_profiler(suite) ->
  174:     [];
  175: dont_profile_profiler(doc) ->
  176:     ["Ensure system profiler process is not profiled."];
  177: dont_profile_profiler(Config) when is_list(Config) ->
  178:     Pid = start_profiler_process(),
  179: 
  180:     Nodes = 10,
  181:     Laps = 10,
  182:     Master = ring(Nodes),
  183:     undefined = erlang:system_profile(Pid, [runnable_procs]),
  184:     % loop a message
  185:     ok = ring_message(Master, message, Laps),
  186:     erlang:system_profile(undefined, []),
  187:     kill_em_all = kill_ring(Master),
  188:     Events = get_profiler_events(),
  189:     false  = has_profiler_pid_event(Events, Pid),
  190: 
  191:     exit(Pid,kill),
  192:     ok.
  193: 
  194: 
  195: %%% Check scheduler profiling
  196: 
  197: check_multi_scheduling_block(Nodes) ->
  198:     Pid = start_profiler_process(),
  199:     undefined = erlang:system_profile(Pid, [scheduler]),
  200:     {ok, Supervisor} = start_load(Nodes),
  201:     wait(600),
  202:     erlang:system_flag(multi_scheduling, block),
  203:     wait(600),
  204:     erlang:system_flag(multi_scheduling, unblock),
  205:     {Pid, [scheduler]} = erlang:system_profile(undefined, []),
  206:     Events = get_profiler_events(),
  207:     true = has_scheduler_event(Events),
  208:     stop_load(Supervisor),
  209:     exit(Pid,kill),
  210:     erase(),
  211:     ok.
  212: 
  213: check_block_system(Nodes) ->
  214:     Dummy = spawn(?MODULE, profiler_process, [[]]),
  215:     Pid = start_profiler_process(),
  216:     undefined = erlang:system_profile(Pid, [scheduler]),
  217:     {ok, Supervisor} = start_load(Nodes),
  218:     wait(300),
  219:     undefined = erlang:system_monitor(Dummy, [busy_port]),
  220:     {Dummy, [busy_port]} = erlang:system_monitor(undefined, []),
  221:     {Pid, [scheduler]} = erlang:system_profile(undefined, []),
  222:     Events = get_profiler_events(),
  223:     true = has_scheduler_event(Events),
  224:     stop_load(Supervisor),
  225:     exit(Pid,kill),
  226:     exit(Dummy,kill),
  227:     erase(),
  228:     ok.
  229: 
  230: %%% Check events
  231: 
  232: check_events([]) -> ok;
  233: check_events([Pid | Pids]) ->
  234:     Master = get(master),
  235:     Laps = get(laps),
  236:     CheckPids = get(pids),
  237:     {Events, N} = get_pid_events(Pid),
  238:     ok = check_event_flow(Events),
  239:     ok = check_event_ts(Events),
  240:     IsMember = lists:member(Pid, CheckPids),
  241:     case Pid of
  242:     	Master ->
  243: 	    io:format("Expected ~p and got ~p profile events from ~p: ok~n", [Laps*2+2, N, Pid]),
  244: 	    N = Laps*2 + 2,
  245:     	    check_events(Pids);
  246: 	Pid when IsMember == true ->
  247: 	    io:format("Expected ~p and got ~p profile events from ~p: ok~n", [Laps*2, N, Pid]),
  248: 	    N = Laps*2,
  249:     	    check_events(Pids);
  250: 	Pid ->
  251: 	    check_events(Pids)
  252:     end.
  253: 
  254: %% timestamp consistency check for descending timestamps
  255: 
  256: check_event_ts(Events) ->
  257:     check_event_ts(Events, undefined).
  258: check_event_ts([], _) -> ok;
  259: check_event_ts([Event | Events], undefined) ->
  260:     check_event_ts(Events, Event);
  261: check_event_ts([{Pid, _, _, TS1}=Event | Events], {Pid,_,_,TS0}) ->
  262:     Time = timer:now_diff(TS1, TS0),
  263:     if 
  264:     	Time < 0.0 -> timestamp_error;
  265:     	true -> check_event_ts(Events, Event)
  266:     end.
  267: 
  268: %% consistency check for active vs. inactive activity (runnable)
  269: 
  270: check_event_flow(Events) ->
  271:     check_event_flow(Events, undefined).
  272: check_event_flow([], _) -> ok;
  273: check_event_flow([Event | PidEvents], undefined) ->
  274:     check_event_flow(PidEvents, Event);
  275: check_event_flow([{Pid,Act,_,_}=Event | Events], PrevEvent) ->
  276:     case PrevEvent of
  277:     	{Pid, Act, _MFA, _TS} -> consistency_error;
  278: 	_ -> check_event_flow(Events, Event)
  279:     end.
  280:     
  281: 
  282: 
  283: get_pid_events(Pid) -> 
  284:     Events = get({pid_events, Pid}),
  285:     {Events, length(Events)}.
  286: 
  287: sort_events_by_pid(Events) ->
  288:     sort_events_by_pid(lists:reverse(Events), []).
  289: sort_events_by_pid([], Pids) -> Pids;
  290: sort_events_by_pid([Event | Events],Pids) ->
  291:     case Event of 
  292:     	{profile,Pid,Act,MFA,TS} ->
  293: 	    case get({pid_events, Pid}) of 
  294: 	    	undefined ->
  295: 		    put({pid_events, Pid}, [{Pid,Act,MFA,TS}]),
  296: 		    sort_events_by_pid(Events, [Pid | Pids]);
  297: 		PidEvents ->
  298: 		    put({pid_events, Pid}, [{Pid,Act,MFA,TS}|PidEvents]),
  299: 		    sort_events_by_pid(Events, Pids)
  300: 	    end
  301:     end.
  302: 
  303: 
  304: %%%
  305: %% Process ring
  306: %%%
  307: 
  308: %% API
  309: 
  310: % Returns master pid
  311: ring(N) -> 
  312:     Pids = build_ring(N, []),
  313:     put(pids, Pids),
  314:     setup_ring(Pids).
  315: 
  316: ring_message(Master, Message, Laps) ->
  317:     Master ! {message, Master, Laps, Message},
  318:     receive
  319:     	{laps_complete, Master} -> ok
  320:     end.
  321: 
  322: kill_ring(Master) -> Master ! kill_em_all.
  323: 
  324: %% Process ring helpers
  325: 
  326: build_ring(0, Pids) -> Pids;
  327: build_ring(N, Pids) ->
  328:     build_ring(N - 1, [spawn_link(?MODULE, ring_loop, [undefined]) | Pids]).
  329: 
  330: setup_ring([Master | Relayers]) ->
  331:     % Relayers may not include the master pid
  332:     Master ! {setup_ring, Relayers, self()},
  333:     receive
  334:         {setup_complete, Master} -> Master
  335:     end.
  336: 
  337: ring_loop(RelayTo) ->
  338:     receive
  339:         kill_em_all ->
  340:             RelayTo ! kill_em_all;
  341:         {setup_ring, [Pid | Pids], Supervisor} ->
  342:             put(supervisor, Supervisor),
  343:             Pid ! {relay_to, Pids, self()},
  344:             ring_loop(Pid);
  345:         {setup_complete, _} ->
  346:             get(supervisor) ! {setup_complete, self()},
  347:             ring_loop(RelayTo);
  348:         {relay_to, [], Master} ->
  349:             Master ! {setup_complete, self()},
  350:             ring_loop(Master);
  351:         {relay_to, [Pid | Pids], Master} ->
  352:             Pid ! {relay_to, Pids, Master},
  353:             ring_loop(Pid);
  354:         {message, Master, Lap, Msg}=Message ->
  355:             case {self(), Lap} of
  356:                 {Master, 0} ->
  357:                     get(supervisor) ! {laps_complete, self()},
  358:                     ring_loop(RelayTo);
  359:                 {Master, Lap} ->
  360:                     RelayTo ! {message, Master, Lap - 1, Msg},
  361:                     ring_loop(RelayTo);
  362:                 _ ->
  363:                     RelayTo ! Message,
  364:                     ring_loop(RelayTo)
  365:             end
  366:     end.
  367: 
  368: %%%
  369: %% Echo driver
  370: %%%
  371: 
  372: %% API
  373: 
  374: echo(Config) ->
  375:     Path = ?config(data_dir, Config),
  376:     erl_ddll:load_driver(Path, echo_drv),
  377:     Pid = spawn_link(?MODULE, port_echo_start, []),
  378:     Pid ! {self(), get_ports},
  379:     receive
  380: 	{port, Port} ->
  381: 	    put(pids, [Port]),
  382:     	    put(master, Port),
  383: 	    Pid
  384:     end.
  385: 
  386: echo_message(Pid, N, Msg) -> 
  387:     Pid ! {start_echo, self(), N, Msg},
  388:     receive
  389: 	{echo_complete, Pid} -> ok
  390:     end.
  391: 
  392: kill_echo(Pid) -> Pid ! kill_em_all.
  393: 
  394: 
  395: %% Echo driver helpers
  396: port_echo_start() ->
  397:     Port = open_port({spawn,echo_drv}, [eof,binary]),
  398:     receive
  399: 	{Pid, get_ports} ->
  400:     	    Pid ! {port, Port},
  401: 	    port_echo_loop(Port)
  402:     end.
  403: 
  404: port_echo_loop(Port) ->
  405:     receive
  406: 	{start_echo, Pid, Echos, Msg} ->
  407: 	    port_command(Port, term_to_binary({Pid, Echos, Msg})),
  408: 	    port_echo_loop(Port);
  409: 	{Port, {data, Data}} ->
  410: 	    {Pid, Echos, Msg} = binary_to_term(Data),
  411: 	    case Echos of
  412: 	    	0 ->
  413: 		    Pid ! {echo_complete, self()},
  414: 		    port_echo_loop(Port);
  415: 		Echos ->
  416: 	    	    port_command(Port, term_to_binary({Pid, Echos - 1, Msg})),
  417: 		    port_echo_loop(Port)
  418: 	    end;
  419: 	kill_em_all -> 
  420: 	    port_close(Port),
  421: 	    ok
  422:     end.
  423: 
  424: 
  425: 
  426: %%%
  427: %% Helpers
  428: %%%
  429: 
  430: start_load(N) ->
  431:    Pid = spawn_link(?MODULE, run_load, [N, []]),
  432:    {ok, Pid}.
  433: 
  434: 
  435: stop_load(Supervisor) ->
  436:     erlang:unlink(Supervisor),
  437:     exit(Supervisor, kill).
  438: 
  439: run_load(0, _Pids) ->
  440:     receive
  441: 	    % wait for an exit signal or a message then kill
  442: 	    % all associated processes.
  443: 	    _ -> exit(annihilated)
  444:     end;
  445: run_load(N, Pids) ->
  446:     Pid = spawn_link(?MODULE, list_load, []),
  447:     run_load(N - 1, [Pid | Pids]).
  448: 
  449: list_load() -> 
  450:     ok = case math:sin(random:uniform(32451)) of
  451:     	A when is_float(A) -> ok;
  452: 	_ -> ok
  453:     end,
  454:     list_load().
  455: 
  456: 
  457: has_scheduler_event(Events) ->
  458:     lists:any(
  459:     	fun (Pred) ->
  460: 	    case Pred of 
  461: 	    	{profile, scheduler, _ID, _Activity, _NR, _TS} -> true;
  462: 		_ -> false
  463: 	    end
  464: 	end, Events).
  465: 
  466: has_runnable_event(Events) ->
  467:     lists:any(
  468:     	fun (Pred) ->
  469: 	    case Pred of
  470: 	    	{profile, _Pid, _Activity, _MFA, _TS} -> true;
  471: 	    	_ -> false
  472: 	    end
  473:         end, Events).
  474: 
  475: has_profiler_pid_event([], _) -> false;
  476: has_profiler_pid_event([{profile, Pid, _Activity, _MFA, _TS}|Events], Pid) -> true;
  477: has_profiler_pid_event([_|Events], Pid) ->
  478:     has_profiler_pid_event(Events, Pid).
  479: 
  480: 
  481: wait(Time) -> receive after Time -> ok end.
  482: 
  483: %%%
  484: %%  Receivers
  485: %%%
  486: 
  487: %% Process receiver
  488: 
  489: 
  490: get_profiler_events() ->
  491:     Pid = get(profiler),
  492:     Pid ! {self(), get_events},
  493:     receive
  494:     	Events -> Events
  495:     end.
  496: 
  497: start_profiler_process() ->
  498:     Pid = spawn(?MODULE, profiler_process, [[]]),
  499:     put(profiler, Pid),
  500:     Pid.
  501: 
  502: profiler_process(Events) ->
  503:     receive 
  504: 	{Pid, get_events} -> 
  505: 	    Ref = erlang:trace_delivered(all),
  506: 	    profiler_process_followup(Pid, Events, Ref);
  507: 	Event -> 
  508: 	    profiler_process([Event | Events])
  509:     end.
  510: 
  511: profiler_process_followup(Pid, Events, Ref) ->
  512:     receive
  513: 	{trace_delivered,all,Ref} ->
  514: 	    Pid ! lists:reverse(Events);
  515: 	Event -> 
  516: 	    profiler_process_followup(Pid, [Event | Events], Ref)
  517:     end.
  518: 
  519: %% Port receiver
  520: 
  521: