aboutsummaryrefslogtreecommitdiffstats
path: root/lib/runtime_tools/test/dyntrace_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/runtime_tools/test/dyntrace_SUITE.erl')
-rw-r--r--lib/runtime_tools/test/dyntrace_SUITE.erl224
1 files changed, 224 insertions, 0 deletions
diff --git a/lib/runtime_tools/test/dyntrace_SUITE.erl b/lib/runtime_tools/test/dyntrace_SUITE.erl
new file mode 100644
index 0000000000..0e4f369ed0
--- /dev/null
+++ b/lib/runtime_tools/test/dyntrace_SUITE.erl
@@ -0,0 +1,224 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(dyntrace_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([smoke/1,process/1]).
+
+%% Default timetrap timeout (set in init_per_testcase)
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case erlang:system_info(dynamic_trace) of
+ none ->
+ {skip,"No dynamic trace in this run-time system"};
+ dtrace ->
+ [{group,smoke}];
+ systemtap ->
+ {skip,"SystemTap tests currently not supported"}
+ end.
+
+groups() ->
+ [{smoke,[sequence],[smoke,{group,rest}]},
+ {rest,[],
+ [process]}].
+
+init_per_suite(Config) ->
+ N = "beam" ++
+ case erlang:system_info(debug_compiled) of
+ false -> "";
+ true -> ".debug"
+ end ++
+ case erlang:system_info(smp_support) of
+ false -> "";
+ true -> ".smp"
+ end,
+ [{emu_name,N}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+smoke(Config) ->
+ Emu = ?t:lookup_config(emu_name, Config),
+ BinEmu = list_to_binary(Emu),
+ case erlang:system_info(dynamic_trace) of
+ dtrace ->
+ Probes = os:cmd("sudo /usr/sbin/dtrace -l -m" ++ Emu),
+ io:put_chars(Probes),
+ [_|Lines] = re:split(Probes, "\n", [trim]),
+ [{_,_} = binary:match(L, BinEmu) || L <- Lines],
+ ok
+ end,
+
+ %% Test that the framework for running dtrace/systemtap works
+ %% by executing an empty script.
+ {ok,[]} = dyntrace("", fun() -> ok end),
+ ok.
+
+
+process(_Config) ->
+ Script = [{probe,"process-spawn"},
+ {action,[{printf,["spawn %s %s\n",{arg,0},{arg,1}]}]},
+ {probe,"process-scheduled"},
+ {action,[{printf,["in %s\n",{arg,0}]}]},
+ {probe,"process-unscheduled"},
+ {action,[{printf,["out %s\n",{arg,0}]}]},
+ {probe,"process-hibernate"},
+ {action,[{printf,["hibernate %s %s\n",{arg,0},{arg,1}]}]},
+ {probe,"process-exit"},
+ {action,[{printf,["exit %s %s\n",{arg,0},{arg,1}]}]}
+ ],
+ F = fun() ->
+ {Pid,Ref} = spawn_monitor(fun my_process/0),
+ Pid ! hibernate,
+ Pid ! quit,
+ receive
+ {'DOWN',Ref,process,Pid,{terminated,Pid}} ->
+ ok
+ end,
+ Pid
+ end,
+ {Pid,Output0} = dyntrace(Script, F),
+ Output1 = [termify_line(L) || L <- Output0],
+ PidStr = pid_to_list(Pid),
+ Output = [L || L <- Output1, element(2, L) =:= PidStr],
+ Reason = "{terminated,"++PidStr++"}",
+ io:format("~p\n", [Output]),
+ [{spawn,PidStr,"erlang:apply/2"},
+ {in,PidStr},
+ {hibernate,PidStr,"erlang:apply/2"},
+ {out,PidStr},
+ {in,PidStr},
+ {exit,PidStr,Reason},
+ {out,PidStr}] = Output,
+ ok.
+
+termify_line(L) ->
+ [H|T] = re:split(L, " ", [{return,list}]),
+ list_to_tuple([list_to_atom(H)|T]).
+
+my_process() ->
+ receive
+ hibernate ->
+ erlang:hibernate(erlang, apply, [fun my_process/0,[]]);
+ quit ->
+ exit({terminated,self()})
+ end.
+
+%%%
+%%% Utility functions.
+%%%
+
+dyntrace(Script0, Action) ->
+ Sudo = os:find_executable(sudo),
+ {Termination,Pid} = termination_probe(),
+ Script1 = Script0++Termination,
+ Script = translate_script(Script1),
+ io:format("~s\n", [Script]),
+ SrcFile = "test-dyntrace.d",
+ ok = file:write_file(SrcFile, Script),
+ Args = ["/usr/sbin/dtrace", "-q","-s",SrcFile],
+ Port = open_port({spawn_executable,Sudo},
+ [{args,Args},stream,in,stderr_to_stdout,eof]),
+ receive
+ {Port,{data,Sofar}} ->
+ Res = Action(),
+ Pid ! quit,
+ {Res,get_data(Port, Sofar)}
+ end.
+
+get_data(Port, Sofar) ->
+ receive
+ {Port,{data,Bytes}} ->
+ get_data(Port, [Sofar|Bytes]);
+ {Port,eof} ->
+ port_close(Port),
+ [$\n|T] = lists:flatten(Sofar),
+ re:split(T, "\n", [{return,list},trim])
+ end.
+
+termination_probe() ->
+ Pid = spawn(fun() ->
+ receive
+ _ ->
+ exit(done)
+ end
+ end),
+ S = [{'BEGIN',[{printf,["\n"]}]},
+ {probe,"process-exit"},
+ {pred,{'==',{arg,0},Pid}},
+ {action,[{exit,[0]}]}],
+ {S,Pid}.
+
+translate_script(Script) ->
+ [dtrace_op(Op) || Op <- Script].
+
+dtrace_op({probe,Function}) ->
+ OsPid = os:getpid(),
+ ["erlang",OsPid,":::",Function,$\n];
+dtrace_op({pred,Pred}) ->
+ ["/",dtrace_op(Pred),"/\n"];
+dtrace_op({action,List}) ->
+ ["{ ",action_list(List)," }\n\n"];
+dtrace_op({'BEGIN',List}) ->
+ ["BEGIN { ",action_list(List)," }\n\n"];
+dtrace_op({'==',Op1,Op2}) ->
+ [dtrace_op(Op1)," == ",dtrace_op(Op2)];
+dtrace_op({arg,N}) ->
+ ["copyinstr(arg",integer_to_list(N),")"];
+dtrace_op({Func,List}) when is_atom(Func), is_list(List) ->
+ [atom_to_list(Func),"(",comma_sep_ops(List),")"];
+dtrace_op(Pid) when is_pid(Pid) ->
+ ["\"",pid_to_list(Pid),"\""];
+dtrace_op(Str) when is_integer(hd(Str)) ->
+ io_lib:format("~p", [Str]);
+dtrace_op(Int) when is_integer(Int) ->
+ integer_to_list(Int).
+
+comma_sep_ops([A,B|T]) ->
+ [dtrace_op(A),","|comma_sep_ops([B|T])];
+comma_sep_ops([H]) ->
+ dtrace_op(H);
+comma_sep_ops([]) -> [].
+
+action_list([H|T]) ->
+ [dtrace_op(H),";"|action_list(T)];
+action_list([]) -> [].