1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2011-2015. 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%%
21%%----------------------------------------------------------------------
22%% Purpose: Module for debug trace functions of the inets application
23%%----------------------------------------------------------------------
24
25-module(inets_trace).
26
27%% API
28-export([enable/2, enable/3,
29	 disable/0,
30	 set_level/1, set_level/2,
31	 report_event/4]).
32
33
34%%====================================================================
35%% API
36%%====================================================================
37
38%%-----------------------------------------------------------------
39%% enable(Level, Destination) -> void()
40%% enable(Level, Destination, Service) -> void()
41%%
42%% Parameters:
43%% Level -> max | min | integer()
44%% Destination -> File | Port | io | HandlerSpec
45%% Service -> httpc | httpd | all
46%% File -> string()
47%% Port -> integer()
48%% Verbosity -> true | false
49%% HandlerSpec = {function(), Data}
50%% Data = term()
51%%
52%% Description:
53%% This function is used to start tracing at level Level and send
54%% the result either to the file File, the port Port or to a
55%% trace handler.
56%% Note that it starts a tracer server.
57%% When Destination is the atom io (or the tuple {io, Verbosity}),
58%% all (printable) inets trace events (trace_ts events which has
59%% Severity withing Limit) will be written to stdout using io:format.
60%%
61%%-----------------------------------------------------------------
62enable(Level, Dest) ->
63    enable(Level, Dest, all).
64
65enable(Level, Dest, Service) ->
66    case valid_trace_service(Service) of
67	true ->
68	    enable2(Level, Dest, Service);
69	false ->
70	    {error, {invalid_service, Service}}
71    end.
72
73enable2(Level, File, Service)
74  when is_list(File) ->
75    case file:open(File, [write]) of
76	{ok, Fd} ->
77	    HandleSpec = {fun handle_trace/2, {Service, Fd}},
78	    do_enable(Level, process, HandleSpec);
79	Err ->
80	    Err
81    end;
82enable2(Level, Port, _) when is_integer(Port) ->
83    do_enable(Level, port, dbg:trace_port(ip, Port));
84enable2(Level, io, Service) ->
85    HandleSpec = {fun handle_trace/2, {Service, standard_io}},
86    do_enable(Level, process, HandleSpec);
87enable2(Level, {Fun, _Data} = HandleSpec, _) when is_function(Fun) ->
88    do_enable(Level, process, HandleSpec).
89
90do_enable(Level, Type, HandleSpec) ->
91    case dbg:tracer(Type, HandleSpec) of
92	{ok, _} ->
93	    set_level(Level),
94	    ok;
95	Error ->
96	    Error
97    end.
98
99valid_trace_service(all) ->
100    true;
101valid_trace_service(Service) ->
102    lists:member(Service, [httpc, httpd]).
103
104
105%%-----------------------------------------------------------------
106%% disable() -> void()
107%%
108%% Description:
109%% This function is used to stop tracing.
110%%-----------------------------------------------------------------
111
112disable() ->
113    %% This is to make handle_trace/2 close the output file (if the
114    %% event gets there before dbg closes)
115    inets_trace:report_event(100, "stop trace", stop_trace, [stop_trace]),
116    dbg:stop().
117
118
119
120%%-----------------------------------------------------------------
121%% set_level(Level) -> void()
122%%
123%% Parameters:
124%% Level -> max | min | integer()
125%%
126%% Description:
127%% This function is used to change the trace level when tracing has
128%% already been started.
129%%-----------------------------------------------------------------
130set_level(Level) ->
131    set_level(Level, all).
132
133set_level(Level, Service) ->
134    Pat = make_pattern(?MODULE, Service, Level),
135    change_pattern(Pat).
136
137make_pattern(Mod, Service, Level)
138  when is_atom(Mod) andalso is_atom(Service) ->
139    case Level of
140        min ->
141            {Mod, Service, []};
142        max ->
143            Head = ['$1', '_', '_', '_'],
144            Body = [],
145            Cond = [],
146            {Mod, Service, [{Head, Cond, Body}]};
147        DetailLevel when is_integer(DetailLevel) ->
148            Head = ['$1', '_', '_', '_'],
149            Body = [],
150            Cond = [{ '=<', '$1', DetailLevel}],
151            {Mod, Service, [{Head, Cond, Body}]};
152        _ ->
153            exit({bad_level, Level})
154    end.
155
156change_pattern({Mod, Service, Pattern})
157  when is_atom(Mod) andalso is_atom(Service) ->
158    MFA = {Mod, report_event, 4},
159    case Pattern of
160        [] ->
161	    try
162		error_to_exit(ctp, dbg:ctp(MFA)),
163		error_to_exit(p,   dbg:p(all, clear))
164	    catch
165		exit:{Where, Reason} ->
166		    {error, {Where, Reason}}
167	    end;
168        List when is_list(List) ->
169	    try
170		error_to_exit(ctp, dbg:ctp(MFA)),
171		error_to_exit(tp,  dbg:tp(MFA, Pattern)),
172		error_to_exit(p,   dbg:p(all, [call, timestamp]))
173	    catch
174		exit:{Where, Reason} ->
175		    {error, {Where, Reason}}
176	    end
177    end.
178
179error_to_exit(_Where, {ok, _} = OK) ->
180    OK;
181error_to_exit(Where, {error, Reason}) ->
182    exit({Where, Reason}).
183
184
185%%-----------------------------------------------------------------
186%% report_event(Severity, Label, Service, Content)
187%%
188%% Parameters:
189%% Severity -> 0 =< integer() =< 100
190%% Label -> string()
191%% Service -> httpd | httpc
192%% Content -> [{tag, term()}]
193%%
194%% Description:
195%% This function is used to generate trace events, that is,
196%% put trace on this function.
197%%-----------------------------------------------------------------
198
199report_event(Severity, Label, Service, Content)
200  when (is_integer(Severity) andalso
201	(Severity >= 0) andalso (100 >= Severity)) andalso
202       is_list(Label) andalso
203       is_atom(Service) andalso
204       is_list(Content) ->
205    hopefully_traced.
206
207
208%% ----------------------------------------------------------------------
209%% handle_trace(Event, Fd) -> Verbosity
210%%
211%% Parameters:
212%% Event -> The trace event
213%% Fd -> standard_io | file_descriptor() | trace_port()
214%%
215%% Description:
216%% This function is used to "receive" and pretty print the trace events.
217%% Events are printed if:
218%%   - Verbosity is max
219%%   - Severity is =< Verbosity (e.g. Severity = 30, and Verbosity = 40)
220%% Events are not printed if:
221%%   - Verbosity is min
222%%   - Severity is > Verbosity
223%%-----------------------------------------------------------------
224
225handle_trace(_, closed_file = Fd) ->
226    Fd;
227handle_trace({trace_ts, _Who, call,
228              {?MODULE, report_event,
229               [_Sev, "stop trace", stop_trace, [stop_trace]]},
230              Timestamp},
231             {_, standard_io} = Fd) ->
232    (catch io:format(standard_io, "stop trace at ~s~n",
233                     [inets_lib:format_timestamp(Timestamp)])),
234    Fd;
235handle_trace({trace_ts, _Who, call,
236              {?MODULE, report_event,
237               [_Sev, "stop trace", stop_trace, [stop_trace]]},
238              Timestamp},
239             standard_io = Fd) ->
240    (catch io:format(Fd, "stop trace at ~s~n",
241                     [inets_lib:format_timestamp(Timestamp)])),
242    Fd;
243handle_trace({trace_ts, _Who, call,
244              {?MODULE, report_event,
245               [_Sev, "stop trace", stop_trace, [stop_trace]]},
246              Timestamp},
247             {_Service, Fd}) ->
248    (catch io:format(Fd, "stop trace at ~s~n",
249                     [inets_lib:format_timestamp(Timestamp)])),
250    (catch file:close(Fd)),
251    closed_file;
252handle_trace({trace_ts, _Who, call,
253              {?MODULE, report_event,
254               [_Sev, "stop trace", stop_trace, [stop_trace]]},
255              Timestamp},
256             Fd) ->
257    (catch io:format(Fd, "stop trace at ~s~n",
258                     [inets_lib:format_timestamp(Timestamp)])),
259    (catch file:close(Fd)),
260    closed_file;
261handle_trace({trace_ts, Who, call,
262              {?MODULE, report_event,
263               [Sev, Label, Service, Content]}, Timestamp},
264             Fd) ->
265    (catch print_inets_trace(Fd, Sev, Timestamp, Who,
266			     Label, Service, Content)),
267    Fd;
268handle_trace(Event, Fd) ->
269    (catch print_trace(Fd, Event)),
270    Fd.
271
272
273print_inets_trace({Service, Fd},
274		  Sev, Timestamp, Who, Label, Service, Content) ->
275    do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content);
276print_inets_trace({ServiceA, Fd},
277		  Sev, Timestamp, Who, Label, ServiceB, Content)
278  when (ServiceA =:= all) ->
279    do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, ServiceB, Content);
280print_inets_trace({ServiceA, _Fd},
281		  _Sev, _Timestamp, _Who, _Label, ServiceB, _Content)
282  when ServiceA =/= ServiceB ->
283    ok;
284print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) ->
285    do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content).
286
287do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) ->
288    Ts = inets_lib:format_timestamp(Timestamp),
289    io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s "
290              "~n   Content: ~p"
291              "~n",
292              [Service, Sev, Who, Ts, Label, Content]).
293
294print_trace({_, Fd}, Event) ->
295    do_print_trace(Fd, Event);
296print_trace(Fd, Event) ->
297    do_print_trace(Fd, Event).
298
299do_print_trace(Fd, {trace, Who, What, Where}) ->
300    io:format(Fd, "[trace]"
301              "~n   Who:   ~p"
302              "~n   What:  ~p"
303              "~n   Where: ~p"
304              "~n", [Who, What, Where]);
305
306do_print_trace(Fd, {trace, Who, What, Where, Extra}) ->
307    io:format(Fd, "[trace]"
308              "~n   Who:   ~p"
309              "~n   What:  ~p"
310              "~n   Where: ~p"
311              "~n   Extra: ~p"
312              "~n", [Who, What, Where, Extra]);
313
314do_print_trace(Fd, {trace_ts, Who, What, Where, When}) ->
315    Ts = inets_lib:format_timestamp(When),
316    io:format(Fd, "[trace ~s]"
317              "~n   Who:   ~p"
318              "~n   What:  ~p"
319              "~n   Where: ~p"
320              "~n", [Ts, Who, What, Where]);
321
322do_print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) ->
323    Ts = inets_lib:format_timestamp(When),
324    io:format(Fd, "[trace ~s]"
325              "~n   Who:   ~p"
326              "~n   What:  ~p"
327              "~n   Where: ~p"
328              "~n   Extra: ~p"
329              "~n", [Ts, Who, What, Where, Extra]);
330
331do_print_trace(Fd, {seq_trace, What, Where}) ->
332    io:format(Fd, "[seq trace]"
333              "~n   What:       ~p"
334              "~n   Where:      ~p"
335              "~n", [What, Where]);
336
337do_print_trace(Fd, {seq_trace, What, Where, When}) ->
338    Ts = inets_lib:format_timestamp(When),
339    io:format(Fd, "[seq trace ~s]"
340              "~n   What:       ~p"
341              "~n   Where:      ~p"
342              "~n", [Ts, What, Where]);
343
344do_print_trace(Fd, {drop, Num}) ->
345    io:format(Fd, "[drop trace] ~p~n", [Num]);
346
347do_print_trace(Fd, Trace) ->
348    io:format(Fd, "[trace] "
349              "~n   ~p"
350              "~n", [Trace]).
351
352
353