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: