diff options
Diffstat (limited to 'lib/runtime_tools')
-rw-r--r-- | lib/runtime_tools/test/Makefile | 1 | ||||
-rw-r--r-- | lib/runtime_tools/test/dyntrace_SUITE.erl | 224 |
2 files changed, 225 insertions, 0 deletions
diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile index cfaf420d65..3e50ba02ca 100644 --- a/lib/runtime_tools/test/Makefile +++ b/lib/runtime_tools/test/Makefile @@ -3,6 +3,7 @@ include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES = \ + dyntrace_SUITE \ runtime_tools_SUITE \ inviso_testmodule1_foo \ inviso_SUITE \ 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([]) -> []. |