%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
-module(lttng_SUITE).
-export([all/0, suite/0]).
-export([init_per_suite/1, end_per_suite/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
-export([t_lttng_list/1,
t_carrier_pool/1,
t_memory_carrier/1,
t_async_io_pool/1,
t_driver_control_ready_async/1,
t_driver_start_stop/1,
t_driver_ready_input_output/1,
t_driver_timeout/1,
t_driver_caller/1,
t_driver_flush/1,
t_scheduler_poll/1]).
-include_lib("common_test/include/ct.hrl").
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {seconds, 10}}].
all() ->
[t_lttng_list,
t_memory_carrier,
t_carrier_pool,
t_async_io_pool,
t_driver_start_stop,
t_driver_ready_input_output,
t_driver_control_ready_async,
t_driver_timeout,
t_driver_caller,
t_driver_flush,
t_scheduler_poll].
init_per_suite(Config) ->
case erlang:system_info(dynamic_trace) of
lttng ->
ensure_lttng_stopped("--all"),
Config;
_ ->
{skip, "No LTTng configured on system."}
end.
end_per_suite(_Config) ->
ensure_lttng_stopped("--all"),
ok.
init_per_testcase(Case, Config) ->
Name = atom_to_list(Case),
ok = ensure_lttng_started(Name, Config),
[{session, Name}|Config].
end_per_testcase(Case, _Config) ->
Name = atom_to_list(Case),
ok = ensure_lttng_stopped(Name),
ok.
%% Not tested yet
%% org_erlang_otp:driver_process_exit
%% org_erlang_otp:driver_event
%% tracepoints
%%
%% org_erlang_otp:carrier_pool_get
%% org_erlang_otp:carrier_pool_put
%% org_erlang_otp:carrier_destroy
%% org_erlang_otp:carrier_create
%% org_erlang_otp:aio_pool_put
%% org_erlang_otp:aio_pool_get
%% org_erlang_otp:driver_control
%% org_erlang_otp:driver_call
%% org_erlang_otp:driver_finish
%% org_erlang_otp:driver_ready_async
%% org_erlang_otp:driver_process_exit
%% org_erlang_otp:driver_stop
%% org_erlang_otp:driver_flush
%% org_erlang_otp:driver_stop_select
%% org_erlang_otp:driver_timeout
%% org_erlang_otp:driver_event
%% org_erlang_otp:driver_ready_output
%% org_erlang_otp:driver_ready_input
%% org_erlang_otp:driver_output
%% org_erlang_otp:driver_outputv
%% org_erlang_otp:driver_init
%% org_erlang_otp:driver_start
%% org_erlang_otp:scheduler_poll
%%
%% Testcases
%%
t_lttng_list(_Config) ->
{ok, _} = cmd("lttng list -u"),
ok.
%% org_erlang_otp:carrier_pool_get
%% org_erlang_otp:carrier_pool_put
t_carrier_pool(Config) ->
case have_carriers(ets_alloc) of
false ->
{skip, "No Memory Carriers configured on system."};
true ->
ok = lttng_start_event("org_erlang_otp:carrier_pool*", Config),
ok = ets_load(),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:carrier_pool_get", Res),
ok = check_tracepoint("org_erlang_otp:carrier_pool_put", Res),
ok
end.
%% org_erlang_otp:carrier_destroy
%% org_erlang_otp:carrier_create
t_memory_carrier(Config) ->
case have_carriers(ets_alloc) of
false ->
{skip, "No Memory Carriers configured on system."};
true ->
ok = lttng_start_event("org_erlang_otp:carrier_*", Config),
ok = ets_load(),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:carrier_destroy", Res),
ok = check_tracepoint("org_erlang_otp:carrier_create", Res),
ok
end.
%% org_erlang_otp:aio_pool_put
%% org_erlang_otp:aio_pool_get
t_async_io_pool(Config) ->
case have_async_threads() of
false ->
{skip, "No Async Threads configured on system."};
true ->
ok = lttng_start_event("org_erlang_otp:aio_pool_*", Config),
Path1 = proplists:get_value(priv_dir, Config),
{ok, [[Path2]]} = init:get_argument(home),
{ok, _} = file:list_dir(Path1),
{ok, _} = file:list_dir(Path2),
{ok, _} = file:list_dir(Path1),
{ok, _} = file:list_dir(Path2),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:aio_pool_put", Res),
ok = check_tracepoint("org_erlang_otp:aio_pool_get", Res),
ok
end.
%% org_erlang_otp:driver_start
%% org_erlang_otp:driver_stop
t_driver_start_stop(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_*", Config),
timer:sleep(500),
Path = proplists:get_value(priv_dir, Config),
Name = filename:join(Path, "sometext.txt"),
Bin = txt(),
ok = file:write_file(Name, Bin),
{ok, Bin} = file:read_file(Name),
timer:sleep(500),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:driver_start", Res),
ok = check_tracepoint("org_erlang_otp:driver_stop", Res),
ok = check_tracepoint("org_erlang_otp:driver_control", Res),
ok = check_tracepoint("org_erlang_otp:driver_outputv", Res),
ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res),
ok.
%% org_erlang_otp:driver_control
%% org_erlang_otp:driver_outputv
%% org_erlang_otp:driver_ready_async
t_driver_control_ready_async(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_control", Config),
ok = lttng_start_event("org_erlang_otp:driver_outputv", Config),
ok = lttng_start_event("org_erlang_otp:driver_ready_async", Config),
Path = proplists:get_value(priv_dir, Config),
Name = filename:join(Path, "sometext.txt"),
Bin = txt(),
ok = file:write_file(Name, Bin),
{ok, Bin} = file:read_file(Name),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:driver_control", Res),
ok = check_tracepoint("org_erlang_otp:driver_outputv", Res),
ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res),
ok.
%% org_erlang_otp:driver_ready_input
%% org_erlang_otp:driver_ready_output
t_driver_ready_input_output(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_ready_*", Config),
timer:sleep(500),
Me = self(),
Pid = spawn_link(fun() -> tcp_server(Me, active) end),
receive {Pid, accept} -> ok end,
Bin = txt(),
Sz = byte_size(Bin),
{ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2}]),
ok = gen_tcp:send(Sock, <<Sz:16, Bin/binary>>),
ok = gen_tcp:send(Sock, <<Sz:16, Bin/binary>>),
ok = gen_tcp:close(Sock),
receive {Pid, done} -> ok end,
timer:sleep(500),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:driver_ready_input", Res),
ok = check_tracepoint("org_erlang_otp:driver_ready_output", Res),
ok.
%% org_erlang_otp:driver_stop_select
%% org_erlang_otp:driver_timeout
t_driver_timeout(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_*", Config),
Me = self(),
Pid = spawn_link(fun() -> tcp_server(Me, timeout) end),
receive {Pid, accept} -> ok end,
{ok, Sock} = gen_tcp:connect("localhost", 5679, [binary]),
ok = gen_tcp:send(Sock, <<"hej">>),
receive {Pid, done} -> ok end,
ok = gen_tcp:close(Sock),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:driver_timeout", Res),
ok = check_tracepoint("org_erlang_otp:driver_stop_select", Res),
ok.
%% org_erlang_otp:driver_call
%% org_erlang_otp:driver_output
%% org_erlang_otp:driver_init
%% org_erlang_otp:driver_finish
t_driver_caller(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_*", Config),
Drv = 'caller_drv',
os:putenv("CALLER_DRV_USE_OUTPUTV", "false"),
ok = load_driver(proplists:get_value(data_dir, Config), Drv),
Port = open_port({spawn, Drv}, []),
true = is_port(Port),
chk_caller(Port, start, self()),
chk_caller(Port, output, spawn_link(fun() ->
port_command(Port, "")
end)),
Port ! {self(), {command, ""}},
chk_caller(Port, output, self()),
chk_caller(Port, control, spawn_link(fun () ->
port_control(Port, 0, "")
end)),
chk_caller(Port, call, spawn_link(fun() ->
erlang:port_call(Port, 0, "")
end)),
true = port_close(Port),
erl_ddll:unload_driver(Drv),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:driver_call", Res),
ok = check_tracepoint("org_erlang_otp:driver_output", Res),
ok = check_tracepoint("org_erlang_otp:driver_init", Res),
ok = check_tracepoint("org_erlang_otp:driver_finish", Res),
ok.
%% org_erlang_otp:scheduler_poll
t_scheduler_poll(Config) ->
ok = lttng_start_event("org_erlang_otp:scheduler_poll", Config),
ok = memory_load(),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:scheduler_poll", Res),
ok.
%% org_erlang_otp:driver_flush
t_driver_flush(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_flush", Config),
Me = self(),
Pid = spawn_link(fun() -> tcp_server(Me, passive_no_read) end),
receive {Pid, accept} -> ok end,
Bin = iolist_to_binary([txt() || _ <- lists:seq(1,100)]),
Sz = byte_size(Bin),
%% We want to create a scenario where sendings stalls and we
%% queue packets in the driver.
%% When we close the socket it has to flush the queue.
{ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2},
{send_timeout, 10},
{sndbuf, 10000000}]),
Pids = [spawn_link(fun() ->
gen_tcp:send(Sock, <<Sz:16, Bin/binary>>),
Me ! {self(), ok}
end) || _ <- lists:seq(1,100)],
[receive {P, ok} -> ok end || P <- Pids],
ok = gen_tcp:close(Sock),
Pid ! die,
receive {Pid, done} -> ok end,
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:driver_flush", Res),
ok.
%%
%% AUX
%%
chk_caller(Port, Callback, ExpectedCaller) ->
receive
{caller, Port, Callback, Caller} ->
ExpectedCaller = Caller
end.
ets_load() ->
Tid = ets:new(ets_load, [public,set]),
N = erlang:system_info(schedulers_online),
Pids = [spawn_link(fun() -> ets_shuffle(Tid) end) || _ <- lists:seq(1,N)],
ok = ets_kill(Pids, 500),
ok.
ets_kill([], _) -> ok;
ets_kill([Pid|Pids], Time) ->
timer:sleep(Time),
Pid ! done,
ets_kill(Pids, Time).
ets_shuffle(Tid) ->
Payload = lists:duplicate(100, $x),
ets_shuffle(Tid, 100, Payload).
ets_shuffle(Tid, I, Data) ->
ets_shuffle(Tid, I, I, Data, Data).
ets_shuffle(Tid, 0, N, _, Data) ->
ets_shuffle(Tid, N, N, Data, Data);
ets_shuffle(Tid, I, N, Data, Data0) ->
receive
done -> ok
after 0 ->
Key = rand:uniform(1000),
Data1 = [I|Data],
ets:insert(Tid, {Key, Data1}),
ets_shuffle(Tid, I - 1, N, Data1, Data0)
end.
memory_load() ->
Me = self(),
Pids0 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
timer:sleep(50),
Pids1 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
[receive {Pid, done} -> ok end || Pid <- Pids0 ++ Pids1],
timer:sleep(500),
ok.
memory_loop(Parent, N, Bin) ->
memory_loop(Parent, N, Bin, []).
memory_loop(Parent, 0, _Bin, _) ->
Parent ! {self(), done};
memory_loop(Parent, N, Bin0, Ls) ->
Bin = binary:copy(<<Bin0/binary, Bin0/binary>>),
memory_loop(Parent, N - 1, Bin, [a,b,c|Ls]).
tcp_server(Pid, Type) ->
{ok, LSock} = gen_tcp:listen(5679, [binary,
{reuseaddr, true},
{active, false}]),
Pid ! {self(), accept},
{ok, Sock} = gen_tcp:accept(LSock),
case Type of
passive_no_read ->
receive die -> ok end;
active ->
inet:setopts(Sock, [{active, once}, {packet,2}]),
receive Msg1 -> io:format("msg1: ~p~n", [Msg1]) end,
inet:setopts(Sock, [{active, once}, {packet,2}]),
receive Msg2 -> io:format("msg2: ~p~n", [Msg2]) end,
ok = gen_tcp:close(Sock);
timeout ->
Res = gen_tcp:recv(Sock, 2000, 1000),
io:format("res ~p~n", [Res])
end,
Pid ! {self(), done},
ok.
txt() ->
<<"%% tracepoints\n"
"%%\n"
"%% org_erlang_otp:carrier_pool_get\n"
"%% org_erlang_otp:carrier_pool_put\n"
"%% org_erlang_otp:carrier_destroy\n"
"%% org_erlang_otp:carrier_create\n"
"%% org_erlang_otp:aio_pool_put\n"
"%% org_erlang_otp:aio_pool_get\n"
"%% org_erlang_otp:driver_control\n"
"%% org_erlang_otp:driver_call\n"
"%% org_erlang_otp:driver_finish\n"
"%% org_erlang_otp:driver_ready_async\n"
"%% org_erlang_otp:driver_process_exit\n"
"%% org_erlang_otp:driver_stop\n"
"%% org_erlang_otp:driver_flush\n"
"%% org_erlang_otp:driver_stop_select\n"
"%% org_erlang_otp:driver_timeout\n"
"%% org_erlang_otp:driver_event\n"
"%% org_erlang_otp:driver_ready_output\n"
"%% org_erlang_otp:driver_ready_input\n"
"%% org_erlang_otp:driver_output\n"
"%% org_erlang_otp:driver_outputv\n"
"%% org_erlang_otp:driver_init\n"
"%% org_erlang_otp:driver_start\n"
"%% org_erlang_otp:scheduler_poll">>.
load_driver(Dir, Driver) ->
case erl_ddll:load_driver(Dir, Driver) of
ok -> ok;
{error, Error} = Res ->
io:format("~s\n", [erl_ddll:format_error(Error)]),
Res
end.
%% check
have_carriers(Alloc) ->
case erlang:system_info({allocator,Alloc}) of
false -> false;
_ -> true
end.
have_async_threads() ->
Tps = erlang:system_info(thread_pool_size),
if Tps =:= 0 -> false;
true -> true
end.
%% lttng
lttng_stop_and_view(Config) ->
Path = proplists:get_value(priv_dir, Config),
Name = proplists:get_value(session, Config),
{ok,_} = cmd("lttng stop " ++ Name),
{ok,Res} = cmd("lttng view " ++ Name ++ " --trace-path=" ++ Path),
Res.
check_tracepoint(TP, Data) ->
case re:run(Data, TP, [global]) of
{match, _} -> ok;
_ -> notfound
end.
lttng_start_event(Event, Config) ->
Name = proplists:get_value(session, Config),
{ok, _} = cmd("lttng enable-event -u " ++ Event ++ " --session=" ++ Name),
{ok, _} = cmd("lttng start " ++ Name),
ok.
ensure_lttng_started(Name, Config) ->
Out = case proplists:get_value(priv_dir, Config) of
undefined -> [];
Path -> "--output="++Path++" "
end,
{ok,_} = cmd("lttng create " ++ Out ++ Name),
ok.
ensure_lttng_stopped(Name) ->
{ok,_} = cmd("lttng stop"),
{ok,_} = cmd("lttng destroy " ++ Name),
ok.
cmd(Cmd) ->
io:format("<< ~ts~n", [Cmd]),
Res = os:cmd(Cmd),
io:format(">> ~ts~n", [Res]),
{ok,Res}.