1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(tracer_SUITE). 22 23%%% 24%%% Tests the tracer module interface 25%%% 26 27-export([all/0, suite/0,groups/0, init_per_suite/1, end_per_suite/1, 28 init_per_group/2,end_per_group/2, init_per_testcase/2, 29 end_per_testcase/2]). 30-export([load/1, unload/1, reload/1, invalid_tracers/1]). 31-export([send/1, recv/1, call/1, call_return/1, spawn/1, exit/1, 32 link/1, unlink/1, getting_linked/1, getting_unlinked/1, 33 register/1, unregister/1, in/1, out/1, gc_start/1, gc_end/1, 34 seq_trace/1]). 35 36suite() -> [{ct_hooks,[ts_install_cth]}, 37 {timetrap, {minutes, 1}}]. 38 39all() -> 40 [load, unload, reload, invalid_tracers, {group, basic}]. 41 42groups() -> 43 [{ basic, [], [send, recv, call, call_return, spawn, exit, 44 link, unlink, getting_linked, getting_unlinked, 45 register, unregister, in, out, gc_start, gc_end, 46 seq_trace]}]. 47 48init_per_suite(Config) -> 49 erlang:trace_pattern({'_','_','_'}, false, [local]), 50 erlang:trace_pattern({'_','_','_'}, false, []), 51 purge(), 52 Config. 53 54end_per_suite(_Config) -> 55 ok. 56 57init_per_group(_GroupName, Config) -> 58 Config. 59 60end_per_group(_GroupName, Config) -> 61 Config. 62 63init_per_testcase(TC, Config) when TC =:= load; TC =:= reload -> 64 65 DataDir = proplists:get_value(data_dir, Config), 66 67 Pid = erlang:spawn(fun F() -> 68 receive 69 {get, Pid} -> 70 Pid ! DataDir, 71 F() 72 end 73 end), 74 register(tracer_test_config, Pid), 75 common_init_per_testcase(Config); 76init_per_testcase(_, Config) -> 77 DataDir = proplists:get_value(data_dir, Config), 78 case catch tracer_test:enabled(trace_status, self(), self()) of 79 discard -> 80 ok; 81 _ -> 82 tracer_test:load(DataDir) 83 end, 84 common_init_per_testcase(Config). 85 86common_init_per_testcase(Config) -> 87 Killer = erlang:spawn(fun() -> killer_loop([]) end), 88 register(killer_process, Killer), 89 Config. 90 91end_per_testcase(TC, _Config) when TC =:= load; TC =:= reload -> 92 purge(), 93 exit(whereis(tracer_test_config), kill), 94 kill_processes(); 95end_per_testcase(_, _Config) -> 96 purge(), 97 kill_processes(). 98 99kill_processes() -> 100 killer_process ! {get_pids,self()}, 101 receive 102 {pids_to_kill,Pids} -> ok 103 end, 104 _ = [begin 105 case erlang:is_process_alive(P) of 106 true -> 107 io:format("Killing ~p\n", [P]); 108 false -> 109 ok 110 end, 111 erlang:unlink(P), 112 exit(P, kill) 113 end || P <- Pids], 114 ok. 115 116killer_loop(Pids) -> 117 receive 118 {add_pid,Pid} -> 119 killer_loop([Pid|Pids]); 120 {get_pids,To} -> 121 To ! {pids_to_kill,Pids} 122 end. 123 124kill_me(Pid) -> 125 killer_process ! {add_pid,Pid}, 126 Pid. 127 128%%% Test cases follow. 129 130load(_Config) -> 131 purge(), 132 1 = erlang:trace(self(), true, [{tracer, tracer_test, []}, call]), 133 purge(), 134 1 = erlang:trace_pattern({?MODULE, all, 0}, [], 135 [{meta, tracer_test, []}]), 136 ok. 137 138unload(_Config) -> 139 140 ServerFun = fun F(0, undefined) -> 141 receive 142 {N, Pid} -> F(N, Pid) 143 end; 144 F(0, Pid) -> 145 Pid ! done, 146 F(0, undefined); 147 F(N, Pid) -> 148 ?MODULE:all(), 149 F(N-1, Pid) 150 end, 151 152 Pid = erlang:spawn_link(fun() -> ServerFun(0, undefined) end), 153 154 Tc = fun(N) -> 155 Pid ! {N, self()}, 156 receive done -> ok after 1000 -> ct:fail(timeout) end, 157 trace_delivered(Pid) 158 end, 159 160 1 = erlang:trace(Pid, true, [{tracer, tracer_test, 161 {#{ call => trace }, self(), []}}, 162 call]), 163 1 = erlang:trace_pattern({?MODULE, all, 0}, [], []), 164 165 Tc(1), 166 receive _M -> ok after 0 -> ct:fail(timeout) end, 167 receive M0 -> ct:fail({unexpected_message0, M0}) after 0 -> ok end, 168 169 code:purge(tracer_test), 170 code:delete(tracer_test), 171 172 Tc(1), 173 receive M1 -> ct:fail({unexpected_message1, M1}) after 0 -> ok end, 174 175 code:purge(tracer_test), 176 177 Tc(1), 178 receive M2 -> ct:fail({unexpected_message2, M2}) after 0 -> ok end, 179 180 ok. 181 182%% This testcase is here to make sure there are not 183%% segfaults when reloading the current nifs. 184reload(_Config) -> 185 186 Tracer = spawn_opt(fun F() -> receive _M -> F() end end, 187 [{message_queue_data, off_heap}]), 188 erlang:link(Tracer), 189 Tracee = spawn_link(fun reload_loop/0), 190 191 [begin 192 Ref = make_ref(), 193 State = {#{ call => trace }, Tracer, [Ref]}, 194 erlang:trace(Tracee, true, [{tracer, tracer_test,State}, call]), 195 erlang:trace_pattern({?MODULE, all, 0}, []), 196 197 false = code:purge(tracer_test), 198 {module, _} = code:load_file(tracer_test), 199 200 %% There is a race involved in between when the internal nif cache 201 %% is purged and when the reload_loop needs the tracer module 202 %% so the tracer may be removed or still there. 203 case erlang:trace_info(Tracee, tracer) of 204 {tracer, []} -> ok; 205 {tracer, {tracer_test, State}} -> ok 206 end, 207 208 false = code:purge(tracer_test), 209 true = code:delete(tracer_test), 210 false = code:purge(tracer_test), 211 timer:sleep(10) 212 end || _ <- lists:seq(1,15)], 213 214 ok. 215 216reload_loop() -> 217 ?MODULE:all(), 218 ?MODULE:all(), 219 ?MODULE:all(), 220 ?MODULE:all(), 221 ?MODULE:all(), 222 timer:sleep(1), 223 reload_loop(). 224 225invalid_tracers(_Config) -> 226 FailTrace = fun(A) -> 227 try erlang:trace(self(), true, A) of 228 _ -> ct:fail(A) 229 catch _:_ -> ok end 230 end, 231 232 FailTrace([{tracer, foobar}, call]), 233 FailTrace([{tracer, foobar, []}, call]), 234 FailTrace([{tracer, make_ref(), []}, call]), 235 FailTrace([{tracer, lists, []}, call]), 236 237 FailTP = fun(MS,FL) -> 238 try erlang:trace_pattern({?MODULE,all,0}, MS, FL) of 239 _ -> ct:fail({MS, FL}) 240 catch _:_ -> ok end 241 end, 242 243 FailTP([],[{meta, foobar}]), 244 FailTP([],[{meta, foobar, []}]), 245 FailTP([],[{meta, make_ref(), []}]), 246 FailTP([],[{meta, lists, []}]), 247 248 ok. 249 250 251 252send(_Config) -> 253 254 Self = self(), 255 Tc = fun(Pid) -> 256 Pid ! fun() -> Self ! ok end, 257 receive ok -> ok after 100 -> ct:fail(timeout) end 258 end, 259 260 Expect = fun(Pid, State, EOpts) -> 261 receive 262 Msg -> 263 {send, State, Pid, ok, Opts} = Msg, 264 check_opts(EOpts, Opts, Self) 265 end 266 end, 267 test(send, Tc, Expect). 268 269 270recv(_Config) -> 271 272 Tc = fun(Pid) -> 273 Pid ! ok 274 end, 275 276 Expect = fun(Pid, State, EOpts) -> 277 receive 278 Msg -> 279 {'receive', State, Pid, ok, Opts} = Msg, 280 check_opts(EOpts, Opts) 281 end 282 end, 283 284 test('receive', Tc, Expect, false). 285 286call(_Config) -> 287 288 Self = self(), 289 Tc = fun(Pid) -> 290 Pid ! fun() -> call_test(Self), Self ! ok end, 291 receive ok -> ok after 100 -> ct:fail(timeout) end 292 end, 293 294 erlang:trace_pattern({?MODULE, call_test, 1}, [], [local]), 295 296 Expect = fun(Pid, State, EOpts) -> 297 receive 298 Msg -> 299 {call, State, Pid, {?MODULE, call_test, [Self]}, Opts} = Msg, 300 check_opts(EOpts, Opts) 301 end 302 end, 303 test(call, Tc, Expect). 304 305call_return(_Config) -> 306 307 Self = self(), 308 Tc = fun(Pid) -> 309 Pid ! fun() -> call_test(undefined), Self ! ok end, 310 receive ok -> ok after 100 -> ct:fail(timeout) end 311 end, 312 313 1 = erlang:trace_pattern({?MODULE, call_test, 1}, [{'_',[],[{return_trace}]}], [local]), 314 315 Expect = fun(Pid, State, EOpts) -> 316 receive 317 CallMsg -> 318 {call, State, Pid, {?MODULE, call_test, [undefined]}, COpts} = CallMsg, 319 check_opts(EOpts, COpts) 320 end, 321 receive 322 RetMsg -> 323 {return_from, State, Pid, {?MODULE, call_test, 1}, ROpts} = RetMsg, 324 check_opts(EOpts, ROpts, undefined) 325 end 326 end, 327 test(call, Tc, Expect). 328 329call_test(Arg) -> 330 Arg. 331 332spawn(_Config) -> 333 334 Tc = fun(Pid) -> 335 Pid ! fun() -> kill_me(erlang:spawn(lists,seq,[1,10])), ok end 336 end, 337 338 Expect = 339 fun(Pid, State, EOpts) -> 340 receive 341 Msg -> 342 {spawn, State, Pid, NewPid, Opts} = Msg, 343 check_opts(EOpts, Opts, {lists,seq,[1,10]}), 344 true = is_pid(NewPid) andalso NewPid /= Pid 345 end 346 end, 347 348 test(spawn, procs, Tc, Expect, false). 349 350exit(_Config) -> 351 Tc = fun(Pid) -> 352 Pid ! fun() -> exit end 353 end, 354 355 Expect = 356 fun(Pid, State, EOpts) -> 357 receive 358 Msg -> 359 {exit, State, Pid, normal, Opts} = Msg, 360 check_opts(EOpts, Opts) 361 end 362 end, 363 364 test(exit, procs, Tc, Expect, true, true). 365 366link(_Config) -> 367 368 Tc = fun(Pid) -> 369 Pid ! fun() -> 370 SPid = erlang:spawn(fun() -> receive _ -> ok end end), 371 erlang:link(SPid), 372 ok 373 end 374 end, 375 376 Expect = 377 fun(Pid, State, EOpts) -> 378 receive 379 Msg -> 380 {link, State, Pid, NewPid, Opts} = Msg, 381 check_opts(EOpts, Opts), 382 true = is_pid(NewPid) andalso NewPid /= Pid 383 end 384 end, 385 386 test(link, procs, Tc, Expect, false). 387 388unlink(_Config) -> 389 390 Tc = fun(Pid) -> 391 Pid ! fun() -> 392 SPid = erlang:spawn(fun() -> receive _ -> ok end end), 393 erlang:link(SPid), 394 erlang:unlink(SPid), 395 kill_me(SPid), 396 ok 397 end 398 end, 399 400 Expect = 401 fun(Pid, State, EOpts) -> 402 receive 403 Msg -> 404 {unlink, State, Pid, NewPid, Opts} = Msg, 405 check_opts(EOpts, Opts), 406 true = is_pid(NewPid) andalso NewPid /= Pid 407 end 408 end, 409 410 test(unlink, procs, Tc, Expect, false). 411 412getting_linked(_Config) -> 413 414 Tc = fun(Pid) -> 415 Pid ! fun() -> 416 Self = self(), 417 erlang:spawn(fun() -> erlang:link(Self) end), 418 ok 419 end 420 end, 421 422 Expect = 423 fun(Pid, State, EOpts) -> 424 receive 425 Msg -> 426 {getting_linked, State, Pid, NewPid, Opts} = Msg, 427 check_opts(EOpts, Opts), 428 true = is_pid(NewPid) andalso NewPid /= Pid 429 end 430 end, 431 432 test(getting_linked, procs, Tc, Expect, false). 433 434getting_unlinked(_Config) -> 435 Tc = fun(Pid) -> 436 Pid ! fun() -> 437 Self = self(), 438 erlang:spawn(fun() -> 439 erlang:link(Self), 440 erlang:unlink(Self) 441 end), 442 ok 443 end 444 end, 445 446 Expect = 447 fun(Pid, State, EOpts) -> 448 receive 449 Msg -> 450 {getting_unlinked, State, Pid, NewPid, Opts} = Msg, 451 check_opts(EOpts, Opts), 452 true = is_pid(NewPid) andalso NewPid /= Pid 453 end 454 end, 455 456 test(getting_unlinked, procs, Tc, Expect, false). 457 458register(_Config) -> 459 460 Tc = fun(Pid) -> 461 Pid ! fun() -> 462 erlang:register(?MODULE, self()), 463 erlang:unregister(?MODULE), 464 ok 465 end 466 end, 467 468 Expect = 469 fun(Pid, State, EOpts) -> 470 receive 471 Msg -> 472 {register, State, Pid, ?MODULE, Opts} = Msg, 473 check_opts(EOpts, Opts) 474 end 475 end, 476 477 test(register, procs, Tc, Expect, false). 478 479unregister(_Config) -> 480 481 Tc = fun(Pid) -> 482 Pid ! fun() -> 483 erlang:register(?MODULE, self()), 484 erlang:unregister(?MODULE), 485 ok 486 end 487 end, 488 489 Expect = 490 fun(Pid, State, EOpts) -> 491 receive 492 Msg -> 493 {unregister, State, Pid, ?MODULE, Opts} = Msg, 494 check_opts(EOpts, Opts) 495 end 496 end, 497 498 test(unregister, procs, Tc, Expect, false). 499 500in(_Config) -> 501 502 Tc = fun(Pid) -> 503 Self = self(), 504 Pid ! fun() -> receive after 10 -> Self ! ok end end, 505 receive ok -> ok end 506 end, 507 508 Expect = 509 fun(Pid, State, EOpts) -> 510 N = (fun F(N) -> 511 receive 512 Msg -> 513 {in, State, Pid, _, Opts} = Msg, 514 check_opts(EOpts, Opts), 515 F(N+1) 516 after 0 -> N 517 end 518 end)(0), 519 true = N > 0 520 end, 521 522 test(in, running, Tc, Expect, false). 523 524out(_Config) -> 525 Tc = fun(Pid) -> 526 Pid ! fun() -> receive after 10 -> exit end end, 527 Ref = erlang:monitor(process, Pid), 528 receive {'DOWN', Ref, _, _, _} -> ok end 529 end, 530 531 Expect = 532 fun(Pid, State, EOpts) -> 533 %% We cannot predict how many out schedules there will be 534 N = (fun F(N) -> 535 receive 536 Msg -> 537 {out, State, Pid, _, Opts} = Msg, 538 check_opts(EOpts, Opts), 539 F(N+1) 540 after 0 -> N 541 end 542 end)(0), 543 true = N > 0 544 end, 545 546 test(out, running, Tc, Expect, false, true). 547 548gc_start(_Config) -> 549 550 Tc = fun(Pid) -> 551 Pid ! fun() -> 552 erlang:garbage_collect(), 553 ok 554 end 555 end, 556 557 Expect = 558 fun(Pid, State, EOpts) -> 559 receive 560 Msg -> 561 {gc_major_start, State, Pid, _, Opts} = Msg, 562 check_opts(EOpts, Opts) 563 end 564 end, 565 566 test(gc_major_start, garbage_collection, Tc, Expect, false). 567 568gc_end(_Config) -> 569 570 Tc = fun(Pid) -> 571 Pid ! fun() -> 572 erlang:garbage_collect(), 573 ok 574 end 575 end, 576 577 Expect = 578 fun(Pid, State, EOpts) -> 579 receive 580 Msg -> 581 {gc_major_end, State, Pid, _, Opts} = Msg, 582 check_opts(EOpts, Opts) 583 end 584 end, 585 586 test(gc_major_end, garbage_collection, Tc, Expect, false). 587 588seq_trace(_Config) -> 589 590 seq_trace:set_system_tracer({tracer_test, 591 {#{ seq_trace => trace }, self(), []}}), 592 erlang:spawn(fun() -> 593 seq_trace:set_token(label,17), 594 seq_trace:set_token(print,true), 595 seq_trace:print(17,"**** Trace Started ****") 596 end), 597 receive 598 {seq_trace, _, 17, {print, _, _, _, _}, _} -> 599 ok; 600 M -> 601 ct:fail("~p~n",[M]) 602 after 100 -> 603 ct:fail(timeout) 604 end. 605 606test(Event, Tc, Expect) -> 607 test(Event, Tc, Expect, false). 608test(Event, Tc, Expect, Removes) -> 609 test(Event, Event, Tc, Expect, Removes). 610test(Event, TraceFlag, Tc, Expect, Removes) -> 611 test(Event, TraceFlag, Tc, Expect, Removes, false). 612test(Event, TraceFlag, Tc, Expect, _Removes, Dies) -> 613 614 ComplexState = {fun() -> ok end, <<0:(128*8)>>}, 615 Opts = #{ }, 616 617 %% Test that trace works 618 State1 = {#{ Event => trace }, self(), ComplexState}, 619 Pid1 = start_tracee(), 620 1 = erlang:trace(Pid1, true, [TraceFlag, {tracer, tracer_test, State1}]), 621 Tc(Pid1), 622 ok = trace_delivered(Pid1), 623 624 Expect(Pid1, State1, Opts), 625 receive M11 -> ct:fail({unexpected, M11}) after 0 -> ok end, 626 if not Dies andalso Event /= in -> 627 {flags, [TraceFlag]} = erlang:trace_info(Pid1, flags), 628 {tracer, {tracer_test, State1}} = erlang:trace_info(Pid1, tracer), 629 erlang:trace(Pid1, false, [TraceFlag]); 630 true -> ok 631 end, 632 633 %% Test that trace works with scheduler id and timestamp 634 Pid1T = start_tracee(), 635 1 = erlang:trace(Pid1T, true, [TraceFlag, {tracer, tracer_test, State1}, 636 timestamp, scheduler_id]), 637 Tc(Pid1T), 638 ok = trace_delivered(Pid1T), 639 640 Expect(Pid1T, State1, Opts#{ scheduler_id => number, 641 timestamp => timestamp}), 642 receive M11T -> ct:fail({unexpected, M11T}) after 0 -> ok end, 643 if not Dies andalso Event /= in -> 644 {flags, [scheduler_id, TraceFlag, timestamp]} 645 = erlang:trace_info(Pid1T, flags), 646 {tracer, {tracer_test, State1}} = erlang:trace_info(Pid1T, tracer), 647 erlang:trace(Pid1T, false, [TraceFlag]); 648 true -> ok 649 end, 650 651 %% Test that discard works 652 Pid2 = start_tracee(), 653 State2 = {#{ Event => discard }, self(), ComplexState}, 654 1 = erlang:trace(Pid2, true, [TraceFlag, {tracer, tracer_test, State2}]), 655 Tc(Pid2), 656 ok = trace_delivered(Pid2), 657 receive M2 -> ct:fail({unexpected, M2}) after 0 -> ok end, 658 if not Dies andalso Event /= in -> 659 {flags, [TraceFlag]} = erlang:trace_info(Pid2, flags), 660 {tracer, {tracer_test, State2}} = erlang:trace_info(Pid2, tracer), 661 erlang:trace(Pid2, false, [TraceFlag]); 662 true -> 663 ok 664 end, 665 666 ok. 667 668check_opts(E, O, Extra) -> 669 check_opts(E#{ extra => Extra }, O). 670check_opts(#{ scheduler_id := number } = E, #{ scheduler_id := N } = O) 671 when is_integer(N) -> 672 E1 = maps:remove(scheduler_id, E), 673 O1 = maps:remove(scheduler_id, O), 674 if E1 == O1 -> ok; 675 true -> ct:fail({invalid_opts, E, O}) 676 end; 677check_opts(Opts, Opts) -> 678 ok; 679check_opts(E,O) -> 680 ct:fail({invalid_opts, E, O}). 681 682start_tracee() -> 683 spawn_link( 684 fun F() -> 685 receive 686 Action when is_function(Action) -> 687 case Action() of 688 ok -> 689 F(); 690 Err -> 691 Err 692 end; 693 _ -> 694 F() 695 end 696 end). 697 698trace_delivered(Pid) -> 699 Ref = erlang:trace_delivered(Pid), 700 receive 701 {trace_delivered, Pid, Ref} -> 702 ok 703 after 1000 -> 704 timeout 705 end. 706 707purge() -> 708 %% Make sure module is not loaded 709 case erlang:module_loaded(tracer_test) of 710 true -> 711 code:purge(tracer_test), 712 true = code:delete(tracer_test), 713 code:purge(tracer_test); 714 _ -> 715 ok 716 end. 717