diff options
Diffstat (limited to 'lib/runtime_tools/src')
-rw-r--r-- | lib/runtime_tools/src/Makefile | 6 | ||||
-rw-r--r-- | lib/runtime_tools/src/appmon_info.erl | 11 | ||||
-rw-r--r-- | lib/runtime_tools/src/dbg.erl | 145 | ||||
-rw-r--r-- | lib/runtime_tools/src/dyntrace.erl | 75 | ||||
-rw-r--r-- | lib/runtime_tools/src/erts_alloc_config.erl | 10 | ||||
-rw-r--r-- | lib/runtime_tools/src/msacc.erl | 355 | ||||
-rw-r--r-- | lib/runtime_tools/src/observer_backend.erl | 99 | ||||
-rw-r--r-- | lib/runtime_tools/src/percept_profile.erl | 9 | ||||
-rw-r--r-- | lib/runtime_tools/src/runtime_tools.app.src | 9 | ||||
-rw-r--r-- | lib/runtime_tools/src/runtime_tools.appup.src | 2 | ||||
-rw-r--r-- | lib/runtime_tools/src/runtime_tools.erl | 2 | ||||
-rw-r--r-- | lib/runtime_tools/src/runtime_tools_sup.erl | 2 | ||||
-rw-r--r-- | lib/runtime_tools/src/system_information.erl | 61 |
13 files changed, 675 insertions, 111 deletions
diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile index 99b90f9ec5..2c902952a1 100644 --- a/lib/runtime_tools/src/Makefile +++ b/lib/runtime_tools/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2013. All Rights Reserved. +# 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. @@ -45,7 +45,9 @@ MODULES= \ percept_profile \ system_information \ observer_backend \ - ttb_autostart + ttb_autostart\ + msacc + HRL_FILES= ../include/observer_backend.hrl ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/runtime_tools/src/appmon_info.erl b/lib/runtime_tools/src/appmon_info.erl index dd4dc34fcb..b5500085a3 100644 --- a/lib/runtime_tools/src/appmon_info.erl +++ b/lib/runtime_tools/src/appmon_info.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -307,9 +307,10 @@ do_work(Key, State) -> {Cmd, Aux, From, _OldRef, Old, Opts} = retrieve(WorkStore, Key), {ok, Result} = do_work2(Cmd, Aux, From, Old, Opts), if - Result==Old -> ok; - true -> - From ! {delivery, self(), Cmd, Aux, Result} + Result==Old -> ok; + true -> + From ! {delivery, self(), Cmd, Aux, Result}, + ok end, case get_opt(timeout, Opts) of at_most_once -> @@ -393,7 +394,7 @@ del_task(Key, WorkStore) -> {_Cmd, _Aux, _From, Ref, _Old, Opts} -> if Ref /= nil -> - timer:cancel(Ref), + {ok,_} = timer:cancel(Ref), receive {do_it, Key} -> Opts diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index 6eea1a0917..f17aa528ed 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -20,6 +20,7 @@ -module(dbg). -export([p/1,p/2,c/3,c/4,i/0,start/0,stop/0,stop_clear/0,tracer/0, tracer/2, tracer/3, get_tracer/0, get_tracer/1, tp/2, tp/3, tp/4, + tpe/2, ctpe/1, ctp/0, ctp/1, ctp/2, ctp/3, tpl/2, tpl/3, tpl/4, ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3, ltp/0, wtp/1, rtp/1, dtp/0, dtp/1, n/1, cn/1, ln/0, h/0, h/1]). @@ -128,7 +129,12 @@ tpl(Module, Pattern) when is_atom(Module) -> do_tp({Module, '_', '_'}, Pattern, [local]); tpl({_Module, _Function, _Arity} = X, Pattern) -> do_tp(X,Pattern,[local]). -do_tp({_Module, _Function, _Arity} = X, Pattern, Flags) + +tpe(Event, Pattern) when Event =:= send; + Event =:= 'receive' -> + do_tp(Event, Pattern, []). + +do_tp(X, Pattern, Flags) when is_integer(Pattern); is_atom(Pattern) -> case ets:lookup(get_pattern_table(), Pattern) of @@ -137,17 +143,16 @@ do_tp({_Module, _Function, _Arity} = X, Pattern, Flags) _ -> {error, unknown_pattern} end; -do_tp({Module, _Function, _Arity} = X, Pattern, Flags) when is_list(Pattern) -> +do_tp(X, Pattern, Flags) when is_list(Pattern) -> Nodes = req(get_nodes), - case Module of - '_' -> - ok; - M when is_atom(M) -> + case X of + {M,_,_} when is_atom(M) -> %% Try to load M on all nodes lists:foreach(fun(Node) -> rpc:call(Node, M, module_info, []) end, - Nodes) + Nodes); + _ -> ok end, case lint_tp(Pattern) of {ok,_} -> @@ -163,9 +168,9 @@ do_tp({Module, _Function, _Arity} = X, Pattern, Flags) when is_list(Pattern) -> end. %% All nodes are handled the same way - also the local node if it is traced -do_tp_on_nodes(Nodes, MFA, P, Flags) -> +do_tp_on_nodes(Nodes, X, P, Flags) -> lists:map(fun(Node) -> - case rpc:call(Node,erlang,trace_pattern,[MFA,P, Flags]) of + case rpc:call(Node,erlang,trace_pattern,[X,P, Flags]) of N when is_integer(N) -> {matched, Node, N}; Else -> @@ -210,13 +215,19 @@ ctpg(Module) when is_atom(Module) -> do_ctp({Module, '_', '_'}, [global]); ctpg({_Module, _Function, _Arity} = X) -> do_ctp(X,[global]). + do_ctp({Module, Function, Arity},[]) -> - do_ctp({Module, Function, Arity},[global]), + {ok,_} = do_ctp({Module, Function, Arity},[global]), do_ctp({Module, Function, Arity},[local]); do_ctp({_Module, _Function, _Arity}=MFA,Flags) -> Nodes = req(get_nodes), {ok,do_tp_on_nodes(Nodes,MFA,false,Flags)}. +ctpe(Event) when Event =:= send; + Event =:= 'receive' -> + Nodes = req(get_nodes), + {ok,do_tp_on_nodes(Nodes,Event,true,[])}. + %% %% ltp() -> ok %% List saved and built-in trace patterns. @@ -260,8 +271,7 @@ wtp(FileName) -> ok end, []), - file:close(File), - ok + ok = file:close(File) end. %% @@ -298,7 +308,12 @@ tracer(port, Port) when is_port(Port) -> start(fun() -> Port end); tracer(process, {Handler,HandlerData}) -> - start(fun() -> start_tracer_process(Handler, HandlerData) end). + start(fun() -> start_tracer_process(Handler, HandlerData) end); + +tracer(module, Fun) when is_function(Fun) -> + start(Fun); +tracer(module, {Module, State}) -> + start(fun() -> {Module, State} end). remote_tracer(port, Fun) when is_function(Fun) -> @@ -308,7 +323,13 @@ remote_tracer(port, Port) when is_port(Port) -> remote_start(fun() -> Port end); remote_tracer(process, {Handler,HandlerData}) -> - remote_start(fun() -> start_tracer_process(Handler, HandlerData) end). + remote_start(fun() -> start_tracer_process(Handler, HandlerData) end); + +remote_tracer(module, Fun) when is_function(Fun) -> + remote_start(Fun); +remote_tracer(module, {Module, State}) -> + remote_start(fun() -> {Module, State} end). + remote_start(StartTracer) -> case (catch StartTracer()) of @@ -406,7 +427,7 @@ trace_port(file, Filename) -> trace_port1(file, Filename, nowrap); trace_port(ip, Portno) when is_integer(Portno) -> - trace_port(ip,{Portno,50}); + trace_port(ip,{Portno,200}); trace_port(ip, {Portno, Qsiz}) when is_integer(Portno), is_integer(Qsiz) -> fun() -> @@ -543,9 +564,8 @@ c(M, F, A, Flags) -> {error,Reason} -> {error,Reason}; Flags1 -> tracer(), - {ok, Tracer} = get_tracer(), S = self(), - Pid = spawn(fun() -> c(S, M, F, A, [{tracer, Tracer} | Flags1]) end), + Pid = spawn(fun() -> c(S, M, F, A, [get_tracer_flag() | Flags1]) end), Mref = erlang:monitor(process, Pid), receive {'DOWN', Mref, _, _, Reason} -> @@ -579,7 +599,7 @@ stop() -> end. stop_clear() -> - ctp(), + {ok, _} = ctp(), stop(). %%% Calling the server. @@ -660,6 +680,9 @@ loop({C,T}=SurviveLinks, Table) -> reply(From, {error, Reason}); Tracer when is_pid(Tracer); is_port(Tracer) -> put(node(),{self(),Tracer}), + reply(From, {ok,self()}); + {Module, _State} = Tracer when is_atom(Module) -> + put(node(),{self(),Tracer}), reply(From, {ok,self()}) end; {_Relay,_Tracer} -> @@ -710,6 +733,9 @@ loop({C,T}=SurviveLinks, Table) -> {_LocalRelay,Tracer} when is_port(Tracer) -> reply(From, {error, cant_trace_remote_pid_to_local_port}), loop(SurviveLinks, Table); + {_LocalRelay,Tracer} when is_tuple(Tracer) -> + reply(From, {error, cant_trace_remote_pid_to_local_module}), + loop(SurviveLinks, Table); {_LocalRelay,Tracer} when is_pid(Tracer) -> case (catch relay(Node, Tracer)) of {ok,Relay} -> @@ -764,7 +790,8 @@ loop({C,T}=SurviveLinks, Table) -> end. reply(Pid, Reply) -> - Pid ! {dbg,Reply}. + Pid ! {dbg,Reply}, + ok. %%% A process-based tracer. @@ -879,9 +906,9 @@ trac(Proc, How, Flags) -> end end. -trac(Node, {_Relay, Tracer}, AtomPid, How, Flags) -> +trac(Node, {_Replay, Tracer}, AtomPid, How, Flags) -> case rpc:call(Node, ?MODULE, erlang_trace, - [AtomPid, How, [{tracer, Tracer} | Flags]]) of + [AtomPid, How, [get_tracer_flag(Tracer) | Flags]]) of N when is_integer(N) -> {matched, Node, N}; {badrpc,Reason} -> @@ -917,9 +944,11 @@ do_relay(Parent,RelP) -> case RelP of {Type,Data} -> {ok,Tracer} = remote_tracer(Type,Data), - Parent ! {started,Tracer}; + Parent ! {started,Tracer}, + ok; Pid when is_pid(Pid) -> - Parent ! {started,self()} + Parent ! {started,self()}, + ok end, do_relay_1(RelP). @@ -1114,7 +1143,7 @@ transform_flags([sos|Tail],Acc) -> transform_flags(Tail,[set_on_spawn|Acc]); transform_flags([sol|Tail],Acc) -> transform_flags(Tail,[set_on_link|Acc]); transform_flags([sofs|Tail],Acc) -> transform_flags(Tail,[set_on_first_spawn|Acc]); transform_flags([sofl|Tail],Acc) -> transform_flags(Tail,[set_on_first_link|Acc]); -transform_flags([all|_],_Acc) -> all()--[silent]; +transform_flags([all|_],_Acc) -> all()--[silent,running]; transform_flags([F|Tail]=List,Acc) when is_atom(F) -> case lists:member(F, all()) of true -> transform_flags(Tail,[F|Acc]); @@ -1123,9 +1152,10 @@ transform_flags([F|Tail]=List,Acc) when is_atom(F) -> transform_flags(Bad,_Acc) -> {error,{bad_flags,Bad}}. all() -> - [send,'receive',call,procs,garbage_collection,running, + [send,'receive',call,procs,ports,garbage_collection,running, set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link, - timestamp,arity,return_to,silent]. + timestamp,monotonic_timestamp,strict_monotonic_timestamp, + arity,return_to,silent,running_procs,running_ports,exiting]. display_info([Node|Nodes]) -> io:format("~nNode ~w:~n",[Node]), @@ -1146,24 +1176,34 @@ display_info1([]) -> ok. get_info() -> - get_info(processes(),[]). + get_info(processes(),get_info(erlang:ports(),[])). +get_info([Port|T], Acc) when is_port(Port) -> + case pinfo(Port, name) of + undefined -> + get_info(T,Acc); + {name, Name} -> + get_info(T,get_tinfo(Port, Name, Acc)) + end; get_info([Pid|T],Acc) -> case pinfo(Pid, initial_call) of undefined -> get_info(T,Acc); {initial_call, Call} -> - case tinfo(Pid, flags) of - undefined -> - get_info(T,Acc); - {flags,[]} -> - get_info(T,Acc); - {flags,Flags} -> - get_info(T,[{Pid,Call,Flags}|Acc]) - end + get_info(T,get_tinfo(Pid, Call, Acc)) end; get_info([],Acc) -> Acc. +get_tinfo(P, Id, Acc) -> + case tinfo(P, flags) of + undefined -> + Acc; + {flags,[]} -> + Acc; + {flags,Flags} -> + [{P,Id,Flags}|Acc] + end. + format_trace([]) -> []; format_trace([Item]) -> [ts(Item)]; format_trace([Item|T]) -> [ts(Item) ," | ", format_trace(T)]. @@ -1188,9 +1228,22 @@ to_pidspec(X) when is_pid(X) -> true -> X; false -> {badpid,X} end; -to_pidspec(new) -> new; -to_pidspec(all) -> all; -to_pidspec(existing) -> existing; +to_pidspec(X) when is_port(X) -> + case erlang:port_info(X) of + undefined -> {badport, X}; + _ -> X + end; +to_pidspec(Tag) + when Tag =:= all; + Tag =:= ports; + Tag =:= processes; + Tag =:= new; + Tag =:= new_ports; + Tag =:= new_processes; + Tag =:= existing; + Tag =:= existing_ports; + Tag =:= existing_processes -> + Tag; to_pidspec(X) when is_atom(X) -> case whereis(X) of undefined -> {badpid,X}; @@ -1203,6 +1256,7 @@ to_pidspec(X) -> {badpid,X}. %% to_pid(X) when is_pid(X) -> X; +to_pid(X) when is_port(X) -> X; to_pid(X) when is_integer(X) -> to_pid({0,X,0}); to_pid({X,Y,Z}) -> to_pid(lists:concat(["<",integer_to_list(X),".", @@ -1217,9 +1271,12 @@ to_pid(X) when is_list(X) -> to_pid(X) -> {badpid,X}. +pinfo(P, X) when node(P) == node(), is_port(P) -> erlang:port_info(P, X); pinfo(P, X) when node(P) == node() -> erlang:process_info(P, X); +pinfo(P, X) when is_port(P) -> check(rpc:call(node(P), erlang, port_info, [P, X])); pinfo(P, X) -> check(rpc:call(node(P), erlang, process_info, [P, X])). + tinfo(P, X) when node(P) == node() -> erlang:trace_info(P, X); tinfo(P, X) -> check(rpc:call(node(P), erlang, trace_info, [P, X])). @@ -1256,6 +1313,9 @@ tc_loop(Other, _Handler, _HData) -> gen_reader(ip, {Host, Portno}) -> case gen_tcp:connect(Host, Portno, [{active, false}, binary]) of {ok, Sock} -> + %% Just in case this is on the traced node, + %% make sure the port is not traced. + p(Sock,clear), mk_reader(fun ip_read/2, Sock); Error -> exit(Error) @@ -1311,7 +1371,7 @@ mk_reader_wrap([_Hd | Tail] = WrapFiles, File) -> {ok, Term} -> [Term | mk_reader_wrap(WrapFiles, File)]; eof -> - file:close(File), + ok = file:close(File), case Tail of [_|_] -> mk_reader_wrap(Tail); @@ -1411,6 +1471,13 @@ get_tracer() -> req({get_tracer,node()}). get_tracer(Node) -> req({get_tracer,Node}). +get_tracer_flag() -> + {ok, Tracer} = get_tracer(), + get_tracer_flag(Tracer). +get_tracer_flag({Module,State}) -> + {tracer, Module, State}; +get_tracer_flag(Port = Pid) when is_port(Port); is_pid(Pid)-> + {tracer, Pid = Port}. save_pattern([]) -> 0; diff --git a/lib/runtime_tools/src/dyntrace.erl b/lib/runtime_tools/src/dyntrace.erl index f7dbef6929..58c5a773c3 100644 --- a/lib/runtime_tools/src/dyntrace.erl +++ b/lib/runtime_tools/src/dyntrace.erl @@ -41,6 +41,27 @@ pn/1, pn/2, pn/3, pn/4, pn/5, pn/6, pn/7, pn/8, pn/9]). -export([put_tag/1, get_tag/0, get_tag_data/0, spread_tag/1, restore_tag/1]). +-export([trace/5, + trace_procs/5, + trace_ports/5, + trace_running_procs/5, + trace_running_ports/5, + trace_call/5, + trace_send/5, + trace_receive/5, + trace_garbage_collection/5]). + +-export([enabled_procs/3, + enabled_ports/3, + enabled_running_procs/3, + enabled_running_ports/3, + enabled_call/3, + enabled_send/3, + enabled_receive/3, + enabled_garbage_collection/3, + enabled/3]). + + -export([user_trace_i4s4/9]). % Know what you're doing! -on_load(on_load/0). @@ -125,6 +146,60 @@ user_trace_i4s4(_, _, _, _, _, _, _, _, _) -> user_trace_n(_, _, _, _, _, _, _, _, _, _) -> erlang:nif_error(nif_not_loaded). +trace(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> + erlang:nif_error(nif_not_loaded). + +trace_procs(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> + erlang:nif_error(nif_not_loaded). + +trace_ports(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> + erlang:nif_error(nif_not_loaded). + +trace_running_procs(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> + erlang:nif_error(nif_not_loaded). + +trace_running_ports(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> + erlang:nif_error(nif_not_loaded). + +trace_call(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> + erlang:nif_error(nif_not_loaded). + +trace_send(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> + erlang:nif_error(nif_not_loaded). + +trace_receive(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> + erlang:nif_error(nif_not_loaded). + +trace_garbage_collection(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) -> + erlang:nif_error(nif_not_loaded). + +enabled(_TraceTag, _TracerState, _Tracee) -> + erlang:nif_error(nif_not_loaded). + +enabled_procs(_TraceTag, _TracerState, _Tracee) -> + erlang:nif_error(nif_not_loaded). + +enabled_ports(_TraceTag, _TracerState, _Tracee) -> + erlang:nif_error(nif_not_loaded). + +enabled_running_procs(_TraceTag, _TracerState, _Tracee) -> + erlang:nif_error(nif_not_loaded). + +enabled_running_ports(_TraceTag, _TracerState, _Tracee) -> + erlang:nif_error(nif_not_loaded). + +enabled_call(_TraceTag, _TracerState, _Tracee) -> + erlang:nif_error(nif_not_loaded). + +enabled_send(_TraceTag, _TracerState, _Tracee) -> + erlang:nif_error(nif_not_loaded). + +enabled_receive(_TraceTag, _TracerState, _Tracee) -> + erlang:nif_error(nif_not_loaded). + +enabled_garbage_collection(_TraceTag, _TracerState, _Tracee) -> + erlang:nif_error(nif_not_loaded). + %%% %%% Erlang support functions %%% diff --git a/lib/runtime_tools/src/erts_alloc_config.erl b/lib/runtime_tools/src/erts_alloc_config.erl index 85aacdd6e1..514530332c 100644 --- a/lib/runtime_tools/src/erts_alloc_config.erl +++ b/lib/runtime_tools/src/erts_alloc_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-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. @@ -128,7 +128,7 @@ make_config(FileName) when is_list(FileName) -> case file:open(FileName, [write]) of {ok, IODev} -> Res = req({make_config, IODev}), - file:close(IODev), + ok = file:close(IODev), Res; Error -> Error @@ -200,9 +200,11 @@ server_loop(State) -> Conf = #conf{segments = ?MBC_MSEG_LIMIT, format_to = IODev}, Res = mk_config(Conf, State#state.alloc), - From ! {response, Ref, Res}; + From ! {response, Ref, Res}, + ok; _ -> - From ! {response, Ref, no_scenario_saved} + From ! {response, Ref, no_scenario_saved}, + ok end, State; {request, From, Ref, stop} -> diff --git a/lib/runtime_tools/src/msacc.erl b/lib/runtime_tools/src/msacc.erl new file mode 100644 index 0000000000..0d9b2690e5 --- /dev/null +++ b/lib/runtime_tools/src/msacc.erl @@ -0,0 +1,355 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014-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% +%% + +%% +%% @doc Microstate accounting utility function +%% +%% This module provides a user interface for analysing +%% erlang:statistics(microstate_accounting) data. +%% + +-module(msacc). +-export([available/0, start/0, start/1, stop/0, reset/0, to_file/1, + from_file/1, stats/0, stats/2, print/0, print/1, print/2, + print/3]). + +-type msacc_data() :: [msacc_data_thread()]. + +-type msacc_data_thread() :: #{ '$type' := msacc_data, + type := msacc_type(), id := msacc_id(), + counters := msacc_data_counters() }. +-type msacc_data_counters() :: #{ msacc_state() => non_neg_integer()}. + +-type msacc_stats() :: [msacc_stats_thread()]. +-type msacc_stats_thread() :: #{ '$type' := msacc_stats, + type := msacc_type(), id := msacc_id(), + system := float(), + counters := msacc_stats_counters()}. +-type msacc_stats_counters() :: #{ msacc_state() => #{ thread := float(), + system := float()}}. + + +-type msacc_type() :: scheduler | aux | async. +-type msacc_id() :: non_neg_integer(). +-type msacc_state() :: alloc | aux | bif | busy_wait | check_io | + emulator | ets | gc | gc_fullsweep | nif | + other | port | send | sleep | timers. + +-type msacc_print_options() :: #{ system => boolean() }. + +-spec available() -> boolean(). +available() -> + try + [_|_] = erlang:statistics(microstate_accounting), + true + catch _:_ -> + false + end. + +-spec start() -> boolean(). +start() -> + erlang:system_flag(microstate_accounting, true). + +-spec stop() -> boolean(). +stop() -> + erlang:system_flag(microstate_accounting, false). + +-spec reset() -> boolean(). +reset() -> + erlang:system_flag(microstate_accounting, reset). + +-spec start(Time) -> true when + Time :: timeout(). +start(Tmo) -> + stop(), reset(), start(), + timer:sleep(Tmo), + stop(). + +-spec to_file(Filename) -> ok | {error, file:posix()} when + Filename :: file:name_all(). +to_file(Filename) -> + file:write_file(Filename, io_lib:format("~p.~n",[stats()])). + +-spec from_file(Filename) -> msacc_data() when + Filename :: file:name_all(). +from_file(Filename) -> + {ok, [Stats]} = file:consult(Filename), + Stats. + +-spec print() -> ok. +print() -> + print(stats()). + +-spec print(DataOrStats) -> ok when + DataOrStats :: msacc_data() | msacc_stats(). +print(Stats) -> + print(Stats, #{}). + +-spec print(DataOrStats, Options) -> ok when + DataOrStats :: msacc_data() | msacc_stats(), + Options :: msacc_print_options(). +print(Stats, Options) -> + print(group_leader(), Stats, Options). + +-spec print(FileOrDevice, DataOrStats, Options) -> ok when + FileOrDevice :: file:filename() | io:device(), + DataOrStats :: msacc_data() | msacc_stats(), + Options :: msacc_print_options(). +print(Filename, Stats, Options) when is_list(Filename) -> + case file:open(Filename,[write]) of + {ok, D} -> print(D, Stats, Options),file:close(D); + Error -> Error + end; +print(Device, Stats, Options) -> + DefaultOpts = #{ system => false }, + print_int(Device, Stats, maps:merge(DefaultOpts, Options)). +print_int(Device, [#{ '$type' := msacc_data, id := _Id }|_] = Stats, Options) -> + TypeStats = stats(type, Stats), + io:format(Device, "~s", [print_stats_overview(Stats, Options)]), + io:format(Device, "~s", [print_stats_header(Stats, Options)]), + io:format(Device, "~s", [print_stats_threads( + stats(realtime, Stats), Options)]), + io:format(Device, "~s", [print_stats_type( + stats(realtime, TypeStats), Options)]); +print_int(Device, [#{ '$type' := msacc_data }|_] = Stats, Options) -> + io:format(Device, "~s", [print_stats_header(Stats, Options)]), + io:format(Device, "~s", [print_stats_type( + stats(realtime, Stats), Options)]); +print_int(Device, [#{ '$type' := msacc_stats, id := _Id }|_] = Stats,Options) -> + io:format(Device, "~s", [print_stats_header(Stats, Options)]), + io:format(Device, "~s", [print_stats_threads(Stats, Options)]), + io:format(Device, "~s", [print_stats_type( + msacc:stats(type, Stats), Options)]); +print_int(Device, [#{ '$type' := msacc_stats }|_] = Stats, Options) -> + io:format(Device, "~s", [print_stats_header(Stats, Options)]), + io:format(Device, "~s", [print_stats_type(Stats, Options)]). + + +-spec stats() -> msacc_data(). +stats() -> + Fun = fun F(K,{PerfCount,StateCount}) -> + %% Need to handle ERTS_MSACC_STATE_COUNTERS + {F(K,PerfCount),StateCount}; + F(_K,PerfCount) -> + erlang:convert_time_unit(PerfCount, perf_counter, 1000000) + end, + UsStats = lists:map( + fun(#{ counters := Cnt } = M) -> + UsCnt = maps:map(Fun,Cnt), + M#{ '$type' => msacc_data, counters := UsCnt } + end, erlang:statistics(microstate_accounting)), + statssort(UsStats). + +-spec stats(Analysis, Stats) -> non_neg_integer() when + Analysis :: system_realtime | system_runtime, + Stats :: msacc_data(); + (Analysis, Stats) -> msacc_stats() when + Analysis :: realtime | runtime, + Stats :: msacc_data(); + (Analysis, StatsOrData) -> msacc_data() | msacc_stats() when + Analysis :: type, + StatsOrData :: msacc_data() | msacc_stats(). +stats(system_realtime, Stats) -> + lists:foldl(fun(#{ counters := Cnt }, Acc) -> + get_total(Cnt, Acc) + end, 0, Stats); +stats(system_runtime, Stats) -> + lists:foldl(fun(#{ counters := Cnt }, Acc) -> + get_total(maps:remove(sleep, Cnt), Acc) + end, 0, Stats); +stats(realtime, Stats) -> + RealTime = stats(system_realtime, Stats), + statssort([get_thread_perc(Thread, RealTime) || Thread <- Stats]); +stats(runtime, Stats) -> + RunTime = stats(system_runtime, Stats), + statssort([get_thread_perc(T#{ counters := maps:remove(sleep,Cnt)}, RunTime) + || T = #{ counters := Cnt } <- Stats]); +stats(type, Stats) -> + statssort(merge_threads(Stats, [])). + +print_stats_overview(Stats, _Options) -> + RunTime = stats(system_runtime, Stats), + RealTime = stats(system_realtime, Stats) div length(Stats), + SchedStats = [S || #{ type := scheduler } = S <- Stats], + AvgSchedRunTime = stats(system_runtime, SchedStats) div length(SchedStats), + NumSize = if + RealTime > RunTime -> length(integer_to_list(RealTime)); + true -> length(integer_to_list(RunTime)) + end, + [io_lib:format("Average thread real-time : ~*B us~n", + [NumSize, RealTime]), + io_lib:format("Accumulated system run-time : ~*B us~n", + [NumSize, RunTime]), + io_lib:format("Average scheduler run-time : ~*B us~n", + [NumSize, AvgSchedRunTime]), + io_lib:format("~n",[])]. + +print_stats_threads(Stats, Options) -> + [io_lib:format("~nStats per thread:~n", []), + [print_thread_info(Thread, Options) || Thread <- Stats]]. + +print_stats_type(Stats, Options) -> + [io_lib:format("~nStats per type:~n", []), + [print_thread_info(Thread, Options) || Thread <- Stats]]. + + +print_stats_header([#{ counters := Cnt }|_], #{ system := PrintSys }) -> + [io_lib:format("~14s", ["Thread"]), + map(fun(Counter, _) when PrintSys-> + io_lib:format("~9s ", [atom_to_list(Counter)]); + (Counter, _) -> + io_lib:format("~9s", [atom_to_list(Counter)]) + end, Cnt), + io_lib:format("~n",[])]. + +print_thread_info(#{ '$type' := msacc_stats, + counters := Cnt } = Thread, #{ system := PrintSys }) -> + [case maps:find(id, Thread) of + error -> + io_lib:format("~14s", [atom_to_list(maps:get(type, Thread))]); + {ok, Id} -> + io_lib:format("~10s(~2B)", [atom_to_list(maps:get(type,Thread)),Id]) + end, + map(fun(_Key, #{ thread := ThreadPerc, system := SystemPerc }) when PrintSys -> + io_lib:format("~6.2f%(~4.1f%)", [ThreadPerc, SystemPerc]); + (_Key, #{ thread := ThreadPerc }) -> + io_lib:format("~8.2f%", [ThreadPerc]) + end, Cnt), + io_lib:format("~n",[])]. + +get_total(Cnt, Base) -> + maps:fold(fun(_, {Val,_}, Time) -> + %% Have to handle ERTS_MSACC_STATE_COUNTERS + Time + Val; + (_, Val, Time) -> Time + Val + end, Base, Cnt). + +get_thread_perc(#{ '$type' := msacc_data, counters := Cnt } = Thread, + SystemTime) -> + ThreadTime = get_total(Cnt, 0), + Thread#{ '$type' := msacc_stats, + system => percentage(ThreadTime,SystemTime), + counters => get_thread_perc(Cnt, ThreadTime, SystemTime)}. +get_thread_perc(Cnt, ThreadTime, SystemTime) -> + maps:map(fun F(Key, {Val, C}) -> + M = F(Key, Val), + M#{ cnt => C }; + F(_Key, Val) -> + #{ thread => percentage(Val, ThreadTime), + system => percentage(Val, SystemTime) } + end, Cnt). + +%% This code is a little bit messy as it has to be able to deal with +%% both [msacc_data()] and [msacc_stats()]. +merge_threads([#{ '$type' := msacc_stats, + type := Type, + counters := Cnt } = M0|R], Acc) -> + case keyfind(type, Type, Acc) of + false -> + merge_threads(R, [maps:remove(id,M0#{ threads => 1 })|Acc]); + #{ '$type' := msacc_stats, counters := Cnt0, + threads := Threads, system := System } = M -> + NewMap = M#{ counters := add_counters(Cnt, Cnt0), + system := System + maps:get(system, M0), + threads := Threads + 1}, + NewAcc = keyreplace(type, Type, NewMap, Acc), + merge_threads(R, NewAcc) + end; +merge_threads([], [#{ '$type' := msacc_stats, + system := System, + threads := Threads, + counters := Cnt} = M0|R]) -> + Counters = maps:map(fun(_,#{ thread := Thr } = Map) -> + Map#{ thread := Thr / Threads } + end, Cnt), + M = maps:remove(threads, M0), + [M#{ system := System, counters := Counters} | merge_threads([],R)]; +merge_threads([], []) -> + []; +%% The clauses below deal with msacc_data() +merge_threads([#{ '$type' := msacc_data, + type := Type, + counters := Cnt } = M0|R], Acc) -> + case keyfind(type, Type, Acc) of + false -> + merge_threads(R, [maps:remove(id,M0)|Acc]); + #{ '$type' := msacc_data, counters := Cnt0 } = M -> + NewMap = M#{ counters := add_counters(Cnt, Cnt0) }, + NewAcc = keyreplace(type, Type, NewMap, Acc), + merge_threads(R, NewAcc) + end; +merge_threads([], Acc) -> + Acc. + +add_counters(M1, M2) -> + maps:map( + fun(Key, #{ thread := Thr1, system := Sys1, cnt := Cnt1}) -> + %% Have to handle ERTS_MSACC_STATE_COUNTERS + #{ thread := Thr2, system := Sys2, cnt := Cnt2} = maps:get(Key, M2), + #{ thread => Thr1 + Thr2, system => Sys1 + Sys2, + cnt => Cnt1 + Cnt2 }; + (Key, #{ thread := Thr1, system := Sys1}) -> + #{ thread := Thr2, system := Sys2} = maps:get(Key, M2), + #{ thread => Thr1 + Thr2, system => Sys1 + Sys2}; + (Key, {V1,C1}) -> + %% Have to handle ERTS_MSACC_STATE_COUNTERS + {V2,C2} = maps:get(Key, M2),{V1+V2,C1+C2}; + (Key, V1) -> maps:get(Key, M2) + V1 + end, M1). + +percentage(Divident, Divisor) -> + if Divisor == 0 andalso Divident /= 0 -> + 100.0; + Divisor == 0 -> + 0.0; + true -> + Divident / Divisor * 100 + end. + +keyfind(Key, Value, [H|T]) -> + case maps:find(Key, H) of + {ok, Value} -> + H; + _ -> + keyfind(Key, Value, T) + end; +keyfind(_, _, []) -> + false. + +keyreplace(Key, Value, NewMap, [H|T]) -> + case maps:find(Key, H) of + {ok, Value} -> + [NewMap|T]; + _ -> + [H|keyreplace(Key, Value, NewMap, T)] + end; +keyreplace(_, _, _, []) -> + []. + +statssort(Stats) -> + lists:sort(fun(#{ type := Type1, id := Id1}, + #{ type := Type2, id := Id2}) -> + {Type1, Id1} < {Type2, Id2}; + (#{ type := Type1}, #{ type := Type2}) -> + Type1 < Type2 + end, Stats). + +map(Fun,Map) -> + [ Fun(K,V) || {K,V} <- maps:to_list(Map) ]. diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 9177752cef..b27bc63d15 100644 --- a/lib/runtime_tools/src/observer_backend.erl +++ b/lib/runtime_tools/src/observer_backend.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2014. All Rights Reserved. +%% Copyright Ericsson AB 2002-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. @@ -23,7 +23,8 @@ -export([vsn/0]). %% observer stuff --export([sys_info/0, get_table/3, get_table_list/2, fetch_stats/2]). +-export([sys_info/0, get_port_list/0, + get_table/3, get_table_list/2, fetch_stats/2]). %% etop stuff -export([etop_collect/1]). @@ -139,6 +140,60 @@ get_mnesia_loop(Parent, {Match, Cont}) -> Parent ! {self(), Match}, get_mnesia_loop(Parent, mnesia:select(Cont)). +get_port_list() -> + ExtraItems = [monitors,monitored_by,parallelism,locking,queue_size,memory], + [begin + [{port_id,P}|erlang:port_info(P)] ++ + port_info(P,ExtraItems) ++ + inet_port_extra(erlang:port_info(P, name), P) + end || P <- erlang:ports()]. + +port_info(P,[Item|Items]) -> + case erlang:port_info(P,Item) of + undefined -> port_info(P,Items); + Value -> [Value|port_info(P,Items)] + end; +port_info(_,[]) -> + []. + +inet_port_extra({_,Type},Port) when Type =:= "udp_inet"; + Type =:= "tcp_inet"; + Type =:= "sctp_inet" -> + Data = + case inet:getstat(Port) of + {ok, Stats} -> [{statistics, Stats}]; + _ -> [] + end ++ + case inet:peername(Port) of + {ok, {RAddr,RPort}} when is_tuple(RAddr), is_integer(RPort) -> + [{remote_address,RAddr},{remote_port,RPort}]; + {ok, RAddr} -> + [{remote_address,RAddr}]; + {error, _} -> [] + end ++ + case inet:sockname(Port) of + {ok, {LAddr,LPort}} when is_tuple(LAddr), is_integer(LPort) -> + [{local_address,LAddr},{local_port,LPort}]; + {ok, LAddr} -> + [{local_address,LAddr}]; + {error, _} -> [] + end ++ + case inet:getopts(Port, + [active, broadcast, buffer, delay_send, + deliver, dontroute, exit_on_close, + header, high_msgq_watermark, high_watermark, + ipv6_v6only, keepalive, linger, low_msgq_watermark, + low_watermark, mode, netns, nodelay, packet, + packet_size, priority, read_packets, recbuf, + reuseaddr, send_timeout, send_timeout_close, + show_econnreset, sndbuf, tos, tclass]) of + {ok, Opts} -> [{options, Opts}]; + {error, _} -> [] + end, + [{inet,Data}]; +inet_port_extra(_,_) -> + []. + get_table_list(ets, Opts) -> HideUnread = proplists:get_value(unread_hidden, Opts, true), HideSys = proplists:get_value(sys_hidden, Opts, true), @@ -259,12 +314,12 @@ etop_collect(Collector) -> case SchedulerWallTime of undefined -> - spawn(fun() -> flag_holder_proc(Collector) end); + erlang:system_flag(scheduler_wall_time,true), + spawn(fun() -> flag_holder_proc(Collector) end), + ok; _ -> ok - end, - - erlang:system_flag(scheduler_wall_time,true). + end. flag_holder_proc(Collector) -> Ref = erlang:monitor(process,Collector), @@ -334,8 +389,8 @@ ttb_init_node(MetaFile_0,PI,Traci) -> MetaPid ! {metadata,Traci}, case PI of true -> - Proci = pnames(), - MetaPid ! {metadata,Proci}; + MetaPid ! {metadata,pnames()}, + ok; false -> ok end, @@ -354,7 +409,8 @@ ttb_meta_tracer(MetaFile,PI,Parent,SessionData) -> erlang:trace_pattern({erlang,spawn_link,3},ReturnMS,[meta]), erlang:trace_pattern({erlang,spawn_opt,1},ReturnMS,[meta]), erlang:trace_pattern({erlang,register,2},[],[meta]), - erlang:trace_pattern({global,register_name,2},[],[meta]); + erlang:trace_pattern({global,register_name,2},[],[meta]), + ok; false -> ok end, @@ -362,7 +418,8 @@ ttb_meta_tracer(MetaFile,PI,Parent,SessionData) -> case proplists:get_value(overload_check, SessionData) of {Ms, M, F} -> catch M:F(init), - erlang:send_after(Ms, self(), overload_check); + erlang:send_after(Ms, self(), overload_check), + ok; _ -> ok end, @@ -371,10 +428,10 @@ ttb_meta_tracer(MetaFile,PI,Parent,SessionData) -> ttb_meta_tracer_loop(MetaFile,PI,Acc,State) -> receive {trace_ts,_,call,{erlang,register,[Name,Pid]},_} -> - ttb_store_meta({pid,{Pid,Name}},MetaFile), + ok = ttb_store_meta({pid,{Pid,Name}},MetaFile), ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {trace_ts,_,call,{global,register_name,[Name,Pid]},_} -> - ttb_store_meta({pid,{Pid,{global,Name}}},MetaFile), + ok = ttb_store_meta({pid,{Pid,{global,Name}}},MetaFile), ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {trace_ts,CallingPid,call,{erlang,spawn_opt,[{M,F,Args,_}]},_} -> MFA = {M,F,length(Args)}, @@ -390,7 +447,7 @@ ttb_meta_tracer_loop(MetaFile,PI,Acc,State) -> NewAcc = dict:update(CallingPid, fun([H|T]) -> - ttb_store_meta({pid,{NewPid,H}},MetaFile), + ok = ttb_store_meta({pid,{NewPid,H}},MetaFile), T end, Acc), @@ -408,22 +465,22 @@ ttb_meta_tracer_loop(MetaFile,PI,Acc,State) -> NewAcc = dict:update(CallingPid, fun([H|T]) -> - ttb_store_meta({pid,{NewPid,H}},MetaFile), + ok = ttb_store_meta({pid,{NewPid,H}},MetaFile), T end, Acc), ttb_meta_tracer_loop(MetaFile,PI,NewAcc,State); {metadata,Data} when is_list(Data) -> - ttb_store_meta(Data,MetaFile), + ok = ttb_store_meta(Data,MetaFile), ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {metadata,Key,Fun} when is_function(Fun) -> - ttb_store_meta([{Key,Fun()}],MetaFile), + ok = ttb_store_meta([{Key,Fun()}],MetaFile), ttb_meta_tracer_loop(MetaFile,PI,Acc,State); {metadata,Key,What} -> - ttb_store_meta([{Key,What}],MetaFile), + ok = ttb_store_meta([{Key,What}],MetaFile), ttb_meta_tracer_loop(MetaFile,PI,Acc,State); overload_check -> {Ms, M, F} = proplists:get_value(overload_check, State), @@ -439,7 +496,7 @@ ttb_meta_tracer_loop(MetaFile,PI,Acc,State) -> ttb_meta_tracer_loop(MetaFile,PI,Acc, State) end; {'DOWN', _, _, _, _} -> - stop_seq_trace(), + _ = stop_seq_trace(), self() ! stop, ttb_meta_tracer_loop(MetaFile,PI,Acc, State); stop when PI=:=true -> @@ -528,7 +585,7 @@ ttb_store_meta(Data,MetaFile) -> ttb_store_meta([Data],MetaFile). ttb_write_binary(Fd,[H|T]) -> - file:write(Fd,ttb_make_binary(H)), + ok = file:write(Fd,ttb_make_binary(H)), ttb_write_binary(Fd,T); ttb_write_binary(_Fd,[]) -> ok. @@ -585,9 +642,9 @@ ttb_fetch(MetaFile,{Port,Host}) -> send_files({Sock,Host},[File|Files]) -> {ok,Fd} = file:open(File,[raw,read,binary]), - gen_tcp:send(Sock,<<1,(list_to_binary(filename:basename(File)))/binary>>), + ok = gen_tcp:send(Sock,<<1,(list_to_binary(filename:basename(File)))/binary>>), send_chunks(Sock,Fd), - file:delete(File), + ok = file:delete(File), send_files({Sock,Host},Files); send_files({_Sock,_Host},[]) -> done. diff --git a/lib/runtime_tools/src/percept_profile.erl b/lib/runtime_tools/src/percept_profile.erl index d5ea2c7032..1e8e913b80 100644 --- a/lib/runtime_tools/src/percept_profile.erl +++ b/lib/runtime_tools/src/percept_profile.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-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. @@ -87,7 +87,7 @@ start(Filename, Options) -> start(Filename, {Module, Function, Args}, Options) -> case whereis(percept_port) of undefined -> - profile_to_file(Filename, Options), + {ok, _} = profile_to_file(Filename, Options), erlang:apply(Module, Function, Args), stop(); Port -> @@ -113,7 +113,7 @@ deliver_all_trace() -> -spec stop() -> 'ok' | {'error', 'not_started'}. stop() -> - erlang:system_profile(undefined, [runnable_ports, runnable_procs]), + _ = erlang:system_profile(undefined, [runnable_ports, runnable_procs]), erlang:trace(all, false, [procs, ports, timestamp]), deliver_all_trace(), case whereis(percept_port) of @@ -158,7 +158,8 @@ set_tracer(Port, Opts) -> {TOpts, POpts} = parse_profile_options(Opts), % Setup profiling and tracing erlang:trace(all, true, [{tracer, Port}, timestamp | TOpts]), - erlang:system_profile(Port, POpts). + _ = erlang:system_profile(Port, POpts), + ok. %% parse_profile_options diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src index ad10655aa0..690c61a4c3 100644 --- a/lib/runtime_tools/src/runtime_tools.app.src +++ b/lib/runtime_tools/src/runtime_tools.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% 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. @@ -22,12 +22,13 @@ {vsn, "%VSN%"}, {modules, [appmon_info, dbg,observer_backend,percept_profile, runtime_tools,runtime_tools_sup,erts_alloc_config, - ttb_autostart,dyntrace,system_information]}, + ttb_autostart,dyntrace,system_information, + msacc]}, {registered, [runtime_tools_sup]}, {applications, [kernel, stdlib]}, {env, []}, {mod, {runtime_tools, []}}, - {runtime_dependencies, ["stdlib-2.0","mnesia-4.12","kernel-3.0", - "erts-7.0"]}]}. + {runtime_dependencies, ["stdlib-3.0","mnesia-4.12","kernel-5.0", + "erts-8.0"]}]}. diff --git a/lib/runtime_tools/src/runtime_tools.appup.src b/lib/runtime_tools/src/runtime_tools.appup.src index 883ff1e074..a42673c87e 100644 --- a/lib/runtime_tools/src/runtime_tools.appup.src +++ b/lib/runtime_tools/src/runtime_tools.appup.src @@ -1,7 +1,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2014. All Rights Reserved. +%% Copyright Ericsson AB 2001-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. diff --git a/lib/runtime_tools/src/runtime_tools.erl b/lib/runtime_tools/src/runtime_tools.erl index 62b9dfd71e..52ae5cc0eb 100644 --- a/lib/runtime_tools/src/runtime_tools.erl +++ b/lib/runtime_tools/src/runtime_tools.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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. diff --git a/lib/runtime_tools/src/runtime_tools_sup.erl b/lib/runtime_tools/src/runtime_tools_sup.erl index e69b495cb5..efa37de42d 100644 --- a/lib/runtime_tools/src/runtime_tools_sup.erl +++ b/lib/runtime_tools/src/runtime_tools_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2013. All Rights Reserved. +%% Copyright Ericsson AB 2006-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. diff --git a/lib/runtime_tools/src/system_information.erl b/lib/runtime_tools/src/system_information.erl index ad7ee7311c..df25297eb9 100644 --- a/lib/runtime_tools/src/system_information.erl +++ b/lib/runtime_tools/src/system_information.erl @@ -28,31 +28,26 @@ -behaviour(gen_server). %% API --export([ - report/0, +-export([report/0, from_file/1, - to_file/1 - ]). --export([ - start/0, stop/0, - load_report/0, load_report/2, - applications/0, applications/1, - application/1, application/2, - environment/0, environment/1, - module/1, module/2, - modules/1, - sanity_check/0 - ]). + to_file/1]). + +-export([start/0, stop/0, + load_report/0, load_report/2, + applications/0, applications/1, + application/1, application/2, + environment/0, environment/1, + module/1, module/2, + modules/1, + sanity_check/0]). %% gen_server callbacks --export([ - init/1, - handle_call/3, - handle_cast/2, - handle_info/2, - terminate/2, - code_change/3 - ]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). -define(SERVER, ?MODULE). @@ -70,14 +65,15 @@ start() -> gen_server:start({local, ?SERVER}, ?MODULE, [], []). + stop() -> - gen_server:call(?SERVER, stop). + gen_server:call(?SERVER, stop, infinity). load_report() -> load_report(data, report()). load_report(file, File) -> load_report(data, from_file(File)); load_report(data, Report) -> - start(), gen_server:call(?SERVER, {load_report, Report}). + ok = start_internal(), gen_server:call(?SERVER, {load_report, Report}, infinity). report() -> [ {init_arguments, init:get_arguments()}, @@ -120,22 +116,22 @@ from_file(File) -> applications() -> applications([]). applications(Opts) when is_list(Opts) -> - gen_server:call(?SERVER, {applications, Opts}). + gen_server:call(?SERVER, {applications, Opts}, infinity). application(App) when is_atom(App) -> application(App, []). application(App, Opts) when is_atom(App), is_list(Opts) -> - gen_server:call(?SERVER, {application, App, Opts}). + gen_server:call(?SERVER, {application, App, Opts}, infinity). environment() -> environment([]). environment(Opts) when is_list(Opts) -> - gen_server:call(?SERVER, {environment, Opts}). + gen_server:call(?SERVER, {environment, Opts}, infinity). module(M) when is_atom(M) -> module(M, []). module(M, Opts) when is_atom(M), is_list(Opts) -> - gen_server:call(?SERVER, {module, M, Opts}). + gen_server:call(?SERVER, {module, M, Opts}, infinity). modules(Opt) when is_atom(Opt) -> - gen_server:call(?SERVER, {modules, Opt}). + gen_server:call(?SERVER, {modules, Opt}, infinity). -spec sanity_check() -> ok | {failed, Failures} when @@ -225,6 +221,13 @@ code_change(_OldVsn, State, _Extra) -> %% Internal functions %%=================================================================== +start_internal() -> + case start() of + {ok,_} -> ok; + {error, {already_started,_}} -> ok; + Error -> Error + end. + %% handle report values get_value([], Data) -> Data; |