1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2002-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(etop_tr). 21-author('siri@erix.ericsson.se'). 22 23%%-compile(export_all). 24-export([setup_tracer/1,stop_tracer/1,reader/1]). 25-import(etop,[getopt/2]). 26 27-include("etop_defs.hrl"). 28 29setup_tracer(Config) -> 30 TraceNode = getopt(node,Config), 31 RHost = rpc:call(TraceNode, net_adm, localhost, []), 32 Store = ets:new(?MODULE, [set, public]), 33 34 %% We can only trace one process anyway kill the old one. 35 case erlang:whereis(dbg) of 36 undefined -> 37 case rpc:call(TraceNode, erlang, whereis, [dbg]) of 38 undefined -> fine; 39 Pid -> 40 exit(Pid, kill) 41 end; 42 Pid -> 43 exit(Pid,kill) 44 end, 45 46 dbg:tracer(TraceNode,port,dbg:trace_port(ip,{getopt(port,Config),5000})), 47 dbg:p(all,[running,timestamp]), 48 T = dbg:get_tracer(TraceNode), 49 Config#opts{tracer=T,host=RHost,store=Store}. 50 51stop_tracer(_Config) -> 52 dbg:p(all,clear), 53 dbg:stop(), 54 ok. 55 56 57 58reader(Config) -> 59 Host = getopt(host, Config), 60 Port = getopt(port, Config), 61 62 {ok, Sock} = gen_tcp:connect(Host, Port, [{active, false}]), 63 spawn_link(fun() -> reader_init(Sock,getopt(store,Config),[]) end). 64 65 66%%%%%%%%%%%%%% Socket reader %%%%%%%%%%%%%%%%%%%%%%%%%%% 67 68reader_init(Sock, Store, Last) -> 69 process_flag(priority, high), 70 reader(Sock, Store, Last). 71 72reader(Sock, Store, Last) -> 73 Data = get_data(Sock), 74 New = handle_data(Last, Data, Store), 75 reader(Sock, Store, New). 76 77handle_data(Last, {_, Pid, in, _, Time}, _) -> 78 [{Pid,Time}|Last]; 79handle_data([], {_, _, out, _, _}, _Store) -> 80 %% ignore - there was probably just a 'drop' 81 []; 82handle_data(Last, {_, Pid, out, _, Time2} = G, Store) -> 83 case lists:keytake(Pid, 1, Last) of 84 {_, {_, Time1}, New} -> 85 Elapsed = elapsed(Time1, Time2), 86 case ets:member(Store,Pid) of 87 true -> ets:update_counter(Store, Pid, Elapsed); 88 false -> ets:insert(Store,{Pid,Elapsed}) 89 end, 90 New; 91 false -> 92 io:format("Erlang top got garbage ~tp~n", [G]), 93 Last 94 end; 95handle_data(_W, {drop, D}, _) -> %% Error case we are missing data here! 96 io:format("Erlang top dropped data ~p~n", [D]), 97 []; 98handle_data(Last, G, _) -> 99 io:format("Erlang top got garbage ~tp~n", [G]), 100 Last. 101 102elapsed({Me1, S1, Mi1}, {Me2, S2, Mi2}) -> 103 Me = (Me2 - Me1) * 1000000, 104 S = (S2 - S1 + Me) * 1000000, 105 Mi2 - Mi1 + S. 106 107 108%%%%%% Socket helpers %%%% 109get_data(Sock) -> 110 [Op | BESiz] = my_ip_read(Sock, 5), 111 Siz = get_be(BESiz), 112 case Op of 113 0 -> 114 B = list_to_binary(my_ip_read(Sock, Siz)), 115 binary_to_term(B); 116 1 -> 117 {drop, Siz}; 118 Else -> 119 exit({'bad trace tag', Else}) 120 end. 121 122get_be([A,B,C,D]) -> 123 A * 16777216 + B * 65536 + C * 256 + D. 124 125my_ip_read(Sock,N) -> 126 case gen_tcp:recv(Sock, N) of 127 {ok, Data} -> 128 case length(Data) of 129 N -> 130 Data; 131 X -> 132 Data ++ my_ip_read(Sock, N - X) 133 end; 134 _Else -> 135 exit(eof) 136 end. 137 138