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