%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2002-2018. 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(etop_tr). -author('siri@erix.ericsson.se'). %%-compile(export_all). -export([setup_tracer/1,stop_tracer/1,reader/1]). -import(etop,[getopt/2]). -include("etop_defs.hrl"). setup_tracer(Config) -> TraceNode = getopt(node,Config), RHost = rpc:call(TraceNode, net_adm, localhost, []), Store = ets:new(?MODULE, [set, public]), %% We can only trace one process anyway kill the old one. case erlang:whereis(dbg) of undefined -> case rpc:call(TraceNode, erlang, whereis, [dbg]) of undefined -> fine; Pid -> exit(Pid, kill) end; Pid -> exit(Pid,kill) end, dbg:tracer(TraceNode,port,dbg:trace_port(ip,{getopt(port,Config),5000})), dbg:p(all,[running,timestamp]), T = dbg:get_tracer(TraceNode), Config#opts{tracer=T,host=RHost,store=Store}. stop_tracer(_Config) -> dbg:p(all,clear), dbg:stop(), ok. reader(Config) -> Host = getopt(host, Config), Port = getopt(port, Config), {ok, Sock} = gen_tcp:connect(Host, Port, [{active, false}]), spawn_link(fun() -> reader_init(Sock,getopt(store,Config),[]) end). %%%%%%%%%%%%%% Socket reader %%%%%%%%%%%%%%%%%%%%%%%%%%% reader_init(Sock, Store, Last) -> process_flag(priority, high), reader(Sock, Store, Last). reader(Sock, Store, Last) -> Data = get_data(Sock), New = handle_data(Last, Data, Store), reader(Sock, Store, New). handle_data(Last, {_, Pid, in, _, Time}, _) -> [{Pid,Time}|Last]; handle_data([], {_, _, out, _, _}, _Store) -> %% ignore - there was probably just a 'drop' []; handle_data(Last, {_, Pid, out, _, Time2} = G, Store) -> case lists:keytake(Pid, 1, Last) of {_, {_, Time1}, New} -> Elapsed = elapsed(Time1, Time2), case ets:member(Store,Pid) of true -> ets:update_counter(Store, Pid, Elapsed); false -> ets:insert(Store,{Pid,Elapsed}) end, New; false -> io:format("Erlang top got garbage ~tp~n", [G]), Last end; handle_data(_W, {drop, D}, _) -> %% Error case we are missing data here! io:format("Erlang top dropped data ~p~n", [D]), []; handle_data(Last, G, _) -> io:format("Erlang top got garbage ~tp~n", [G]), Last. elapsed({Me1, S1, Mi1}, {Me2, S2, Mi2}) -> Me = (Me2 - Me1) * 1000000, S = (S2 - S1 + Me) * 1000000, Mi2 - Mi1 + S. %%%%%% Socket helpers %%%% get_data(Sock) -> [Op | BESiz] = my_ip_read(Sock, 5), Siz = get_be(BESiz), case Op of 0 -> B = list_to_binary(my_ip_read(Sock, Siz)), binary_to_term(B); 1 -> {drop, Siz}; Else -> exit({'bad trace tag', Else}) end. get_be([A,B,C,D]) -> A * 16777216 + B * 65536 + C * 256 + D. my_ip_read(Sock,N) -> case gen_tcp:recv(Sock, N) of {ok, Data} -> case length(Data) of N -> Data; X -> Data ++ my_ip_read(Sock, N - X) end; _Else -> exit(eof) end.