%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
%%----------------------------------------------------------------------
%% Purpose: Misc utility functions for the mstone modules
%%----------------------------------------------------------------------
-module(megaco_codec_mstone_lib).
%% API
%% Avoid warning for local function error/1 clashing with autoimported BIF.
-compile({no_auto_import,[error/1]}).
-export([start_flex_scanner/0, stop_flex_scanner/1,
expanded_messages/2, expanded_messages/3,
display_os_info/0,
display_system_info/0,
display_alloc_info/0,
display_app_info/0,
detect_version/3]).
%% Internal exports
-export([flex_scanner_handler/1]).
-include_lib("kernel/include/file.hrl").
%%----------------------------------------------------------------------
%%
%% D e t e c t V e r s i o n
%%
%%----------------------------------------------------------------------
detect_version(Codec, Conf, Bin) ->
case (catch Codec:version_of(Conf, Bin)) of
{ok, V} ->
case (catch Codec:decode_message(Conf, V, Bin)) of
{ok, M} ->
case (catch Codec:encode_message(Conf, V, M)) of
{ok, NewBin} ->
{V, NewBin};
Error1 ->
error({encode_failed, Error1, Codec, Conf, M})
end;
Error2 ->
error({decode_failed, Error2, Codec, Conf, Bin})
end;
Error3 ->
error({version_of_failed, Error3, Codec, Conf, Bin})
end.
%%----------------------------------------------------------------------
%%
%% D i s p l a y O s I n f o
%%
%%----------------------------------------------------------------------
display_os_info() ->
V = case os:version() of
{Major, Minor, Release} ->
lists:flatten(
io_lib:format("~w.~w.~w", [Major, Minor, Release]));
Str ->
Str
end,
case os:type() of
{OsFam, OsName} ->
io:format("OS: ~p-~p: ~s~n", [OsFam, OsName, V]);
OsFam ->
io:format("OS: ~p: ~s~n", [OsFam, V])
end.
%%----------------------------------------------------------------------
%%
%% D i s p l a y S y s t e m I n f o
%%
%%----------------------------------------------------------------------
display_system_info() ->
SysArch = system_architecture(),
OtpRel = otp_release(),
SysVer = system_version(),
SysHT = heap_type(),
SysSMP = smp_support(),
SysNumSched = schedulers(),
SysProcLimit = process_limit(),
SysThreads = threads(),
SysTPSz = thread_pool_size(),
SchedBindings = scheduler_bindings(),
SchedBindType = scheduler_bind_type(),
CpuTopology = cpu_topology(),
io:format("System architecture: ~s~n", [SysArch]),
io:format("OTP release: ~s~n", [OtpRel]),
io:format("System version: ~s~n", [SysVer]),
io:format("Heap type: ~s~n", [SysHT]),
io:format("Thread support: ~s~n", [SysThreads]),
io:format("Thread pool size: ~s~n", [SysTPSz]),
io:format("Process limit: ~s~n", [SysProcLimit]),
io:format("SMP support: ~s~n", [SysSMP]),
io:format("Num schedulers: ~s~n", [SysNumSched]),
io:format("Scheduler bindings: ~s~n", [SchedBindings]),
io:format("Scheduler bind type: ~s~n", [SchedBindType]),
io:format("Cpu topology: ~s~n", [CpuTopology]),
ok.
system_architecture() ->
string:strip(system_info(system_architecture, string),right,$\n).
otp_release() ->
system_info(otp_release, string).
system_version() ->
string:strip(system_info(system_version, string),right,$\n).
heap_type() ->
system_info(heap_type, any).
smp_support() ->
system_info(smp_support, any).
schedulers() ->
system_info(schedulers, any).
process_limit() ->
system_info(process_limit, any).
threads() ->
system_info(threads, any).
thread_pool_size() ->
system_info(thread_pool_size, any).
scheduler_bindings() ->
system_info(scheduler_bindings, any).
scheduler_bind_type() ->
system_info(scheduler_bind_type, any).
cpu_topology() ->
system_info(cpu_topology, any).
system_info(Tag, Type) ->
case (catch erlang:system_info(Tag)) of
{'EXIT', _} ->
"-";
Info when is_list(Info) andalso (Type =:= string) ->
Info;
Info ->
lists:flatten(io_lib:format("~w", [Info]))
end.
%%----------------------------------------------------------------------
%%
%% D i s p l a y A l l o c a t o r I n f o
%%
%%----------------------------------------------------------------------
display_alloc_info() ->
io:format("Allocator memory information:~n", []),
AllocInfo = alloc_info(),
display_alloc_info(AllocInfo).
display_alloc_info([]) ->
ok;
display_alloc_info([{Alloc, Mem}|AllocInfo]) ->
io:format(" ~15w: ~10w~n", [Alloc, Mem]),
display_alloc_info(AllocInfo).
alloc_info() ->
case erlang:system_info(allocator) of
{_Allocator, _Version, Features, _Settings} ->
alloc_info(Features);
_ ->
[]
end.
alloc_info(Allocators) ->
Allocs = [temp_alloc, sl_alloc, std_alloc, ll_alloc, eheap_alloc,
ets_alloc, binary_alloc, driver_alloc],
alloc_info(Allocators, Allocs, []).
alloc_info([], _, Acc) ->
lists:reverse(Acc);
alloc_info([Allocator | Allocators], Allocs, Acc) ->
case lists:member(Allocator, Allocs) of
true ->
Instances0 = erlang:system_info({allocator, Allocator}),
Instances =
if
is_list(Instances0) ->
[Instance || Instance <- Instances0,
element(1, Instance) =:= instance];
true ->
[]
end,
AllocatorMem = alloc_mem_info(Instances),
alloc_info(Allocators, Allocs, [{Allocator, AllocatorMem} | Acc]);
false ->
alloc_info(Allocators, Allocs, Acc)
end.
alloc_mem_info(Instances) ->
alloc_mem_info(Instances, []).
alloc_mem_info([], Acc) ->
lists:sum([Mem || {instance, _, Mem} <- Acc]);
alloc_mem_info([{instance, N, Info}|Instances], Acc) ->
InstanceMemInfo = alloc_instance_mem_info(Info),
alloc_mem_info(Instances, [{instance, N, InstanceMemInfo} | Acc]).
alloc_instance_mem_info(InstanceInfo) ->
MBCS = alloc_instance_mem_info(mbcs, InstanceInfo),
SBCS = alloc_instance_mem_info(sbcs, InstanceInfo),
MBCS + SBCS.
alloc_instance_mem_info(Key, InstanceInfo) ->
case lists:keysearch(Key, 1, InstanceInfo) of
{value, {Key, Info}} ->
case lists:keysearch(blocks_size, 1, Info) of
{value, {blocks_size, Mem, _, _}} ->
Mem;
_ ->
0
end;
_ ->
0
end.
%%----------------------------------------------------------------------
%%
%% D i s p l a y A p p I n f o
%%
%%----------------------------------------------------------------------
display_app_info() ->
display_megaco_info(),
display_asn1_info().
display_megaco_info() ->
MI = megaco:module_info(),
{value, {attributes, Attr}} = lists:keysearch(attributes, 1, MI),
{value, {app_vsn, Ver}} = lists:keysearch(app_vsn, 1, Attr),
io:format("Megaco version: ~s~n", [Ver]).
display_asn1_info() ->
AI = megaco_ber_bin_drv_media_gateway_control_v1:info(),
Vsn =
case lists:keysearch(vsn, 1, AI) of
{value, {vsn, V}} when is_atom(V) ->
atom_to_list(V);
{value, {vsn, V}} when is_list(V) ->
V;
_ ->
"unknown"
end,
io:format("ASN.1 version: ~s~n", [Vsn]).
%%----------------------------------------------------------------------
%%
%% E x p a n d M e s s a g e s
%%
%%----------------------------------------------------------------------
expanded_messages(Codecs, DrvInclude) ->
MessagePackage = time_test,
expanded_messages(MessagePackage, Codecs, DrvInclude).
expanded_messages(MessagePackage, Codecs, DrvInclude) ->
ECodecs = expand_codecs(Codecs, DrvInclude),
Messages = megaco_codec_transform:messages(MessagePackage),
expanded_messages2(ECodecs, Messages, []).
expanded_messages2([], _Messages, EMessages) ->
lists:reverse(EMessages);
expanded_messages2([{Codec, Mod, Conf}|ECodecs], Messages, EMessages) ->
case lists:keysearch(Codec, 1, Messages) of
{value, {Codec, Msgs}} ->
expanded_messages2(ECodecs, Messages,
[{Codec, Mod, Conf, Msgs}|EMessages]);
false ->
exit({error, {no_such_codec_data, Codec}})
end.
%%----------------------------------------------------------------------
%%
%% E x p a n d C o d e c s
%%
%%----------------------------------------------------------------------
expand_codecs(Codecs, DrvInclude) ->
expand_codecs(Codecs, DrvInclude, []).
expand_codecs([], _, ECodecs) ->
lists:reverse(lists:flatten(ECodecs));
expand_codecs([Codec|Codecs], DrvInclude, ECodecs) when is_atom(Codec) ->
ECodec = expand_codec(Codec, DrvInclude),
expand_codecs(Codecs, DrvInclude, [ECodec|ECodecs]).
expand_codec(Codec, flex) ->
case Codec of
pretty ->
[{Codec, megaco_pretty_text_encoder, [flex_scanner]},
{Codec, megaco_pretty_text_encoder, [flex_scanner]},
{Codec, megaco_pretty_text_encoder, [flex_scanner]},
{Codec, megaco_pretty_text_encoder, [flex_scanner]},
{Codec, megaco_pretty_text_encoder, [flex_scanner]},
{Codec, megaco_pretty_text_encoder, [flex_scanner]},
{Codec, megaco_pretty_text_encoder, [flex_scanner]},
{Codec, megaco_pretty_text_encoder, [flex_scanner]}];
compact ->
[{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, [flex_scanner]}];
ber ->
[];
per ->
[];
erlang ->
[];
Else ->
error({invalid_codec, Else})
end;
expand_codec(Codec, only_drv) ->
case Codec of
pretty ->
[{Codec, megaco_pretty_text_encoder, [flex_scanner]},
{Codec, megaco_pretty_text_encoder, [flex_scanner]}];
compact ->
[{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, [flex_scanner]}];
ber ->
[{Codec, megaco_ber_bin_encoder, [driver,native]},
{Codec, megaco_ber_bin_encoder, [driver]},
{Codec, megaco_ber_bin_encoder, [driver,native]},
{Codec, megaco_ber_bin_encoder, [driver]}];
per ->
[{Codec, megaco_per_bin_encoder, [driver,native]},
{Codec, megaco_per_bin_encoder, [native]},
{Codec, megaco_per_bin_encoder, [driver,native]},
{Codec, megaco_per_bin_encoder, [native]}];
erlang ->
Encoder = megaco_erl_dist_encoder,
[
{Codec, Encoder, [megaco_compressed,compressed]},
{Codec, Encoder, [compressed]},
{Codec, Encoder, [megaco_compressed,compressed]},
{Codec, Encoder, [compressed]}
];
Else ->
error({invalid_codec, Else})
end;
expand_codec(Codec, no_drv) ->
case Codec of
pretty ->
[{Codec, megaco_pretty_text_encoder, []},
{Codec, megaco_pretty_text_encoder, []}];
compact ->
[{Codec, megaco_compact_text_encoder, []},
{Codec, megaco_compact_text_encoder, []}];
ber ->
[{Codec, megaco_ber_bin_encoder, [native]},
{Codec, megaco_ber_bin_encoder, []},
{Codec, megaco_ber_bin_encoder, [native]},
{Codec, megaco_ber_bin_encoder, []}];
per ->
[{Codec, megaco_per_bin_encoder, [native]},
{Codec, megaco_per_bin_encoder, []},
{Codec, megaco_per_bin_encoder, [native]},
{Codec, megaco_per_bin_encoder, []}];
erlang ->
Encoder = megaco_erl_dist_encoder,
[
{Codec, Encoder, [megaco_compressed]},
{Codec, Encoder, []},
{Codec, Encoder, [megaco_compressed]},
{Codec, Encoder, []}
];
Else ->
error({invalid_codec, Else})
end;
expand_codec(Codec, _) ->
case Codec of
pretty ->
[{Codec, megaco_pretty_text_encoder, [flex_scanner]},
{Codec, megaco_pretty_text_encoder, []}];
compact ->
[{Codec, megaco_compact_text_encoder, [flex_scanner]},
{Codec, megaco_compact_text_encoder, []}];
ber ->
[{Codec, megaco_ber_bin_encoder, [driver,native]},
{Codec, megaco_ber_bin_encoder, [native]},
{Codec, megaco_ber_bin_encoder, [driver]},
{Codec, megaco_ber_bin_encoder, []}];
per ->
[{Codec, megaco_per_bin_encoder, [driver,native]},
{Codec, megaco_per_bin_encoder, [native]},
{Codec, megaco_per_bin_encoder, [driver]},
{Codec, megaco_per_bin_encoder, []}];
erlang ->
Encoder = megaco_erl_dist_encoder,
[
{Codec, Encoder, [megaco_compressed,compressed]},
{Codec, Encoder, [compressed]},
{Codec, Encoder, [megaco_compressed]},
{Codec, Encoder, []}
];
Else ->
error({invalid_codec, Else})
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%----------------------------------------------------------------------
%%
%% S t a r t F l e x S c a n n e r H a n d l e r
%%
%%----------------------------------------------------------------------
start_flex_scanner() ->
Pid = proc_lib:spawn(?MODULE, flex_scanner_handler, [self()]),
receive
{flex_scanner_started, Pid, Conf} ->
{Pid, [Conf]};
{flex_scanner_error, {failed_loading_flex_scanner_driver, Reason}} ->
error({failed_loading_flex_scanner_driver, Reason});
{flex_scanner_error, Reason} ->
error({failed_loading_flex_scanner_driver, Reason})
after 10000 ->
exit(Pid, kill),
error({failed_starting_flex_scanner, timeout})
end.
%%----------------------------------------------------------------------
%%
%% S t o p F l e x S c a n n e r H a n d l e r
%%
%%----------------------------------------------------------------------
stop_flex_scanner(Pid) ->
Pid ! stop_flex_scanner.
flex_scanner_handler(Pid) ->
case (catch megaco_flex_scanner:start()) of
{ok, PortOrPorts} ->
Pid ! {flex_scanner_started, self(), {flex, PortOrPorts}},
flex_scanner_handler_loop(Pid, PortOrPorts);
{error, {load_driver, {open_error, Reason}}} ->
Error = {failed_loading_flex_scanner_driver, Reason},
Pid ! {flex_scanner_error, Error},
exit(Error);
Else ->
Error = {unknown_result_from_start_flex_scanner, Else},
Pid ! {flex_scanner_error, Error},
exit(Error)
end.
flex_scanner_handler_loop(Pid, PortOrPorts) ->
receive
{ping, Pinger} ->
Pinger ! {pong, self()},
flex_scanner_handler_loop(Pid, PortOrPorts);
{'EXIT', Port, Reason} when (Port =:= PortOrPorts) ->
Pid ! {flex_scanner_exit, Reason},
exit({flex_scanner_exit, Reason});
{'EXIT', Port, Reason} when is_port(Port) ->
case megaco_flex_scanner:is_scanner_port(Port, PortOrPorts) of
true ->
Pid ! {flex_scanner_exit, Reason},
exit({flex_scanner_exit, Reason});
false ->
%% Just ignore this crap
flex_scanner_handler_loop(Pid, PortOrPorts)
end;
stop_flex_scanner ->
megaco_flex_scanner:stop(PortOrPorts),
exit(normal);
_Other ->
flex_scanner_handler_loop(Pid, PortOrPorts)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
error(Reason) ->
throw({error, Reason}).