149
158
lists:flatten(lists:map(Fun, Services))
163
%%--------------------------------------------------------------------
164
%% Function: print_version_info()
166
%% Description: Simple utility function to print information
167
%% about versions (system, OS and modules).
168
%%--------------------------------------------------------------------
170
print_version_info() ->
171
{ok, Versions} = inets:versions(),
172
print_version_info(Versions).
174
print_version_info(Versions) when is_list(Versions) ->
175
print_sys_info(Versions),
176
print_os_info(Versions),
177
print_mods_info(Versions).
179
print_sys_info(Versions) ->
180
case key1search(sys_info, Versions) of
181
{value, SysInfo} when is_list(SysInfo) ->
182
{value, Arch} = key1search(arch, SysInfo, "Not found"),
183
{value, Ver} = key1search(ver, SysInfo, "Not found"),
184
io:format("System info: "
190
io:format("System info: Not found~n", []),
194
print_os_info(Versions) ->
195
case key1search(os_info, Versions) of
196
{value, OsInfo} when is_list(OsInfo) ->
198
case key1search(fam, OsInfo, "Not found") of
199
{value, F} when is_atom(F) ->
201
{value, LF} when is_list(LF) ->
204
lists:flatten(io_lib:format("~p", [XF]))
207
case key1search(name, OsInfo) of
208
{value, N} when is_atom(N) ->
209
"[" ++ atom_to_list(N) ++ "]";
210
{value, LN} when is_list(LN) ->
216
case key1search(ver, OsInfo, "Not found") of
217
{value, T} when is_tuple(T) ->
219
{value, LV} when is_list(LV) ->
222
lists:flatten(io_lib:format("~p", [XV]))
224
io:format("OS info: "
227
"~n", [Fam, Name, Ver]),
230
io:format("OS info: Not found~n", []),
236
LibDir = code:lib_dir(App),
237
File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]),
238
case file:consult(File) of
239
{ok, [{application, App, AppFile}]} ->
240
case lists:keysearch(modules, 1, AppFile) of
241
{value, {modules, Mods}} ->
242
{ok, version_info(Mods)};
244
{error, {invalid_format, modules}}
247
{error, {invalid_format, Error}}
250
version_info(Mods) ->
251
SysInfo = sys_info(),
253
ModInfo = [mod_version_info(Mod) || Mod <- Mods],
254
[{sys_info, SysInfo}, {os_info, OsInfo}, {mod_info, ModInfo}].
256
mod_version_info(Mod) ->
257
Info = Mod:module_info(),
258
{value, {attributes, Attr}} = lists:keysearch(attributes, 1, Info),
259
{value, {vsn, [Vsn]}} = lists:keysearch(vsn, 1, Attr),
260
{value, {app_vsn, AppVsn}} = lists:keysearch(app_vsn, 1, Attr),
261
{value, {compile, Comp}} = lists:keysearch(compile, 1, Info),
262
{value, {version, Ver}} = lists:keysearch(version, 1, Comp),
263
{value, {time, Time}} = lists:keysearch(time, 1, Comp),
266
{compiler_version, Ver},
267
{compile_time, Time}]}.
270
SysArch = string:strip(erlang:system_info(system_architecture),right,$\n),
271
SysVer = string:strip(erlang:system_info(system_version),right,$\n),
272
[{arch, SysArch}, {ver, SysVer}].
278
[{fam, OsFam}, {name, OsName}, {ver, V}];
280
[{fam, OsFam}, {ver, V}]
284
print_mods_info(Versions) ->
285
case key1search(mod_info, Versions) of
286
{value, ModsInfo} when is_list(ModsInfo) ->
287
io:format("Module info: ~n", []),
288
lists:foreach(fun print_mod_info/1, ModsInfo);
290
io:format("Module info: Not found~n", []),
295
L = tuple_to_list(T),
303
integer_to_list(A) ++ "." ++ lversion(R).
305
print_mod_info({Module, Info}) ->
306
% Maybe a asn1 generated module
308
case (catch Module:info()) of
309
AI when is_list(AI) ->
310
case (catch key1search(vsn, AI)) of
311
{value, V} when is_atom(V) ->
320
case key1search(vsn, Info) of
321
{value, I} when is_integer(I) ->
327
case key1search(app_vsn, Info) of
328
{value, S1} when is_list(S1) ->
334
case key1search(compiler_version, Info) of
335
{value, S2} when is_list(S2) ->
341
case key1search(compile_time, Info) of
342
{value, {Year, Month, Day, Hour, Min, Sec}} ->
344
io_lib:format("~w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",
345
[Year, Month, Day, Hour, Min, Sec]));
353
" Compiler ver: ~s~n"
354
" Compile time: ~s~n",
355
[Module, Vsn, AppVsn, Asn1Vsn, CompVer, CompDate]),
359
key1search(Key, Vals) ->
360
case lists:keysearch(Key, 1, Vals) of
361
{value, {Key, Val}} ->
367
key1search(Key, Vals, Def) ->
368
case key1search(Key, Vals) of
151
376
%%--------------------------------------------------------------------
152
377
%% Function: service_names() -> [ServiceName]
158
383
service_names() ->
159
384
[ftpc, tftpd, httpc, httpd].
387
%%-----------------------------------------------------------------
388
%% enable_trace(Level, Destination) -> void()
391
%% Level -> max | min | integer()
392
%% Destination -> File | Port | io | {io, Verbosity} | HandlerSpec
395
%% Verbosity -> true | false
396
%% HandlerSpec = {function(), Data}
400
%% This function is used to start tracing at level Level and send
401
%% the result either to the file File, the port Port or to a
403
%% Note that it starts a tracer server.
404
%% When Destination is the atom io (or the tuple {io, Verbosity}),
405
%% all (printable) inets trace events (trace_ts events which has
406
%% Severity withing Limit) will be written to stdout using io:format.
408
%%-----------------------------------------------------------------
409
enable_trace(Level, File) when is_list(File) ->
410
case file:open(File, [write]) of
412
HandleSpec = {fun handle_trace/2, Fd},
413
do_enable_trace(Level, process, HandleSpec);
417
enable_trace(Level, Port) when is_integer(Port) ->
418
do_enable_trace(Level, port, dbg:trace_port(ip, Port));
419
enable_trace(Level, io) ->
420
HandleSpec = {fun handle_trace/2, standard_io},
421
do_enable_trace(Level, process, HandleSpec);
422
enable_trace(Level, {Fun, _Data} = HandleSpec) when is_function(Fun) ->
423
do_enable_trace(Level, process, HandleSpec).
425
do_enable_trace(Level, Type, HandleSpec) ->
426
case dbg:tracer(Type, HandleSpec) of
435
%%-----------------------------------------------------------------
436
%% disable_trace() -> void()
439
%% This function is used to stop tracing.
440
%%-----------------------------------------------------------------
442
%% This is to make handle_trace/2 close the output file (if the
443
%% event gets there before dbg closes)
444
inets:report_event(100, "stop trace", stop_trace, [stop_trace]),
449
%%-----------------------------------------------------------------
450
%% set_trace(Level) -> void()
453
%% Level -> max | min | integer()
456
%% This function is used to change the trace level when tracing has
457
%% already been started.
458
%%-----------------------------------------------------------------
460
set_trace(Level, all).
462
set_trace(Level, Service) ->
463
Pat = make_pattern(?MODULE, Service, Level),
466
make_pattern(Mod, Service, Level)
467
when is_atom(Mod) andalso is_atom(Service) ->
472
Head = ['$1', '_', '_', '_'],
475
{Mod, Service, [{Head, Cond, Body}]};
476
DetailLevel when is_integer(DetailLevel) ->
477
Head = ['$1', '_', '_', '_'],
479
Cond = [{ '=<', '$1', DetailLevel}],
480
{Mod, Service, [{Head, Cond, Body}]};
482
exit({bad_level, Level})
485
change_pattern({Mod, Service, Pattern})
486
when is_atom(Mod) andalso is_atom(Service) ->
487
MFA = {Mod, report_event, 4},
491
error_to_exit(ctp, dbg:ctp(MFA)),
492
error_to_exit(p, dbg:p(all, clear))
494
exit:{Where, Reason} ->
495
{error, {Where, Reason}}
497
List when is_list(List) ->
499
error_to_exit(ctp, dbg:ctp(MFA)),
500
error_to_exit(tp, dbg:tp(MFA, Pattern)),
501
error_to_exit(p, dbg:p(all, [call, timestamp]))
503
exit:{Where, Reason} ->
504
{error, {Where, Reason}}
507
exit({bad_pattern, Pattern})
511
error_to_exit(_Where, {ok, _} = OK) ->
513
error_to_exit(Where, {error, Reason}) ->
514
exit({Where, Reason}).
517
%%-----------------------------------------------------------------
518
%% report_event(Serverity, Label, Service, Content)
521
%% Severity -> 0 =< integer() =< 100
523
%% Service -> httpd | httpc | ftp | tftp
524
%% Content -> [{tag, term()}]
527
%% This function is used to generate trace events, that is,
528
%% put trace on this function.
529
%%-----------------------------------------------------------------
531
report_event(Severity, Label, Service, Content)
532
when (is_integer(Severity) andalso
533
(Severity >= 0) andalso (100 >= Severity)) andalso
534
is_list(Label) andalso
535
is_atom(Service) andalso
540
%% ----------------------------------------------------------------------
541
%% handle_trace(Event, Fd) -> Verbosity
544
%% Event -> The trace event (only megaco 'trace_ts' events are printed)
545
%% Fd -> standard_io | file_descriptor() | trace_port()
548
%% This function is used to "receive" and print the trace events.
549
%% Events are printed if:
550
%% - Verbosity is max
551
%% - Severity is =< Verbosity (e.g. Severity = 30, and Verbosity = 40)
552
%% Events are not printed if:
553
%% - Verbosity is min
554
%% - Severity is > Verbosity
555
%%-----------------------------------------------------------------
557
handle_trace(_, closed_file = Fd) ->
559
handle_trace({trace_ts, _Who, call,
560
{?MODULE, report_event,
561
[_Sev, "stop trace", stop_trace, [stop_trace]]},
564
(catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
566
handle_trace({trace_ts, _Who, call,
567
{?MODULE, report_event,
568
[_Sev, "stop trace", stop_trace, [stop_trace]]},
571
(catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
572
(catch file:close(Fd)),
574
handle_trace({trace_ts, Who, call,
575
{?MODULE, report_event,
576
[Sev, Label, Service, Content]}, Timestamp},
578
(catch print_inets_trace(Fd, Sev, Timestamp, Who,
579
Label, Service, Content)),
581
handle_trace(Event, Fd) ->
582
(catch print_trace(Fd, Event)),
586
print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) ->
587
Ts = format_timestamp(Timestamp),
588
io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s "
591
[Service, Sev, Who, Ts, Label, Content]).
594
print_trace(Fd, {trace, Who, What, Where}) ->
595
io:format(Fd, "[trace]"
599
"~n", [Who, What, Where]);
601
print_trace(Fd, {trace, Who, What, Where, Extra}) ->
602
io:format(Fd, "[trace]"
607
"~n", [Who, What, Where, Extra]);
609
print_trace(Fd, {trace_ts, Who, What, Where, When}) ->
610
Ts = format_timestamp(When),
611
io:format(Fd, "[trace ~s]"
615
"~n", [Ts, Who, What, Where]);
617
print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) ->
618
Ts = format_timestamp(When),
619
io:format(Fd, "[trace ~s]"
624
"~n", [Ts, Who, What, Where, Extra]);
626
print_trace(Fd, {seq_trace, What, Where}) ->
627
io:format(Fd, "[seq trace]"
630
"~n", [What, Where]);
632
print_trace(Fd, {seq_trace, What, Where, When}) ->
633
Ts = format_timestamp(When),
634
io:format(Fd, "[seq trace ~s]"
637
"~n", [Ts, What, Where]);
639
print_trace(Fd, {drop, Num}) ->
640
io:format(Fd, "[drop trace] ~p~n", [Num]);
642
print_trace(Fd, Trace) ->
643
io:format(Fd, "[trace] "
648
format_timestamp({_N1, _N2, N3} = Now) ->
649
{Date, Time} = calendar:now_to_datetime(Now),
651
{Hour,Min,Sec} = Time,
653
io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
654
[YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
655
lists:flatten(FormatDate).
161
658
%%--------------------------------------------------------------------
162
659
%%% Internal functions
163
660
%%--------------------------------------------------------------------