|
|
- -module(lg_SUITE).
- -compile(export_all).
-
- -import(ct_helper, [config/2]).
- -import(ct_helper, [doc/1]).
-
- %% ct.
-
- all() ->
- [{group, all}].
-
- %% We cannot run the tests in parallel or they would
- %% interfere with each other.
- groups() ->
- [{all, [], ct_helper:all(?MODULE)}].
-
- %% Tests.
-
- app(Config) ->
- doc("Trace a specific application."),
- eTpf:trace({app, stdlib}, tpTracerFile, config(priv_dir, Config) ++ "/app.lz4"),
- lists:seq(1, 10),
- eTpf:stop(),
- do_ensure_decompress(config(priv_dir, Config) ++ "/app.lz4").
-
- callback(Config) ->
- doc("Trace using patterns from a callback function."),
- eTpf:trace({callback, ?MODULE, do_callback}, tpTracerFile,
- config(priv_dir, Config) ++ "/callback.lz4"),
- lists:seq(1, 10),
- eTpf:stop(),
- do_ensure_decompress(config(priv_dir, Config) ++ "/callback.lz4").
-
- do_callback() ->
- [{scope, [self()]}, lists].
-
- callgrind_running(Config) ->
- doc("Save events to files on disk then build callgrind files."),
- PrivDir = config(priv_dir, Config),
- eTpf:trace([{scope, [self()]}, ?MODULE, {app, stdlib}], tpTracerFile,
- PrivDir ++ "/callgrind_running.lz4",
- #{mode => profile, running => true}),
- do_callgrind_running(),
- eTpf:stop(),
- tpCallgrind:profile_many(
- PrivDir ++ "/callgrind_running.lz4.*",
- PrivDir ++ "/callgrind_running.out",
- #{running => true}),
- %% For debugging purposes, print the contents of the callgrind.out files.
- %% Uncomment for easier debugging, otherwise look into the files directly.
- % _ = [begin
- % {ok, File} = file:read_file(PrivDir ++ "/callgrind_running.out." ++ integer_to_list(N)),
- % io:format(user, "# callgrind_running.out.~p~n~s", [N, File]),
- % lg_file_reader:foreach(fun(E) -> io:format(user, "~p~n", [E]) end,
- % PrivDir ++ "/callgrind_running.lz4." ++ integer_to_list(N))
- % end || N <- lists:seq(1, erlang:system_info(schedulers))],
- ok.
-
- do_callgrind_running() ->
- timer:sleep(1000),
- Ref = make_ref(),
- erlang:send_after(1000, self(), {go, Ref}),
- lists:seq(1, 100),
- do_callgrind_running_receive(Ref),
- lists:seq(1, 100),
- ok.
-
- do_callgrind_running_receive(Ref) ->
- receive
- {go, Ref} ->
- ok
- end.
-
- callgrind_running_cycle(Config) ->
- doc("Save events to files on disk then build callgrind files. "
- "Create a recursive cycle using two functions calling each other."),
- PrivDir = config(priv_dir, Config),
- eTpf:trace([{scope, [self()]}, ?MODULE, {app, stdlib}], tpTracerFile,
- PrivDir ++ "/callgrind_running_cycle.lz4",
- #{mode => profile, running => true}),
- do_callgrind_running_cycle(),
- eTpf:stop(),
- tpCallgrind:profile_many(
- PrivDir ++ "/callgrind_running_cycle.lz4.*",
- PrivDir ++ "/callgrind_running_cycle.out",
- #{running => true}),
- %% For debugging purposes, print the contents of the callgrind.out files.
- %% Uncomment for easier debugging, otherwise look into the files directly.
- % _ = [begin
- % {ok, File} = file:read_file(PrivDir ++ "/callgrind_running_cycle.out." ++ integer_to_list(N)),
- % io:format(user, "# callgrind_running_cycle.out.~p~n~s", [N, File]),
- % lg_file_reader:foreach(fun(E) -> io:format(user, "~p~n", [E]) end,
- % PrivDir ++ "/callgrind_running_cycle.lz4." ++ integer_to_list(N))
- % end || N <- lists:seq(1, erlang:system_info(schedulers))],
- ok.
-
- do_callgrind_running_cycle() ->
- timer:sleep(1000),
- lists:seq(1, 100),
- do_callgrind_running_cycle1(do_callgrind_running_cycle_timer(20)),
- lists:seq(1, 100),
- ok.
-
- do_callgrind_running_cycle_timer(N) ->
- erlang:start_timer(N * 10, self(), N).
-
- do_callgrind_running_cycle1(Ref) ->
- receive
- {timeout, Ref, 0} ->
- ok;
- {timeout, Ref, N} when N rem 5 =:= 0 ->
- do_callgrind_running_cycle2(do_callgrind_running_cycle_timer(N - 1));
- {timeout, Ref, N} ->
- do_callgrind_running_cycle1(do_callgrind_running_cycle_timer(N - 1))
- end.
-
- do_callgrind_running_cycle2(Ref) ->
- receive
- {timeout, Ref, 0} ->
- ok;
- {timeout, Ref, N} when N rem 4 =:= 0 ->
- do_callgrind_running_cycle1(do_callgrind_running_cycle_timer(N - 1));
- {timeout, Ref, N} ->
- do_callgrind_running_cycle2(do_callgrind_running_cycle_timer(N - 1))
- end.
-
- file_tracer(Config) ->
- doc("Save events to files on disk."),
- eTpf:trace(lists, tpTracerFile, config(priv_dir, Config) ++ "/file_tracer.lz4"),
- lists:seq(1, 10),
- eTpf:stop(),
- do_ensure_decompress(config(priv_dir, Config) ++ "/file_tracer.lz4").
-
- file_tracer_rotation(Config) ->
- doc("Save events to files on disk; rotate the files if they get too big."),
- Prefix = config(priv_dir, Config) ++ "/file_tracer.lz4",
- eTpf:trace(lists, tpTracerFile, #{
- filename_prefix => Prefix,
- max_size => 100, %% Intentionally low.
- events_per_frame => 10 %% Needed to trigger the rotation, default is too high.
- }),
- lists:seq(1, 1000),
- eTpf:stop(),
- %% We should have one or more rotated files.
- Result = [begin
- Filename = Prefix ++ "." ++ integer_to_list(N) ++ ".bak",
- filelib:is_file(Filename)
- end || N <- lists:seq(1, erlang:system_info(schedulers))],
- true = lists:member(true, lists:usort(Result)),
- ok.
-
- mod(Config) ->
- doc("Trace a specific module."),
- eTpf:trace(lists, tpTracerFile, config(priv_dir, Config) ++ "/mod.lz4"),
- lists:seq(1, 10),
- eTpf:stop(),
- do_ensure_decompress(config(priv_dir, Config) ++ "/mod.lz4").
-
- profile_mode(Config) ->
- doc("Trace a specific module in profile mode."),
- eTpf:trace(lists, tpTracerFile, config(priv_dir, Config) ++ "/profile_mode.lz4",
- #{mode => profile}),
- lists:seq(1, 10),
- eTpf:stop(),
- do_ensure_decompress(config(priv_dir, Config) ++ "/profile_mode.lz4").
-
- raw_console_tracer(_) ->
- doc("Print raw events to the console."),
- ct:print("Start tracing to the console."),
- %% @todo It seems the order matters when starting. Should it?
- eTpf:trace([{scope, [self()]}, lists]),
- lists:seq(1, 10),
- eTpf:stop(),
- ct:print("Stop tracing to the console.").
-
- running_true(Config) ->
- doc("Trace a specific module with running option enabled."),
- eTpf:trace(lists, tpTracerFile, config(priv_dir, Config) ++ "/running_true.lz4",
- #{running => true}),
- lists:seq(1, 10),
- eTpf:stop(),
- do_ensure_decompress(config(priv_dir, Config) ++ "/running_true.lz4").
-
- send_true(Config) ->
- doc("Trace a specific module with send option enabled."),
- eTpf:trace(lists, tpTracerFile, config(priv_dir, Config) ++ "/send_true.lz4",
- #{send => true}),
- Self = self(),
- %% Send a message to and from an existing process.
- Pid = spawn(fun() ->
- receive {msg_from, Self} ->
- Self ! {msg_from, self()}
- end
- end),
- Pid ! {msg_from, Self},
- receive {msg_from, Pid} -> ok end,
- %% Also send a message to a non existing process.
- DeadPid = spawn(fun() -> ok end),
- receive after 100 -> ok end,
- DeadPid ! {msg_from, Self},
- eTpf:stop(),
- do_ensure_decompress(config(priv_dir, Config) ++ "/send_true.lz4").
-
- socket_tracer(_) ->
- doc("Send events to a socket."),
- Port = 61234,
- eTpf:trace(lists, tpTracerSocket, Port, #{pool_size => 1}),
- {ok, Socket} = gen_tcp:connect("localhost", Port,
- [binary, {packet, 2}, {active, true}]),
- lists:seq(1, 10),
- eTpf:stop(),
- do_socket_tracer_recv(Socket).
-
- socket_tracer_client(Config) ->
- doc("Send events to a socket client."),
- Port = 61234,
- eTpf:trace(lists, tpTracerSocket, Port, #{pool_size => 1}),
- BaseFilename = config(priv_dir, Config) ++ "/socket_tracer_client.lz4",
- {ok, Pid} = tpSocketCli:start_link(Port, BaseFilename),
- timer:sleep(1000),
- lists:seq(1, 10),
- eTpf:stop(),
- tpSocketCli:stop(Pid),
- {ok, File} = file:read_file(BaseFilename ++ ".0"),
- _ = lz4f:decompress(File),
- true = filelib:file_size(BaseFilename ++ ".0") > 0,
- ok.
-
- socket_tracer_many(_) ->
- doc("Send events to many sockets."),
- Port = 61234,
- eTpf:trace(lists, tpTracerSocket, Port, #{pool_size => 5}),
- {ok, _} = gen_tcp:connect("localhost", Port, []),
- {ok, _} = gen_tcp:connect("localhost", Port + 1, []),
- {ok, _} = gen_tcp:connect("localhost", Port + 2, []),
- {ok, _} = gen_tcp:connect("localhost", Port + 3, []),
- {ok, _} = gen_tcp:connect("localhost", Port + 4, []),
- {error, _} = gen_tcp:connect("localhost", Port + 5, []),
- eTpf:stop().
-
- socket_tracer_reconnect(_) ->
- doc("Confirm we can reconnect to the tracer."),
- Port = 61234,
- eTpf:trace(lists, tpTracerSocket, Port, #{pool_size => 1}),
- {ok, Socket0} = gen_tcp:connect("localhost", Port,
- [binary, {packet, 2}, {active, true}]),
- ok = gen_tcp:close(Socket0),
- {ok, Socket} = gen_tcp:connect("localhost", Port,
- [binary, {packet, 2}, {active, true}]),
- lists:seq(1, 10),
- eTpf:stop(),
- do_socket_tracer_recv(Socket).
-
- do_socket_tracer_recv(Socket) ->
- receive
- {tcp, Socket, Data} ->
- Term = binary_to_term(Data),
- true = is_tuple(Term),
- do_socket_tracer_recv(Socket);
- {tcp_closed, Socket} ->
- ok
- after 1000 ->
- error(timeout)
- end.
-
- stop_while_trace_is_running(Config) ->
- doc("Stop tracing while events are still coming in."),
- Self = self(),
- Pid = spawn_link(fun() -> Self ! {self(), continue}, lists:seq(1, 10000000) end),
- eTpf:trace([{scope, [Pid]}, lists], tpTracerFile,
- config(priv_dir, Config) ++ "/stop_while_trace_is_running.lz4"),
- receive {Pid, continue} -> ok after 100 -> error(timeout) end,
- eTpf:stop(),
- do_ensure_decompress(config(priv_dir, Config) ++ "/stop_while_trace_is_running.lz4").
-
- %% Internal.
-
- do_ensure_decompress(Prefix) ->
- %% Ensure the files can be decompressed.
- Sizes = [begin
- Filename = Prefix ++ "." ++ integer_to_list(N),
- {ok, File} = file:read_file(Filename),
- _ = lz4f:decompress(File),
- filelib:file_size(Filename)
- end || N <- lists:seq(1, erlang:system_info(schedulers))],
- %% We also need to make sure there is actual data in the files,
- %% as lz4f:decompress will succeed when provided with no data.
- true = 0 < lists:sum(Sizes),
- ok.
|