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